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 part2 #455

Merged

Conversation

tarrantzhang
Copy link
Collaborator

@tarrantzhang tarrantzhang commented Jul 28, 2017

Continuing on RFC for custom response creation.
Part 1.

Implementing steps 1~5:

  • Define interface ResponseWriter and its default implementation
  • Refactor Response class, splitting into ResponseData and three implementations of ResponseWriter
  • Define interface ResponseWriterSelector and its default implementation
  • Implement
        void addResponseType(ResponseType type, ResponseWriter writer);
  • Hook up the new serialization logic with HttpResponseMaker to replace the old one

@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 28, 2017
@tarrantzhang tarrantzhang force-pushed the refactor-httpresponsemaker-part2 branch from 34fd04f to 18f15ce Compare July 28, 2017 22:13
@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 28, 2017
@yahoo yahoo deleted a comment Jul 31, 2017
@yahoo yahoo deleted a comment Jul 31, 2017
@yahoo yahoo deleted a comment Jul 31, 2017
@yahoo yahoo deleted a comment Jul 31, 2017
@yahoo yahoo deleted a comment Jul 31, 2017
@yahoo yahoo deleted a comment Jul 31, 2017
@yahoo yahoo deleted a comment Jul 31, 2017
@@ -88,7 +87,8 @@ public Response(
SimplifiedIntervalList volatileIntervals,
Map<String, URI> paginationLinks,
Pagination pagination,
ObjectMappersSuite objectMappers
ObjectMappersSuite objectMappers,
ApiRequest apiRequest
Copy link
Collaborator Author

@tarrantzhang tarrantzhang Jul 31, 2017

Choose a reason for hiding this comment

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

I really hesitate to add apiRequest to ResponseData, but it's needed by write function in line 183

Copy link
Contributor

Choose a reason for hiding this comment

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

We should just remove that function. It no longer belongs on this class, and this is so deep into the guts of Fili, that I don't think we have to worry about removing it breaking anybody.

Copy link
Contributor

Choose a reason for hiding this comment

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

Basically, anything related to creating JSON or CSV no longer belongs on this class, and should be removed. That includes the ObjectMappers and the jsonFactory and csvMapper.

Copy link
Contributor

Choose a reason for hiding this comment

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

We can probably also drop the ResponseFormatType parameter, since the writers are pulling that off the ApiRequest.

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.

This still needs a little bit of work. That being said, the writers look pretty good, and I'm very happy about your efforts to document everything.

Mostly, we just need to refactor the tests to better line up with the new response format, finish tearing all the response writing out of ResponseData and we need to allow the user to inject a ResponseWriter, and then use it.

*
* @throws IOException if a problem is encountered writing to the OutputStreamC
*/
public void write(
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be annotated with @Override

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, since this method isn't changing the contract of the method it's implementing, JavaDoc isn't necessary.

*
* @throws IOException if a problem is encountered writing to the OutputStreamC
*/
public void write(
Copy link
Contributor

Choose a reason for hiding this comment

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

Annotated with @Override.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, since this method isn't changing the contract of the method it's implementing, JavaDoc isn't necessary.

OutputStream os
) throws IOException {
ResponseWriter writer = responseWriterSelector.select(request);
writer.write(request, responseData, os);
Copy link
Contributor

Choose a reason for hiding this comment

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

We should do a null check on writer here and throw a useful error message if the response type isn't in the map (i.e. isn't recognized). Otherwise, if at some point down the line, someone adds a custom response format, but forgets to register the writer, then they'll just get a vague NullPointerException.

* @param request ApiRequest object with all the associated info in it
* @return Response writer for the given format type
*/
public ResponseWriter select(ApiRequest request) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be annotated with @Override.

import java.util.Map;

/**
* Response writer selector object.
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the place where I would specify that this selector selects the ResponseWriter based on the format type in the ApiRequest, rather than on the constructor or method signature.

import java.io.OutputStream;

/**
* Response writer interface.
Copy link
Contributor

@archolewa archolewa Jul 31, 2017

Choose a reason for hiding this comment

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

We should provide a better class level JavaDoc. Nothing here says anything that the class declaration doesn't already.

In general, when writing JavaDoc you should focus on the what, and possibly the why depending on context (and how much tolerance your co-developers have for large class docs 😉 .

For example, I might write something like the following:

The interface for objects that write fully-processed ResultSets back to the user. 
This allows customers to fully customize how they choose to serialize the results from
 Fili based on the request.

* @param os OutputStream
* @throws IOException if a problem is encountered writing to the OutputStream
*/
default void write(ApiRequest request, ResponseData responseData, OutputStream os)throws IOException {
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing a space: OuputStream os)throws



/**
* Response writer selector object.
Copy link
Contributor

Choose a reason for hiding this comment

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

Again, this should provide a bit more insight into what this exists, and why it should be used. Maybe something like:

Typically, a Fili program's response writing infrastructure will consist of a collection of unrelated 
ResponseWriters each responsible for serializing the ResultSet into a specific format, and a single 
ResponseWriter that chooses the correct one based on the current state of the DataApiRequest.

This interface allows customers provide a clean interface to the logic that makes that selection. Its 
sole purpose is to take a `DataApiRequest` and return the `ResponseWriter` that should be used 
to  write the response. 

@@ -107,7 +107,11 @@ class ResponseSpec extends Specification {

Set<Column> columns
Set<LogicalMetric> testLogicalMetrics
Response response
ResponseData response
Copy link
Contributor

Choose a reason for hiding this comment

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

At a minimum, we should turn this into the FiliResponseWriterSpec, since that's what this is testing now.

However, we really should break this apart. Anything that tests only one format (i.e. just CSV, or just JSON) should be moved into a spec for the appropriate ResponseWriter. Anything that tests the getters in ResponseData should be moved into ResponseDataSpec.

In my mind, testing should be the following:

  1. There should be dedicated Spec files for each writer, which test that they serialize the format correclty.
  2. There should be a dedicated Spec that tests those ResponseData utility methods that are interesting.
  3. There should be a Spec for FiliResponseWriter that ensures it selects the right writer (heh).

@@ -136,7 +136,7 @@ private ResponseBuilder createResponseBuilder(
LinkedHashMap::new
));

Response response = new Response(
ResponseData response = new ResponseData(
Copy link
Contributor

Choose a reason for hiding this comment

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

You're building a ResponseData object, but you're not actually using a ResponseWriter anywhere (except through the ResponseData::write method that should be removed). In order to be effective, the ResponseWriter needs to be injected, just like the HttpResponseMaker:

  1. Add a binding of ResponseWriter to the AbstractBinderFactory that defaults to the FiliResponseWriter.
  2. Pass the ResponseWriter into the HttpResponseMaker. The injection framework should then get the writer into the maker.
  3. Use the writer to write the response to the outputstream.
  4. Pass the output stream to the response builder

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@archolewa I have trouble making ResponseWriter injectable.

The default ResponseWriter requires ResponseWriterSelector which requires all three pre-defined ResponseWriter to be instantiated (JSON, JsonApi and Csv) at binding time. But JsonResponseWriter and JsonApiResponseWriter needs two variables: responseData and paginationLinks that are not available at binding time. Is there a way to work around this.

@yahoo yahoo deleted a comment Aug 2, 2017
@yahoo yahoo deleted a comment Aug 2, 2017
@yahoo yahoo deleted a comment Aug 2, 2017
@yahoo yahoo deleted a comment Aug 2, 2017
@yahoo yahoo deleted a comment Aug 2, 2017
@tarrantzhang
Copy link
Collaborator Author

Most line changes are due to refactoring large test cases and Response class, the number itself looks scary though.

@yahoo yahoo deleted a comment Aug 7, 2017
@yahoo yahoo deleted a comment Aug 7, 2017
@yahoo yahoo deleted a comment Aug 7, 2017
@yahoo yahoo deleted a comment Aug 7, 2017
@yahoo yahoo deleted a comment Aug 7, 2017
@yahoo yahoo deleted a comment Aug 7, 2017
@yahoo yahoo deleted a comment Aug 7, 2017
@yahoo yahoo deleted a comment Aug 7, 2017
@yahoo yahoo deleted a comment Aug 7, 2017
@yahoo yahoo deleted a comment Aug 7, 2017
@yahoo yahoo deleted a comment Aug 8, 2017
@yahoo yahoo deleted a comment Aug 8, 2017
@yahoo yahoo deleted a comment Aug 8, 2017
@yahoo yahoo deleted a comment Aug 8, 2017
@yahoo yahoo deleted a comment Aug 8, 2017
@yahoo yahoo deleted a comment Aug 8, 2017
@yahoo yahoo deleted a comment Aug 8, 2017
@yahoo yahoo deleted a comment Aug 8, 2017
@yahoo yahoo deleted a comment Aug 8, 2017
@archolewa
Copy link
Contributor

@kevinhinterlong I agree that having custom response types would be nice to have, but it's outside the scope of this PR. When someone wants custom response types, they can come in and make the refactors to the response type (i.e. hiding the enum behind an interface) that will be necessary.

@kevinhinterlong
Copy link
Member

@archolewa I agree I think this PR is ready. I'll go ahead and make an issue for this

Rework on response serialization logic, and allow customizable response.
@tarrantzhang tarrantzhang force-pushed the refactor-httpresponsemaker-part2 branch from 1f153cc to f044062 Compare August 8, 2017 15:51
@yahoo yahoo deleted a comment Aug 8, 2017
@yahoo yahoo deleted a comment Aug 8, 2017
@yahoo yahoo deleted a comment Aug 8, 2017
@yahoo yahoo deleted a comment Aug 8, 2017
@tarrantzhang
Copy link
Collaborator Author

tarrantzhang commented Aug 8, 2017

@archolewa @kevinhinterlong Rebased and squashed, ready for merge.

@archolewa archolewa merged commit fdadc11 into yahoo:master Aug 8, 2017
saiyan86 pushed a commit to saiyan86/fili that referenced this pull request Aug 28, 2017
Rework on response serialization logic, and allow customizable response.
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.

3 participants