|
5 | 5 | */ |
6 | 6 | package io.jooby; |
7 | 7 |
|
8 | | -import io.jooby.SneakyThrows.Consumer2; |
9 | | -import org.apache.commons.io.IOUtils; |
10 | | - |
11 | | -import javax.annotation.Nonnull; |
12 | | -import java.io.IOException; |
| 8 | +import java.io.ByteArrayInputStream; |
13 | 9 | import java.io.InputStream; |
14 | 10 | import java.nio.charset.StandardCharsets; |
15 | 11 | import java.util.Arrays; |
|
18 | 14 | import java.util.Map; |
19 | 15 | import java.util.Optional; |
20 | 16 |
|
| 17 | +import javax.annotation.Nonnull; |
| 18 | + |
| 19 | +import org.apache.commons.io.IOUtils; |
| 20 | +import org.jetbrains.annotations.NotNull; |
| 21 | +import org.jetbrains.annotations.Nullable; |
| 22 | + |
| 23 | +import io.jooby.SneakyThrows.Consumer2; |
| 24 | + |
21 | 25 | /** |
22 | 26 | * OpenAPI supports for Jooby. Basic Usage: |
23 | 27 | * |
|
40 | 44 | */ |
41 | 45 | public class OpenAPIModule implements Extension { |
42 | 46 |
|
| 47 | + private static class OpenAPIAsset implements Asset { |
| 48 | + |
| 49 | + private long lastModified; |
| 50 | + |
| 51 | + private byte[] content; |
| 52 | + |
| 53 | + private MediaType type; |
| 54 | + |
| 55 | + OpenAPIAsset(MediaType type, byte[] content, long lastModified) { |
| 56 | + this.content = content; |
| 57 | + this.type = type; |
| 58 | + this.lastModified = lastModified; |
| 59 | + } |
| 60 | + |
| 61 | + @Override public long getSize() { |
| 62 | + return content.length; |
| 63 | + } |
| 64 | + |
| 65 | + @Override public long getLastModified() { |
| 66 | + return lastModified; |
| 67 | + } |
| 68 | + |
| 69 | + @Override public boolean isDirectory() { |
| 70 | + return false; |
| 71 | + } |
| 72 | + |
| 73 | + @NotNull @Override public MediaType getContentType() { |
| 74 | + return type; |
| 75 | + } |
| 76 | + |
| 77 | + @Override public InputStream stream() { |
| 78 | + return new ByteArrayInputStream(content); |
| 79 | + } |
| 80 | + |
| 81 | + @Override public void close() throws Exception { |
| 82 | + // NOOP |
| 83 | + } |
| 84 | + } |
| 85 | + |
| 86 | + private static class OpenAPISource implements AssetSource { |
| 87 | + |
| 88 | + private Map<String, Asset> assets = new HashMap<>(); |
| 89 | + |
| 90 | + public OpenAPISource put(String key, Asset asset) { |
| 91 | + assets.put(key, asset); |
| 92 | + return this; |
| 93 | + } |
| 94 | + |
| 95 | + @Nullable @Override public Asset resolve(@NotNull String path) { |
| 96 | + return assets.get(path); |
| 97 | + } |
| 98 | + } |
| 99 | + |
43 | 100 | /** |
44 | 101 | * Available formats. |
45 | 102 | */ |
@@ -156,33 +213,41 @@ private void configureUI(Jooby application) { |
156 | 213 | } |
157 | 214 | } |
158 | 215 |
|
159 | | - private void redoc(Jooby application, AssetSource source) throws IOException { |
160 | | - application.assets(redocPath + "/*", source); |
| 216 | + private void redoc(Jooby application, AssetSource source) throws Exception { |
| 217 | + |
161 | 218 | String openAPIJSON = fullPath( |
162 | 219 | fullPath(application.getContextPath(), openAPIPath), "/openapi.json"); |
163 | 220 |
|
164 | | - String template = readString(source, "index.html") |
165 | | - .replace("${openAPIPath}", openAPIJSON) |
166 | | - .replace("${redocPath}", fullPath(application.getContextPath(), redocPath)); |
167 | | - application |
168 | | - .get(redocPath, ctx -> ctx.setResponseType(MediaType.html).send(template)); |
| 221 | + AssetSource customSource = new OpenAPISource() |
| 222 | + .put("index.html", |
| 223 | + processAsset(source, MediaType.html, "index.html", "${openAPIPath}", openAPIJSON, |
| 224 | + "${redocPath}", fullPath(application.getContextPath(), redocPath))); |
| 225 | + |
| 226 | + application.assets(redocPath + "/?*", customSource, source); |
169 | 227 | } |
170 | 228 |
|
171 | | - private void swaggerUI(Jooby application, AssetSource source) throws IOException { |
| 229 | + private void swaggerUI(Jooby application, AssetSource source) throws Exception { |
172 | 230 | String openAPIJSON = fullPath( |
173 | 231 | fullPath(application.getContextPath(), openAPIPath), "/openapi.json"); |
174 | 232 |
|
175 | | - String template = readString(source, "index.html") |
176 | | - .replace("${openAPIPath}", openAPIJSON) |
177 | | - .replace("${swaggerPath}", fullPath(application.getContextPath(), swaggerUIPath)); |
178 | | - |
179 | | - application.assets(swaggerUIPath + "/*", source); |
180 | | - application.get(swaggerUIPath, ctx -> ctx.setResponseType(MediaType.html).send(template)); |
| 233 | + AssetSource customSource = new OpenAPISource() |
| 234 | + .put("index.html", processAsset(source, MediaType.html, "index.html", "${swaggerPath}", |
| 235 | + fullPath(application.getContextPath(), swaggerUIPath))) |
| 236 | + .put("swagger-initializer.js", |
| 237 | + processAsset(source, MediaType.html, "swagger-initializer.js", "${openAPIPath}", |
| 238 | + openAPIJSON)); |
| 239 | + application.assets(swaggerUIPath + "/?*", customSource, source); |
181 | 240 | } |
182 | 241 |
|
183 | | - private static String readString(AssetSource source, String resource) throws IOException { |
184 | | - try (InputStream stream = source.resolve(resource).stream()) { |
185 | | - return IOUtils.toString(stream, StandardCharsets.UTF_8); |
| 242 | + private static Asset processAsset(AssetSource source, MediaType type, String resource, |
| 243 | + String... replacements) throws Exception { |
| 244 | + try (Asset asset = source.resolve(resource)) { |
| 245 | + String content = IOUtils.toString(asset.stream(), StandardCharsets.UTF_8); |
| 246 | + for (int i = 0; i < replacements.length; i += 2) { |
| 247 | + content = content.replace(replacements[i], replacements[i + 1]); |
| 248 | + } |
| 249 | + return new OpenAPIAsset(type, content.getBytes(StandardCharsets.UTF_8), |
| 250 | + asset.getLastModified()); |
186 | 251 | } |
187 | 252 | } |
188 | 253 |
|
|
0 commit comments