Skip to content
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

Encode filter parameter as a base64-encoded JSON string in List requests #563

Merged
merged 12 commits into from
Jan 2, 2019

Conversation

neuromage
Copy link
Contributor

@neuromage neuromage commented Dec 18, 2018

Generate new swagger definitions and use these to generate the frontend
APIs using npm run apis.

This is to support filtering in List requests, as the current
grpc-gateway swagger generator tool does not support repeated fields in
requests used in GET endpoints.

Using POST lets us get around the problem in issue grpc-ecosystem/grpc-gateway#492


This change is Reviewable

@neuromage
Copy link
Contributor Author

/assign @yebrahim
/assign @rileyjbauer

@yebrahim
Copy link
Contributor

The frontend needs to change the API calls to pass the object. I can help take a look at this soon.

* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
createExperiment(body: ApiExperiment, options: any = {}): FetchArgs {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the heads up. This was indeed messed up, because both create and list used the same endpoints with differing verbs (POST and GET).

I decided to go another route: encode the filter as a simple JSON string and put it in a single parameter. Maybe this was what you were alluding to earlier, and I think this should work better, as it doesn't break any of the existing semantics. See what you think.

@neuromage neuromage changed the title Make all ListXXX operations use POST instead of GET. Encode filter parameter as a base64-encoded JSON string in List requests Dec 18, 2018
@@ -44,7 +46,23 @@ func (s *ExperimentServer) ListExperiment(ctx context.Context, request *api.List
if request.PageToken != "" {
opts, err = list.NewOptionsFromToken(request.PageToken, int(request.PageSize))
} else {
opts, err = list.NewOptions(&model.Experiment{}, int(request.PageSize), request.SortBy, request.Filter)
var f *api.Filter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance we can reuse this code instead of copying it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I consolidated this logic into a new function, and also added tests for it. PTAL.

Copy link
Contributor

@yebrahim yebrahim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm
just one minor comment.

Copy link
Contributor

@yebrahim yebrahim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm
just a minor comment.

}

got, err := parseAPIFilter(in)
if !cmp.Equal(got, want) || err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just assert.Equal(...? It takes care of showing the compared objects in the error message right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but I actually prefer the cmp package, as it prints a more precise diff. It's also maintained and recommended by the Go team.


// SetFilter adds the filter to the list experiment params
func (o *ListExperimentParams) SetFilter(filter *string) {
o.Filter = filter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this function is so simple, and not widely used, why not just inline it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is auto-generated by the swagger tool. Everything under api/go_http_client and api/go_client is autogenerated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, I didn't realize they were auto-generated

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, yeah, it's definitely not obvious :-)

@rileyjbauer
Copy link
Contributor

/lgtm

@rileyjbauer
Copy link
Contributor

/test kubeflow-pipeline-e2e-test

@@ -44,7 +44,12 @@ func (s *ExperimentServer) ListExperiment(ctx context.Context, request *api.List
if request.PageToken != "" {
opts, err = list.NewOptionsFromToken(request.PageToken, int(request.PageSize))
} else {
opts, err = list.NewOptions(&model.Experiment{}, int(request.PageSize), request.SortBy, request.Filter)
f, err := parseAPIFilter(request.Filter)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not quite understand the if else statement here. does it means only one of pagetoken and filter can exist?
Are filtered results paginated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pagetoken encapsulates both sorting and filtering, so if you already have a non-empty page token, we ignore further filtering + sorting criteria.

return nil, nil
}

errorF := func(err error) (*api.Filter, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is any good reason of having this function instead of returning errors directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoids duplicate in error handling, since the message is the same. It's a very idiomatic Go thing to do.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sgtm

if err := jsonpb.UnmarshalString(string(b), f); err != nil {
return errorF(err)
}
return f, nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add additional validation to make sure the filter format is correct?
e.g. value missing, op missing etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The api filter will get parsed into a filter.Filter object that can actually be used to do filtering. That parsing will sanity check to ensure the filter is well formed and return an error otherwise.

@k8s-ci-robot k8s-ci-robot removed the lgtm label Dec 21, 2018
@neuromage
Copy link
Contributor Author

Thanks @rileyjbauer for pointing out that the Filter definitions for Swagger went missing with this change. This was caused by the fact that Filter is only implicitly used by the API, so protoc-gen-swagger will not generate Swagger files correctly for this proto. I modified the Makefile to hack in a dummy service during generation to ensure we get the Swagger definitions. And I updated the npm run apis script to generate TS interface definitions for filter as well.

PTAL.

@neuromage
Copy link
Contributor Author

/retest

Copy link
Contributor

@yebrahim yebrahim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oof, that hack!
So the protoc->swagger tool can't generate a standalone message, and only works with service definitions?
What's the harm of leaving that service defined in the proto?

/lgtm

@@ -52,6 +52,13 @@ all: dependencies
--grpc-gateway_out=logtostderr=true:go_client \
*.proto

# Filter.proto is implicitly used by clients and server, and transmitted as a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: indentation (this file uses tabs for some reason).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: indentation (this file uses tabs for some reason).

It's not a nit. Makefile requires tabs for receipt prefixes and will misbehave if they are not there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird, but good to know.

@k8s-ci-robot k8s-ci-robot added lgtm and removed lgtm labels Dec 21, 2018
@neuromage
Copy link
Contributor Author

Yeah, it's a pretty ugly hack.

I did have the dummy service in initially, but it was generating a corresponding client/server code in Go, which didn't seem like the right thing to do. Hence, this hack :-(

@neuromage
Copy link
Contributor Author

/retest

@rileyjbauer
Copy link
Contributor

/test kubeflow-pipeline-e2e-test

Generate new swagger definitions and use these to generate the frontend
APIs using `npm run apis`.

This is to support filtering in List requests, as the current
grpc-gateway swagger generator tool does not support repeated fields in
requests used in GET endpoints.
This lets us keep filter as a simple parameter in the ListXXX requests,
and gets around having to use POST for List requests.
@yebrahim
Copy link
Contributor

How about setting up "go vet -shadow" for catching these errors. I think shadowing is bad practice anyway.
@neuromage @IronPan

@neuromage
Copy link
Contributor Author

Agreed! We can set this up in Travis as part of the presubmit. If you're ok with it, I can try to do that, and also fix any existing shadow errors.

@yebrahim
Copy link
Contributor

I'd prefer it in a separate change, this one had already gotten big enough, but if you feel you want to add it here please feel free to.

@yebrahim
Copy link
Contributor

/lgtm

@neuromage
Copy link
Contributor Author

yes, definitely, I meant in a separate PR. This is one is good to go @yebrahim @IronPan

@IronPan
Copy link
Member

IronPan commented Jan 2, 2019

/lgtm
/approve
assuming that you will have a separate PR to validate the query string as we discussed offline

  • If page token doesn't exist, validate the filter and sort by fields.
  • If page token exist, either
    • filter and sort by fields exist and they are valid and equals to the one encoded in page token
    • or they can be opt-out and no validation is needed in this case.

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: IronPan

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

1 similar comment
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: IronPan

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot merged commit 8616398 into kubeflow:master Jan 2, 2019
HumairAK pushed a commit to red-hat-data-services/data-science-pipelines that referenced this pull request Mar 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants