From 267b576360c780925e6a8bf6a1c0127dddf05bba Mon Sep 17 00:00:00 2001 From: Jeremy Whiting Date: Mon, 15 Apr 2024 09:35:29 +0100 Subject: [PATCH] Add examples to API that support a query. Add documentation for API Filter Query processing. --- .../en/docs/Concepts/core-concepts/index.md | 3 ++ .../docs/Reference/APIFilterQueryReference.md | 45 +++++++++++++++++++ docs/site/content/en/openapi/openapi.yaml | 2 + .../api/internal/services/SqlService.java | 17 +++++-- .../api/internal/services/UserService.java | 5 ++- .../horreum/api/services/RunService.java | 7 ++- 6 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 docs/site/content/en/docs/Reference/APIFilterQueryReference.md diff --git a/docs/site/content/en/docs/Concepts/core-concepts/index.md b/docs/site/content/en/docs/Concepts/core-concepts/index.md index a7ce6e413..6421e32ab 100644 --- a/docs/site/content/en/docs/Concepts/core-concepts/index.md +++ b/docs/site/content/en/docs/Concepts/core-concepts/index.md @@ -137,3 +137,6 @@ Actions only for a particular [`Test`](#test) ## Action allow list `Horreum` will only allow generic HTTP requests to domains that have been pre-configured in Horreum by an Administrator. + +## API Filter Query +`Horreum` [API](/docs/Reference/APIFilterQueryReference.md) provides query parameters that are `JSONPath` paths that filter operation results. diff --git a/docs/site/content/en/docs/Reference/APIFilterQueryReference.md b/docs/site/content/en/docs/Reference/APIFilterQueryReference.md new file mode 100644 index 000000000..7740a0c44 --- /dev/null +++ b/docs/site/content/en/docs/Reference/APIFilterQueryReference.md @@ -0,0 +1,45 @@ +--- +title: API Filter Queries +description: Reference guide for API Filter Query +date: 2024-03-27 +weight: 3 +--- + +Some APIs provide the ability to filter results when HTTP API response content type is `application-json`. The `API Filter Query` operates on the server side to reduce payload size. + +API operations that support Filter Query provide an HTTP request parameter named *query*. The value of the *query* parameter is a `JSONPath` expression. Any syntax errors in the `JSONPath` will cause the operation to fail with details of the syntax issue. + +To use the Filter Query with an operation that returns a [Run](/docs/concepts/core-concepts/#run) data can be done as follows. + +```bash +curl -s 'http://localhost:8080/api/sql/1/queryrun?query=$.*&array=true' -H 'content-type: application/json' -H 'Authorization: Bearer '$TOKEN +{ + "valid": true, + "jsonpath": "$.*", + "errorCode": 0, + "sqlState": null, + "reason": null, + "sql": null, + "value": "[\"urn:acme:benchmark:0.1\", [{\"test\": \"Foo\", \"duration\": 10, \"requests\": 123}, {\"test\": \"Bar\", \"duration\": 20, \"requests\": 456}], \"defec8eddeadbeafcafebabeb16b00b5\", \"This gets lost by the transformer\"]}" +} +``` + +The response contains an enclosing JSON object that is common for Filter Query operations. The *value* property contains an array. The elements of the array contain the values of properties in the uploaded [Run](/docs/concepts/core-concepts/#run) JSON. Note the additional HTTP request query parameter named *array*. Setting *array* is necessary to return dynamically sized results. + +To filter the above API operation to retrieve only the *results* property of the uploaded [Run](/docs/concepts/core-concepts/#run) object the *query* parameter is defined as `$.results` + +```bash +curl -s 'http://localhost:8080/api/sql/1/queryrun?query=$.results&array=true' -H 'content-type: application/json' -H 'Authorization: Bearer '$TOKEN +{ + "valid": true, + "jsonpath": "$.results", + "errorCode": 0, + "sqlState": null, + "reason": null, + "sql": null, + "value": "[[{\"test\": \"Foo\", \"duration\": 10, \"requests\": 123}, {\"test\": \"Bar\", \"duration\": 20, \"requests\": 456}]]" +} + +``` + +The response JSON object this time contains the *results* property value JSON. diff --git a/docs/site/content/en/openapi/openapi.yaml b/docs/site/content/en/openapi/openapi.yaml index fb7de0539..756fd2dc4 100644 --- a/docs/site/content/en/openapi/openapi.yaml +++ b/docs/site/content/en/openapi/openapi.yaml @@ -477,9 +477,11 @@ paths: parameters: - name: query in: query + description: JSONPath to be autocompleted required: true schema: type: string + example: $. responses: "200": description: OK diff --git a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/internal/services/SqlService.java b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/internal/services/SqlService.java index bd1b72888..ff8a10216 100644 --- a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/internal/services/SqlService.java +++ b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/internal/services/SqlService.java @@ -11,6 +11,7 @@ import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; import org.eclipse.microprofile.openapi.annotations.tags.Tag; @@ -21,7 +22,11 @@ public interface SqlService { @GET @Path("testjsonpath") - JsonpathValidation testJsonPath(@Parameter(required = true) @QueryParam("query") String jsonpath); + @Operation(description = "Test a JSONPath for syntax errors using database") + JsonpathValidation testJsonPath(@Parameter(required = true, + name="testjsonpath", + description = "JSONPath to be tested", + example = "$.qdup") @QueryParam("query") String jsonpath); @Path("roles") @GET @@ -31,14 +36,20 @@ public interface SqlService { @GET @Path("{id}/queryrun") QueryResult queryRunData(@PathParam("id") int id, - @Parameter(required = true) @QueryParam("query") String jsonpath, + @Parameter(required = true, + name = "query", + description = "JSONPath path executed on the Run", + example = "$.results") @QueryParam("query") String jsonpath, @QueryParam("uri") String schemaUri, @QueryParam("array") @DefaultValue("false") boolean array); @Path("{id}/querydataset") @GET QueryResult queryDatasetData(@PathParam("id") int datasetId, - @Parameter(required = true) @QueryParam("query") String jsonpath, + @Parameter(required = true, + name = "query", + description = "JSONPath path executed on the Dataset", + example = "$.test") @QueryParam("query") String jsonpath, @QueryParam("array") @DefaultValue("false") boolean array, @QueryParam("schemaUri") String schemaUri); diff --git a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/internal/services/UserService.java b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/internal/services/UserService.java index 3b6f98ae4..b0dd4d687 100644 --- a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/internal/services/UserService.java +++ b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/internal/services/UserService.java @@ -14,6 +14,7 @@ import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; import org.eclipse.microprofile.openapi.annotations.tags.Tag; @@ -33,7 +34,9 @@ public interface UserService { @GET @Path("search") @Blocking - List searchUsers(@Parameter(required = true) @QueryParam("query") String query); + @Operation(description="Search for user(s) with an optional query condition.") + List searchUsers(@Parameter(required = true, name="query", description = "filter users by username (case insensitive)", + example = "user") @QueryParam("query") String query); @POST @Path("info") diff --git a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/services/RunService.java b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/services/RunService.java index da17bc5c0..8cd1b3227 100644 --- a/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/services/RunService.java +++ b/horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/services/RunService.java @@ -322,13 +322,16 @@ Response addRunFromData(@Parameter(required = true) @QueryParam("start") String @GET @Path("autocomplete") @ApiIgnore - List autocomplete(@Parameter(required = true) @QueryParam("query") String query); + List autocomplete(@Parameter(required = true, + name = "query", + description = "JSONPath to be autocompleted", + example = "$.") @QueryParam("query") String query); @GET @Path("list") @Operation(description = "Retrieve a paginated list of Runs with available count") @Parameters(value = { - @Parameter(name = "query", description = "query string to filter runs", example = ""), + @Parameter(name = "query", description = "query string to filter runs", example = "$.*"), @Parameter(name = "matchAll", description = "match all Runs?", example = "false"), @Parameter(name = "roles", description = "__my, __all or a comma delimited list of roles", example = "__my"), @Parameter(name = "trashed", description = "show trashed runs", example = "false"),