-
Notifications
You must be signed in to change notification settings - Fork 96
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
Conversation
* | ||
* @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 |
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.
Not so sure if UriInfo
and ResponseFormatType
still needed as we can get them from ApiRequest.
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.
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
.
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.
CHANGELOG needed
@@ -907,6 +910,15 @@ protected JobPayloadBuilder buildJobPayloadBuilder() { | |||
} | |||
|
|||
/** | |||
* Initializes the factory that builds HttpResponseMaker. | |||
* | |||
* @return An instance of the {@link HttpResponseMaker} |
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.
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; |
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.
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.* |
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.
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 |
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.
static import goes to the top
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.
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 |
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.
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, |
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.
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.
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.
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, |
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.
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.
Just the one comment that can easily be handled in the next PR.
) { | ||
ResponseBuilder rspBuilder = createResponseBuilder( | ||
preResponse.getResultSet(), | ||
preResponse.getResponseContext(), | ||
responseFormatType, | ||
uriInfo | ||
apiRequest.getFormat(), |
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 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.
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.
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) |
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.
One entry per PR
) { | ||
@SuppressWarnings("unchecked") | ||
ResponseFormatType responseFormatType = apiRequest.getFormat(); | ||
UriInfo uriInfo = apiRequest.getUriInfo(); |
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.
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; | |||
|
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.
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) { |
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.
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); |
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.
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); |
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 a test. At least testing that it's been binded like 1 * dc.bind({ it.implementation.contains(HttpResponseMaker.canonicalName) }, _)
in "test configure bindings"()
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.
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 |
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.
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 |
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.
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 |
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.
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 |
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.
@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?
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 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.
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.
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.
@QubitPi The steps are mislabeled in the RFC (there are two 5's). He's implementing the second five |
@QubitPi Sorry didn't sync up the PR description with RFC, it should be steps 6 & 7. |
719fc1f
to
2082607
Compare
This PR implements step 6,7 in RFC for custom response creation
httpResponseMaker
injectable.httpResponseChannel
constructor to takeApiRequest
.