Skip to content

Commit 4eab2c8

Browse files
committed
Use cdn for compatible subscriptions-transport-ws dependency in GraphiQLController
fix graphql-java-kickstart#463
1 parent f7c4489 commit 4eab2c8

File tree

3 files changed

+134
-127
lines changed

3 files changed

+134
-127
lines changed

example-graphql-subscription/src/main/resources/application.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ graphql:
99
subscriptions:
1010
websocket:
1111
path: /subscriptions
12+
13+
graphiql:
14+
cdn:
15+
enabled: true

graphiql-spring-boot-autoconfigure/src/main/java/graphql/kickstart/graphiql/boot/GraphiQLController.java

Lines changed: 129 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -27,146 +27,149 @@
2727
@Slf4j
2828
public abstract class GraphiQLController {
2929

30-
private static final String CDNJS_CLOUDFLARE_COM_AJAX_LIBS = "//cdnjs.cloudflare.com/ajax/libs/";
31-
private static final String CDN_JSDELIVR_NET_NPM = "//cdn.jsdelivr.net/npm/";
32-
private static final String GRAPHIQL = "graphiql";
33-
private static final String FAVICON_GRAPHQL_ORG = "//graphql.org/img/favicon.png";
34-
35-
@Autowired
36-
private Environment environment;
37-
38-
@Autowired
39-
private GraphiQLProperties graphiQLProperties;
40-
41-
private String template;
42-
private String props;
43-
private Properties headerProperties;
44-
45-
public void onceConstructed() throws IOException {
46-
loadTemplate();
47-
loadProps();
48-
loadHeaders();
49-
}
50-
51-
private void loadTemplate() throws IOException {
52-
try (InputStream inputStream = new ClassPathResource("graphiql.html").getInputStream()) {
53-
template = StreamUtils.copyToString(inputStream, Charset.defaultCharset());
54-
}
55-
}
56-
57-
private void loadProps() throws IOException {
58-
props = new PropsLoader(environment).load();
30+
private static final String CDNJS_CLOUDFLARE_COM_AJAX_LIBS = "//cdnjs.cloudflare.com/ajax/libs/";
31+
private static final String CDN_JSDELIVR_NET_NPM = "//cdn.jsdelivr.net/npm/";
32+
private static final String GRAPHIQL = "graphiql";
33+
private static final String FAVICON_GRAPHQL_ORG = "//graphql.org/img/favicon.png";
34+
35+
@Autowired
36+
private Environment environment;
37+
38+
@Autowired
39+
private GraphiQLProperties graphiQLProperties;
40+
41+
private String template;
42+
private String props;
43+
private Properties headerProperties;
44+
45+
public void onceConstructed() throws IOException {
46+
loadTemplate();
47+
loadProps();
48+
loadHeaders();
49+
}
50+
51+
private void loadTemplate() throws IOException {
52+
try (InputStream inputStream = new ClassPathResource("graphiql.html").getInputStream()) {
53+
template = StreamUtils.copyToString(inputStream, Charset.defaultCharset());
5954
}
60-
61-
private void loadHeaders() {
62-
PropertyGroupReader propertyReader = new PropertyGroupReader(environment, "graphiql.headers.");
63-
headerProperties = propertyReader.load();
64-
addIfAbsent(headerProperties, "Accept");
65-
addIfAbsent(headerProperties, "Content-Type");
55+
}
56+
57+
private void loadProps() throws IOException {
58+
props = new PropsLoader(environment).load();
59+
}
60+
61+
private void loadHeaders() {
62+
PropertyGroupReader propertyReader = new PropertyGroupReader(environment, "graphiql.headers.");
63+
headerProperties = propertyReader.load();
64+
addIfAbsent(headerProperties, "Accept");
65+
addIfAbsent(headerProperties, "Content-Type");
66+
}
67+
68+
private void addIfAbsent(Properties headerProperties, String header) {
69+
if (!headerProperties.containsKey(header)) {
70+
headerProperties.setProperty(header, MediaType.APPLICATION_JSON_VALUE);
6671
}
72+
}
6773

68-
private void addIfAbsent(Properties headerProperties, String header) {
69-
if (!headerProperties.containsKey(header)) {
70-
headerProperties.setProperty(header, MediaType.APPLICATION_JSON_VALUE);
71-
}
74+
public byte[] graphiql(String contextPath, @PathVariable Map<String, String> params, Object csrf) {
75+
if (csrf != null) {
76+
CsrfToken csrfToken = (CsrfToken) csrf;
77+
headerProperties.setProperty(csrfToken.getHeaderName(), csrfToken.getToken());
7278
}
7379

74-
public byte[] graphiql(String contextPath, @PathVariable Map<String, String> params, Object csrf) {
75-
if (csrf != null) {
76-
CsrfToken csrfToken = (CsrfToken) csrf;
77-
headerProperties.setProperty(csrfToken.getHeaderName(), csrfToken.getToken());
78-
}
79-
80-
Map<String, String> replacements = getReplacements(
81-
constructGraphQlEndpoint(contextPath, params),
82-
contextPath + graphiQLProperties.getEndpoint().getSubscriptions(),
83-
contextPath + graphiQLProperties.getSTATIC().getBasePath()
84-
);
85-
86-
String populatedTemplate = StrSubstitutor.replace(template, replacements);
87-
return populatedTemplate.getBytes(Charset.defaultCharset());
80+
Map<String, String> replacements = getReplacements(
81+
constructGraphQlEndpoint(contextPath, params),
82+
contextPath + graphiQLProperties.getEndpoint().getSubscriptions(),
83+
contextPath + graphiQLProperties.getSTATIC().getBasePath()
84+
);
85+
86+
String populatedTemplate = StrSubstitutor.replace(template, replacements);
87+
return populatedTemplate.getBytes(Charset.defaultCharset());
88+
}
89+
90+
private Map<String, String> getReplacements(
91+
String graphqlEndpoint,
92+
String subscriptionsEndpoint,
93+
String staticBasePath
94+
) {
95+
Map<String, String> replacements = new HashMap<>();
96+
replacements.put("graphqlEndpoint", graphqlEndpoint);
97+
replacements.put("subscriptionsEndpoint", subscriptionsEndpoint);
98+
replacements.put("staticBasePath", staticBasePath);
99+
replacements.put("pageTitle", graphiQLProperties.getPageTitle());
100+
replacements.put("pageFavicon", getResourceUrl(staticBasePath, "favicon.ico", FAVICON_GRAPHQL_ORG));
101+
replacements.put("es6PromiseJsUrl", getResourceUrl(staticBasePath, "es6-promise.auto.min.js",
102+
joinCdnjsPath("es6-promise", "4.1.1", "es6-promise.auto.min.js")));
103+
replacements.put("fetchJsUrl", getResourceUrl(staticBasePath, "fetch.min.js",
104+
joinCdnjsPath("fetch", "2.0.4", "fetch.min.js")));
105+
replacements.put("reactJsUrl", getResourceUrl(staticBasePath, "react.min.js",
106+
joinCdnjsPath("react", "16.8.3", "umd/react.production.min.js")));
107+
replacements.put("reactDomJsUrl", getResourceUrl(staticBasePath, "react-dom.min.js",
108+
joinCdnjsPath("react-dom", "16.8.3", "umd/react-dom.production.min.js")));
109+
replacements.put("graphiqlCssUrl", getResourceUrl(staticBasePath, "graphiql.min.css",
110+
joinJsDelivrPath(GRAPHIQL, graphiQLProperties.getCdn().getVersion(), "graphiql.css")));
111+
replacements.put("graphiqlJsUrl", getResourceUrl(staticBasePath, "graphiql.min.js",
112+
joinJsDelivrPath(GRAPHIQL, graphiQLProperties.getCdn().getVersion(), "graphiql.min.js")));
113+
replacements.put("subscriptionsTransportWsBrowserClientUrl", getResourceUrl(staticBasePath,
114+
"subscriptions-transport-ws-browser-client.js",
115+
joinJsDelivrPath("subscriptions-transport-ws", "0.8.3", "browser/client.js")));
116+
replacements.put("graphiqlSubscriptionsFetcherBrowserClientUrl", getResourceUrl(staticBasePath,
117+
"graphiql-subscriptions-fetcher-browser-client.js",
118+
joinJsDelivrPath("graphiql-subscriptions-fetcher", "0.0.2", "browser/client.js")));
119+
replacements.put("props", props);
120+
try {
121+
replacements.put("headers", new ObjectMapper().writeValueAsString(headerProperties));
122+
} catch (JsonProcessingException e) {
123+
log.error("Cannot serialize headers", e);
88124
}
89-
90-
private Map<String, String> getReplacements(
91-
String graphqlEndpoint,
92-
String subscriptionsEndpoint,
93-
String staticBasePath
94-
) {
95-
Map<String, String> replacements = new HashMap<>();
96-
replacements.put("graphqlEndpoint", graphqlEndpoint);
97-
replacements.put("subscriptionsEndpoint", subscriptionsEndpoint);
98-
replacements.put("staticBasePath", staticBasePath);
99-
replacements.put("pageTitle", graphiQLProperties.getPageTitle());
100-
replacements.put("pageFavicon", getResourceUrl(staticBasePath, "favicon.ico", FAVICON_GRAPHQL_ORG));
101-
replacements.put("es6PromiseJsUrl", getResourceUrl(staticBasePath, "es6-promise.auto.min.js",
102-
joinCdnjsPath("es6-promise", "4.1.1", "es6-promise.auto.min.js")));
103-
replacements.put("fetchJsUrl", getResourceUrl(staticBasePath, "fetch.min.js",
104-
joinCdnjsPath("fetch", "2.0.4", "fetch.min.js")));
105-
replacements.put("reactJsUrl", getResourceUrl(staticBasePath, "react.min.js",
106-
joinCdnjsPath("react", "16.8.3", "umd/react.production.min.js")));
107-
replacements.put("reactDomJsUrl", getResourceUrl(staticBasePath, "react-dom.min.js",
108-
joinCdnjsPath("react-dom", "16.8.3", "umd/react-dom.production.min.js")));
109-
replacements.put("graphiqlCssUrl", getResourceUrl(staticBasePath, "graphiql.min.css",
110-
joinJsDelivrPath(GRAPHIQL, graphiQLProperties.getCdn().getVersion(), "graphiql.css")));
111-
replacements.put("graphiqlJsUrl", getResourceUrl(staticBasePath, "graphiql.min.js",
112-
joinJsDelivrPath(GRAPHIQL, graphiQLProperties.getCdn().getVersion(), "graphiql.min.js")));
113-
replacements.put("subscriptionsTransportWsBrowserClientUrl", joinStaticPath(staticBasePath,
114-
"subscriptions-transport-ws-browser-client.js"));
115-
replacements.put("graphiqlSubscriptionsFetcherBrowserClientUrl", getResourceUrl(staticBasePath,
116-
"graphiql-subscriptions-fetcher-browser-client.js",
117-
joinJsDelivrPath("graphiql-subscriptions-fetcher", "0.0.2", "browser/client.js")));
118-
replacements.put("props", props);
119-
try {
120-
replacements.put("headers", new ObjectMapper().writeValueAsString(headerProperties));
121-
} catch (JsonProcessingException e) {
122-
log.error("Cannot serialize headers", e);
123-
}
124-
replacements.put("subscriptionClientTimeout", String.valueOf(graphiQLProperties.getSubscriptions().getTimeout() * 1000));
125-
replacements.put("subscriptionClientReconnect", String.valueOf(graphiQLProperties.getSubscriptions().isReconnect()));
126-
replacements.put("editorThemeCss", getEditorThemeCssURL());
127-
return replacements;
125+
replacements
126+
.put("subscriptionClientTimeout", String.valueOf(graphiQLProperties.getSubscriptions().getTimeout() * 1000));
127+
replacements
128+
.put("subscriptionClientReconnect", String.valueOf(graphiQLProperties.getSubscriptions().isReconnect()));
129+
replacements.put("editorThemeCss", getEditorThemeCssURL());
130+
return replacements;
131+
}
132+
133+
private String getEditorThemeCssURL() {
134+
String theme = graphiQLProperties.getProps().getVariables().getEditorTheme();
135+
if (theme != null) {
136+
return String.format(
137+
"https://cdnjs.cloudflare.com/ajax/libs/codemirror/%s/theme/%s.min.css",
138+
graphiQLProperties.getCodeMirror().getVersion(),
139+
theme.split("\\s")[0]
140+
);
128141
}
142+
return "";
143+
}
129144

130-
private String getEditorThemeCssURL() {
131-
String theme = graphiQLProperties.getProps().getVariables().getEditorTheme();
132-
if (theme != null) {
133-
return String.format(
134-
"https://cdnjs.cloudflare.com/ajax/libs/codemirror/%s/theme/%s.min.css",
135-
graphiQLProperties.getCodeMirror().getVersion(),
136-
theme.split("\\s")[0]
137-
);
138-
}
139-
return "";
145+
private String getResourceUrl(String staticBasePath, String staticFileName, String cdnUrl) {
146+
if (graphiQLProperties.getCdn().isEnabled() && StringUtils.isNotBlank(cdnUrl)) {
147+
return cdnUrl;
140148
}
149+
return joinStaticPath(staticBasePath, staticFileName);
150+
}
141151

142-
private String getResourceUrl(String staticBasePath, String staticFileName, String cdnUrl) {
143-
if (graphiQLProperties.getCdn().isEnabled() && StringUtils.isNotBlank(cdnUrl)) {
144-
return cdnUrl;
145-
}
146-
return joinStaticPath(staticBasePath, staticFileName);
147-
}
152+
private String joinStaticPath(String staticBasePath, String staticFileName) {
153+
return staticBasePath + "vendor/graphiql/" + staticFileName;
154+
}
148155

149-
private String joinStaticPath(String staticBasePath, String staticFileName) {
150-
return staticBasePath + "vendor/graphiql/" + staticFileName;
151-
}
156+
private String joinCdnjsPath(String library, String cdnVersion, String cdnFileName) {
157+
return CDNJS_CLOUDFLARE_COM_AJAX_LIBS + library + "/" + cdnVersion + "/" + cdnFileName;
158+
}
152159

153-
private String joinCdnjsPath(String library, String cdnVersion, String cdnFileName) {
154-
return CDNJS_CLOUDFLARE_COM_AJAX_LIBS + library + "/" + cdnVersion + "/" + cdnFileName;
155-
}
160+
private String joinJsDelivrPath(String library, String cdnVersion, String cdnFileName) {
161+
return CDN_JSDELIVR_NET_NPM + library + "@" + cdnVersion + "/" + cdnFileName;
162+
}
156163

157-
private String joinJsDelivrPath(String library, String cdnVersion, String cdnFileName) {
158-
return CDN_JSDELIVR_NET_NPM + library + "@" + cdnVersion + "/" + cdnFileName;
164+
private String constructGraphQlEndpoint(String contextPath, @RequestParam Map<String, String> params) {
165+
String endpoint = graphiQLProperties.getEndpoint().getGraphql();
166+
for (Map.Entry<String, String> param : params.entrySet()) {
167+
endpoint = endpoint.replaceAll("\\{" + param.getKey() + "}", param.getValue());
159168
}
160-
161-
private String constructGraphQlEndpoint(String contextPath, @RequestParam Map<String, String> params) {
162-
String endpoint = graphiQLProperties.getEndpoint().getGraphql();
163-
for (Map.Entry<String, String> param : params.entrySet()) {
164-
endpoint = endpoint.replaceAll("\\{" + param.getKey() + "}", param.getValue());
165-
}
166-
if (StringUtils.isNotBlank(contextPath) && !endpoint.startsWith(contextPath)) {
167-
return contextPath + endpoint;
168-
}
169-
return endpoint;
169+
if (StringUtils.isNotBlank(contextPath) && !endpoint.startsWith(contextPath)) {
170+
return contextPath + endpoint;
170171
}
172+
return endpoint;
173+
}
171174

172175
}

graphiql-spring-boot-autoconfigure/src/main/java/graphql/kickstart/graphiql/boot/GraphiQLProperties.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ static class Variables {
4848
@Data
4949
static class Cdn {
5050
private boolean enabled = false;
51-
private String version = "0.13.0";
51+
private String version = "1.0.6";
5252
}
5353

5454
@Data

0 commit comments

Comments
 (0)