-
Notifications
You must be signed in to change notification settings - Fork 112
Added ability to cache the whole graphql response #247
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
Changes from 7 commits
4d4020d
b66190e
8836fab
c52e45b
2322c25
9153aa5
86cc378
1cf9e73
6c95ef3
4b4edfe
aa936f9
4d25d64
2316944
ac6fc42
315b77f
bc93099
2902ee7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,22 +2,29 @@ | |
|
||
import graphql.ExecutionResult; | ||
import graphql.kickstart.execution.GraphQLObjectMapper; | ||
import graphql.kickstart.execution.input.GraphQLInvocationInput; | ||
import graphql.kickstart.servlet.cache.CachedResponse; | ||
import graphql.kickstart.servlet.cache.GraphQLResponseCache; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Slf4j | ||
@RequiredArgsConstructor | ||
class BatchedQueryResponseWriter implements QueryResponseWriter { | ||
|
||
private final List<ExecutionResult> results; | ||
private final GraphQLObjectMapper graphQLObjectMapper; | ||
private final GraphQLInvocationInput invocationInput; | ||
|
||
@Override | ||
public void write(HttpServletRequest request, HttpServletResponse response) throws IOException { | ||
public void write(HttpServletRequest request, HttpServletResponse response, GraphQLResponseCache responseCache) throws IOException { | ||
response.setContentType(HttpRequestHandler.APPLICATION_JSON_UTF8); | ||
response.setStatus(HttpRequestHandler.STATUS_OK); | ||
|
||
|
@@ -34,6 +41,16 @@ public void write(HttpServletRequest request, HttpServletResponse response) thro | |
|
||
String responseContent = responseBuilder.toString(); | ||
byte[] contentBytes = responseContent.getBytes(StandardCharsets.UTF_8); | ||
|
||
if (responseCache != null && responseCache.isCacheable(request, invocationInput)) { | ||
try { | ||
responseCache.put(request, invocationInput, CachedResponse.ofContent(contentBytes)); | ||
} catch (Throwable t) { | ||
log.warn(t.getMessage(), t); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd loose this log statement and just add the stacktrace to the log statement on the next line. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
log.warn("Ignore read from cache, unexpected error happened"); | ||
} | ||
} | ||
|
||
response.setContentLength(contentBytes.length); | ||
response.getOutputStream().write(contentBytes); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,15 +3,18 @@ | |
import graphql.GraphQLException; | ||
import graphql.kickstart.execution.GraphQLInvoker; | ||
import graphql.kickstart.execution.GraphQLQueryResult; | ||
import graphql.kickstart.servlet.input.BatchInputPreProcessResult; | ||
import graphql.kickstart.servlet.input.BatchInputPreProcessor; | ||
import graphql.kickstart.execution.input.GraphQLBatchedInvocationInput; | ||
import graphql.kickstart.execution.input.GraphQLInvocationInput; | ||
import graphql.kickstart.execution.input.GraphQLSingleInvocationInput; | ||
import java.io.IOException; | ||
import graphql.kickstart.servlet.cache.CacheResponseWriter; | ||
import graphql.kickstart.servlet.cache.CachedResponse; | ||
import graphql.kickstart.servlet.input.BatchInputPreProcessResult; | ||
import graphql.kickstart.servlet.input.BatchInputPreProcessor; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import lombok.extern.slf4j.Slf4j; | ||
import java.io.IOException; | ||
|
||
@Slf4j | ||
class HttpRequestHandlerImpl implements HttpRequestHandler { | ||
|
@@ -49,11 +52,31 @@ public void handle(HttpServletRequest request, HttpServletResponse response) thr | |
private void execute(GraphQLInvocationInput invocationInput, HttpServletRequest request, | ||
HttpServletResponse response) { | ||
try { | ||
if (configuration.getResponseCache() != null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bit too much logic here imo. Would be better if you'd move this part into a separate class and just call its method here. If you let it return a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
CachedResponse cachedResponse = null; | ||
try { | ||
cachedResponse = configuration.getResponseCache().get(request, invocationInput); | ||
} catch (Throwable t) { | ||
log.warn(t.getMessage(), t); | ||
log.warn("Ignore read from cache, unexpected error happened"); | ||
} | ||
|
||
if (cachedResponse != null) { | ||
CacheResponseWriter cacheResponseWriter = new CacheResponseWriter(); | ||
cacheResponseWriter.write(request, response, cachedResponse); | ||
return; | ||
} | ||
} | ||
|
||
GraphQLQueryResult queryResult = invoke(invocationInput, request, response); | ||
|
||
QueryResponseWriter queryResponseWriter = QueryResponseWriter.createWriter(queryResult, configuration.getObjectMapper(), | ||
configuration.getSubscriptionTimeout()); | ||
queryResponseWriter.write(request, response); | ||
QueryResponseWriter queryResponseWriter = QueryResponseWriter.createWriter( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd add the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
queryResult, | ||
configuration.getObjectMapper(), | ||
configuration.getSubscriptionTimeout(), | ||
invocationInput | ||
); | ||
queryResponseWriter.write(request, response, configuration.getResponseCache()); | ||
} catch (Throwable t) { | ||
response.setStatus(STATUS_BAD_REQUEST); | ||
log.info("Bad GET request: path was not \"/schema.json\" or no query variable named \"query\" given"); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package graphql.kickstart.servlet.cache; | ||
|
||
import graphql.kickstart.servlet.HttpRequestHandler; | ||
|
||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
public class CacheResponseWriter { | ||
|
||
public void write(HttpServletRequest request, HttpServletResponse response, CachedResponse cachedResponse) | ||
throws IOException { | ||
if (cachedResponse.isError()) { | ||
response.sendError(cachedResponse.getErrorStatusCode(), cachedResponse.getErrorMessage()); | ||
} else { | ||
response.setContentType(HttpRequestHandler.APPLICATION_JSON_UTF8); | ||
response.setStatus(HttpRequestHandler.STATUS_OK); | ||
response.setCharacterEncoding(StandardCharsets.UTF_8.name()); | ||
response.setContentLength(cachedResponse.getContentBytes().length); | ||
response.getOutputStream().write(cachedResponse.getContentBytes()); | ||
} | ||
} | ||
|
||
} |
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 block or something nearly identical is repeated in every
QueryResponseWriter
implementation. Instead it would be better if you'd apply the decorator pattern. If somebody enabled caching then have theQueryResponseWriter
factory method create someCachingResponseWriter
(which should implementQueryResponseWriter
too obviously) wrapping the other implementations. That way you have one class with this logic and you don't pollute the other classes with caching logic. Ideally they're not aware of that at all.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.
Done.