-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adds support for query parameters #625
Conversation
Signed-off-by: Bruno Calza <brunoangelicalza@gmail.com>
6eeb081
to
2f4a7f2
Compare
Makefile
Outdated
@@ -80,7 +80,8 @@ lint: | |||
.PHONY: lint | |||
|
|||
# OpenAPI | |||
SPEC_URL=https://raw.githubusercontent.com/tablelandnetwork/docs/main/specs/validator/tableland-openapi-spec.yaml | |||
#SPEC_URL=https://raw.githubusercontent.com/tablelandnetwork/docs/main/specs/validator/tableland-openapi-spec.yaml | |||
SPEC_URL=https://raw.githubusercontent.com/tablelandnetwork/docs/8ec1d2f5137d4372b493cf6c64707ae21b5ba19b/specs/validator/tableland-openapi-spec.yaml |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotta point back to main after tablelandnetwork/docs#198 is merged
@@ -91,6 +92,7 @@ gen-api-v1: | |||
&& mv go/* . \ | |||
&& rm -rf go main.go Dockerfile README.md api .swagger-codegen .swagger-codegen-ignore *.yaml \ | |||
&& sed -i 's/\*OneOfTableAttributesValue/interface{}/' model_table_attributes.go \ | |||
&& sed -i 's/\OneOfQueryParamsItems/interface{}/' model_query.go \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a trick we do because the type of the field params
in the Post body can be anything. See mode_query.go:L16
go.mod
Outdated
@@ -20,7 +20,7 @@ require ( | |||
github.com/sethvargo/go-limiter v0.7.2 | |||
github.com/spf13/cobra v1.7.0 | |||
github.com/stretchr/testify v1.8.2 | |||
github.com/tablelandnetwork/sqlparser v0.0.0-20230605164749-c0e6862c37f6 | |||
github.com/tablelandnetwork/sqlparser v0.0.0-20240523182602-af3edf08e3db |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be changed after tablelandnetwork/go-sqlparser#388 is merged
@@ -12,6 +12,8 @@ package apiv1 | |||
type Query struct { | |||
// The SQL read query statement | |||
Statement string `json:"statement,omitempty"` | |||
// The values of query parameters | |||
Params []interface{} `json:"params,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this code was generated by running this
@@ -231,9 +231,14 @@ func (c *Controller) GetTableQuery(rw http.ResponseWriter, r *http.Request) { | |||
rw.Header().Set("Content-Type", "application/json") | |||
|
|||
stm := r.URL.Query().Get("statement") | |||
params := []string{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
capturing the params
query param from a GET request
@@ -276,6 +281,7 @@ func (c *Controller) PostTableQuery(rw http.ResponseWriter, r *http.Request) { | |||
|
|||
// setting a default body because these options could be missing from JSON | |||
body := &apiv1.Query{ | |||
Params: []any{}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the body can contain now a params
field
@@ -289,8 +295,31 @@ func (c *Controller) PostTableQuery(rw http.ResponseWriter, r *http.Request) { | |||
} | |||
_ = r.Body.Close() | |||
|
|||
params := make([]string, len(body.Params)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we try to standardize to a []string
, the same as we would get if the request was a GET
require.Len(t, res, 2) // it should be 2 because we inserted two rows | ||
require.Equal(t, int64(5), res[0].BlockNumber) // the block number should be 5 | ||
} | ||
|
||
func TestQueryParams(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added this e2e test
@@ -61,7 +61,9 @@ func ReadUnwrap() ReadOption { | |||
var queryURL, _ = url.Parse("/api/v1/query") | |||
|
|||
// Read runs a read query with the provided opts and unmarshals the results into target. | |||
func (c *Client) Read(ctx context.Context, query string, target interface{}, opts ...ReadOption) error { | |||
func (c *Client) Read( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This go client library now accepts query params. @asutula if you update the go library in your project your code will probably break, you just have to add an empty slice of string to fix it
@@ -111,6 +111,53 @@ func TestReadRunSQL(t *testing.T) { | |||
} | |||
} | |||
|
|||
func TestReadQueryWithParams(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added this test just to see if the query is being parsed and resolved correctly
Signed-off-by: Bruno Calza <brunoangelicalza@gmail.com>
params := make([]string, len(body.Params)) | ||
for i, p := range body.Params { | ||
switch v := p.(type) { | ||
case float64: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A JSON Number is converted float64
when decoded
case float64: | ||
params[i] = fmt.Sprint(v) | ||
case string: | ||
params[i] = fmt.Sprintf("\"%s\"", v) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We must enclose the string with double quotes so that it behaves similarly with GET /query?statement=...¶ms="bla"
See also how PrepareParams
handle this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good!
Signed-off-by: Bruno Calza <brunoangelicalza@gmail.com>
@avichalp would you mind approving this again? i've updated with the correct parser version |
Summary
This PR adds support for the new SQL parser that is able to interpret placeholders (
?
) and replace them with query parameters that come from the request. You can now perform the following queries:GET /query?statement="select * from my_table where id = ?"¶ms=1
POST --json '{"statement": "SELECT * FROM my_table WHERE counter = ?", "params" : [1], "unwrap": true, "extract": true}' /query
If you want to provide multiple params values, you just repeat the
params
with new values. For example¶ms=1¶ms=2¶ms=3
. And if you want to provide a string, you gotta enclose it¶ms="3"
.This PR depends on add support for query parameters go-sqlparser#388 and add support for params in POST /query docs#198
Context
The context for this PR can be found at ENG-852.
Make sure you understand tablelandnetwork/go-sqlparser#388 and tablelandnetwork/docs#198 before reviewing this one.
Implementation overview
This PR changes a lot of files, but most of the changes are just a side effect of the main changes. Here's an overview on the most important changes:
Controller
The controller needed to be changed to capture the
params
query parameter. Both GET /query and POST /query were changed. Theparams
is captured as a slice of string ([]string
). In the case of the POST, since we have an[]interface{}
after the JSON is decoded into memory, we try to convert it to[]string
.Resolver is now passed to the
GatewayService
I made a change to make the resolver a dependency of the
GatewayService
. See the implication of this change here, here and here. The reason for that is that it makes more sense for theGatewayService
to attach the parameters values to the resolver, than the storage layer. The storage layer is just responsible for executing the query.The resolver is not a dependency of
GatewayStore
and is now passed in theRead
methodWe already mentioned how the resolver is passed to a
GatewayService
instead ofGatewayStore
. In order to build the final statement, we need to pass the resolver to theRead
method. See it here:The resolver implementation needs to implement
GetBindValues() []sqlparser.Expr
We must implement the new method of our resolver (remember this? ). For that, we make use of
PrepareParams
that converts[]string
that comes from the controller to[]sqlparser.Expr
and store those values inside the struct. Here's howPrepareParams
is used.Review orientation
internal/router/controllers/controller.go
and understand the change made in the controllercmd/api/main.go
and see how the resolver is now passed to the gateway and not to the storage layer then check the changes atinternal/gateway/gateway.go
internal/gateway/impl/gateway_store.go
filepkg/parsing/readstatementresolver.go
filepkg/client/v1/readquery.go
)