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

Custom Response creation part1 #447

Merged
merged 1 commit into from
Jul 28, 2017

Conversation

tarrantzhang
Copy link
Collaborator

@tarrantzhang tarrantzhang commented Jul 26, 2017

This PR implements step 6,7 in RFC for custom response creation

  • Make httpResponseMaker injectable.
  • Change signature of httpResponseChannel constructor to take ApiRequest.

*
* @return Completely built response with headers and result set
*/
public javax.ws.rs.core.Response buildResponse(
PreResponse preResponse,
ResponseFormatType responseFormatType,
UriInfo uriInfo
UriInfo uriInfo,
ApiRequest apiRequest
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not so sure if UriInfo and ResponseFormatType still needed as we can get them from ApiRequest.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can safely get rid of the UriInfo and ResponseFormatType fields, and let people pull them from the ApiRequest when needed. I prefer to have one source of truth whenever possible.

The same applies to HttpResponseChannel.

Copy link
Contributor

@QubitPi QubitPi left a comment

Choose a reason for hiding this comment

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

CHANGELOG needed

@@ -907,6 +910,15 @@ protected JobPayloadBuilder buildJobPayloadBuilder() {
}

/**
* Initializes the factory that builds HttpResponseMaker.
*
* @return An instance of the {@link HttpResponseMaker}
Copy link
Contributor

@QubitPi QubitPi Jul 26, 2017

Choose a reason for hiding this comment

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

Lowercase An (This is fili doc standard)

import rx.Observer;

import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

import static com.yahoo.bard.webservice.web.handlers.workflow.DruidWorkflow.RESPONSE_WORKFLOW_TIMER;
Copy link
Contributor

Choose a reason for hiding this comment

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

static import goes to the top

import javax.ws.rs.core.PathSegment
import javax.ws.rs.core.Response
import javax.ws.rs.core.UriInfo
import javax.ws.rs.core.*
Copy link
Contributor

Choose a reason for hiding this comment

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

Avoid start import


import static com.yahoo.bard.webservice.data.time.DefaultTimeGrain.DAY
import static com.yahoo.bard.webservice.web.responseprocessors.ResponseContextKeys.API_METRIC_COLUMN_NAMES
import static com.yahoo.bard.webservice.web.responseprocessors.ResponseContextKeys.REQUESTED_API_DIMENSION_FIELDS
Copy link
Contributor

Choose a reason for hiding this comment

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

static import goes to the top

Copy link
Contributor

@archolewa archolewa left a comment

Choose a reason for hiding this comment

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

You missed a key step here: Passing in the HttpResponseMaker in as a constructor argument to DataServlet. If you don't do that, then Fili won't actually use the HttpResponseMaker bound in the AbstractBinderFactory!

*
* @return Completely built response with headers and result set
*/
public javax.ws.rs.core.Response buildResponse(
PreResponse preResponse,
ResponseFormatType responseFormatType,
UriInfo uriInfo
UriInfo uriInfo,
ApiRequest apiRequest
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can safely get rid of the UriInfo and ResponseFormatType fields, and let people pull them from the ApiRequest when needed. I prefer to have one source of truth whenever possible.

The same applies to HttpResponseChannel.

@@ -404,6 +405,7 @@ public void getData(
apiRequest.getAsyncAfter(),
apiRequest.getFormat(),
uriInfo,
apiRequest,
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we have to pass the entire apiRequest into this method anyway, we might as well drop the parameters that we pull directly from the apiRequest (i.e. asyncAfter, format, the uriInfo). It'll make this method's signature a little bit smaller, which is always nice when you can get away with it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't really have a good place to put this, because github stupidly insists on limiting what lines I can comment on. Right now, we're not actually using the HttpResponseMaker that is being injected. You'll note that on line 397 we are still manually building the HttpResponseMaker. So it doesn't matter what customers put in the AbstractBinderFactory, we'll still build and use the version defined in Fili!

So, In addition to providing an injection point in the AbstractBinderFactory, you also need to make the HttpResponseMaker a constructor argument for DataServlet.

Since DataServlet has the @Inject annotation, HK2 will build it for us, and as a part of that, it will build all of the constructor arguments and pass them in to the DataServlet.

httpResponseMaker,
apiRequest.getFormat(),
apiRequest.getUriInfo()
apiRequest,
Copy link
Contributor

Choose a reason for hiding this comment

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

@yahoo yahoo deleted a comment Jul 27, 2017
Copy link
Contributor

@archolewa archolewa left a comment

Choose a reason for hiding this comment

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

Just the one comment that can easily be handled in the next PR.

) {
ResponseBuilder rspBuilder = createResponseBuilder(
preResponse.getResultSet(),
preResponse.getResponseContext(),
responseFormatType,
uriInfo
apiRequest.getFormat(),
Copy link
Contributor

Choose a reason for hiding this comment

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

The response builder should also take the full ApiRequest instead of just the format and uriInfo, or are you planning to make that change in the next PR as part of splitting Response? If so, feel free to disregard this comment.

Copy link
Contributor

@QubitPi QubitPi left a comment

Choose a reason for hiding this comment

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

I didn't see any implementation for step 5. Is that intended?

CHANGELOG.md Outdated
- [Make HttpResponseMaker injectable](https://github.com/yahoo/fili/pull/447)
* Make `HttpResponseMaker` injectable. `DataServlet` and `JobsServlet` takes `HttpResponseMaker` as input parameter now

- [Change functions signature related to custom response creation](https://github.com/yahoo/fili/pull/447)
Copy link
Contributor

Choose a reason for hiding this comment

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

One entry per PR

) {
@SuppressWarnings("unchecked")
ResponseFormatType responseFormatType = apiRequest.getFormat();
UriInfo uriInfo = apiRequest.getUriInfo();
Copy link
Contributor

Choose a reason for hiding this comment

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

uriInfo is used only once. It's better to have ResponseFormat.getCsvContentDispositionValue(apiRequest.getUriInfo()) instead of ResponseFormat.getCsvContentDispositionValue(uriInfo)

@@ -114,6 +117,8 @@
private final ObjectWriter writer;
private final ObjectMappersSuite objectMappers;

Copy link
Contributor

Choose a reason for hiding this comment

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

remove empty line

@@ -474,15 +470,15 @@ private void setupAsynchronousWorkflows(
// resources once per subscription. A connectable Observable's chain is only executed once
// regardless of the number of subscriptions.
ConnectableObservable<Either<PreResponse, JobRow>> payloadEmitter;
if (asyncAfter == DataApiRequest.ASYNCHRONOUS_ASYNC_AFTER_VALUE) {
if (apiRequest.getAsyncAfter() == DataApiRequest.ASYNCHRONOUS_ASYNC_AFTER_VALUE) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Pull it into a variable, since it's used multiple times

Observable<PreResponse> queryResultsEmitter,
ContainerRequestContext containerRequestContext,
AsyncResponse asyncResponse,
HttpResponseMaker httpResponseMaker
) {
JobRow jobMetadata = jobRowBuilder.buildJobRow(uriInfo, containerRequestContext);
JobRow jobMetadata = jobRowBuilder.buildJobRow(apiRequest.getUriInfo(), containerRequestContext);
Copy link
Contributor

Choose a reason for hiding this comment

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

Pull it into a variable, since it's used multiple times

@@ -302,6 +303,8 @@ protected void configure() {

bind(getClock()).to(Clock.class);

bind(getHttpResponseMaker()).to(HttpResponseMaker.class);
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs a test. At least testing that it's been binded like 1 * dc.bind({ it.implementation.contains(HttpResponseMaker.canonicalName) }, _) in "test configure bindings"()

Copy link
Contributor

Choose a reason for hiding this comment

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

I disagree. This is a dirt-simple binding. All we'd be testing is the HK2 injection framework. Far as I can tell we test hardly any of the other similarly simple bindings (i.e. we don't verify that the ObjectMapperSuite is being bound correctly.

* @param asyncAfter How long the user is willing to wait for a synchronous request
* @param responseFormat The requested format for the response
* @param uriInfo The URI of the request
* @param apiRequest The apiRequest
Copy link
Contributor

Choose a reason for hiding this comment

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

Param description doesn't help too much. People already know that from param name. Descriptions talks about what it is and how it's gonna be used

@@ -61,21 +64,18 @@ public HttpResponseMaker(ObjectMappersSuite objectMappers, DimensionDictionary d
* Build complete response.
*
* @param preResponse PreResponse object which contains result set, response context and headers
* @param responseFormatType The format in which the response should be returned to the user
* @param uriInfo UriInfo of the request
* @param apiRequest apiRequest needed for serialization writer
Copy link
Contributor

Choose a reason for hiding this comment

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

2 spaces between param name and param desc. Capitalize first word in description (This is fili doc standard) ¯_(ツ)_/¯

@@ -95,18 +95,18 @@ public HttpResponseMaker(ObjectMappersSuite objectMappers, DimensionDictionary d
*
* @param resultSet The result set being processed
* @param responseContext A meta data container for the state gathered by the web container
* @param responseFormatType The format in which the response should be returned to the user
* @param uriInfo UriInfo of the request
* @param apiRequest apiRequest needed for serialization writer
Copy link
Contributor

Choose a reason for hiding this comment

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

2 spaces between param name and param desc. Capitalize first word in description

@@ -58,16 +35,15 @@ public HttpResponseChannel(
* @param apiRequest Api request object with all the associated info with it
* @param httpResponseMaker Helper class instance to prepare the response object
*
* @deprecated The ResponseFormatType and UriInfo should be passed explicitly, rather than implicitly via the
Copy link
Contributor

@QubitPi QubitPi Jul 28, 2017

Choose a reason for hiding this comment

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

@michael-mclawhorn @archolewa Is it really necessary to pass the giant ApiRequest object? It de-separate layer abstraction and makes it more difficult to set up a test. Does passing more variables from ApiRequest solves the problem, instead of passing ApiRequest?

Copy link
Contributor

@archolewa archolewa Jul 28, 2017

Choose a reason for hiding this comment

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

The purpose of making the HttpResponseChannel injectable is so that customers can inject their own HttpResponseChannel so that they can customize how they send back the response.

There are situations (like the one that motivated this change) where customers will want to customize the response format based on something in the request. However, we can't very well predict what components of the request customers will use to determine how to send the response.

Copy link
Contributor

Choose a reason for hiding this comment

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

And it actually doesn't it make that much harder to setup a test since Groovy ignores access modifiers and the DataApiRequest (at least) has a private default constructor. So you can build an empty ApiRequest and then use the request's withers to initialize only the pieces you care about for the test.

@archolewa
Copy link
Contributor

archolewa commented Jul 28, 2017

@QubitPi The steps are mislabeled in the RFC (there are two 5's). He's implementing the second five

@tarrantzhang
Copy link
Collaborator Author

@QubitPi Sorry didn't sync up the PR description with RFC, it should be steps 6 & 7.

@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 28, 2017
@QubitPi QubitPi merged commit ab4929f into yahoo:master Jul 28, 2017
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.

None yet

3 participants