88import java .io .ByteArrayInputStream ;
99import java .io .InputStream ;
1010import java .nio .charset .StandardCharsets ;
11- import java .util .Arrays ;
12- import java .util .EnumSet ;
13- import java .util .HashMap ;
14- import java .util .Map ;
15- import java .util .Optional ;
11+ import java .util .*;
1612
1713import edu .umd .cs .findbugs .annotations .NonNull ;
1814import edu .umd .cs .findbugs .annotations .Nullable ;
@@ -109,13 +105,23 @@ public enum Format {
109105 JSON ,
110106
111107 /** YAML. */
112- YAML
108+ YAML ;
109+
110+ public static Format from (@ NonNull String filePath ) {
111+ for (Format value : values ()) {
112+ if (filePath .endsWith ("." + value .name ().toLowerCase ())) {
113+ return value ;
114+ }
115+ }
116+ throw new IllegalArgumentException ("Unsupported format: " + filePath );
117+ }
113118 }
114119
115120 private final String openAPIPath ;
116121 private String swaggerUIPath = "/swagger" ;
117122 private String redocPath = "/redoc" ;
118123 private EnumSet <Format > format = EnumSet .of (Format .JSON , Format .YAML );
124+ private final Set <String > customFiles = new LinkedHashSet <>();
119125
120126 /**
121127 * Creates an OpenAPI module. The path is used to route the open API files. For example:
@@ -141,6 +147,17 @@ public OpenAPIModule() {
141147 this ("/" );
142148 }
143149
150+ /**
151+ * Set custom openapi file to use. Could be a file system or classpath resource.
152+ *
153+ * @param path Path.
154+ * @return This module.
155+ */
156+ public @ NonNull OpenAPIModule file (String path ) {
157+ customFiles .add (path );
158+ return this ;
159+ }
160+
144161 /**
145162 * Customize the swagger-ui path. Defaults is <code>/swagger</code>.
146163 *
@@ -178,22 +195,42 @@ public OpenAPIModule() {
178195
179196 @ Override
180197 public void install (@ NonNull Jooby application ) throws Exception {
181- String dir = Optional .ofNullable (application .getBasePackage ()).orElse ("/" ).replace ("." , "/" );
182-
183- String appName = application .getClass ().getSimpleName ()
184- .replace ("Jooby" , "openapi" )
185- .replace ("Kooby" , "openapi" );
186- for (Format ext : format ) {
187- String filename = String .format ("/%s.%s" , appName , ext .name ().toLowerCase ());
188- String openAPIFileLocation = Router .normalizePath (dir ) + filename ;
189- application .assets (
190- fullPath (openAPIPath , "/openapi." + ext .name ().toLowerCase ()), openAPIFileLocation );
191- }
198+ var filePaths = computeOpenAPIFiles (application );
199+
200+ /*
201+ Add /openapi.json and/or /openapi.yaml paths
202+ */
203+ filePaths .forEach ((path , source ) -> application .assets (fullPath (openAPIPath , path ), source ));
192204
193205 /** Configure UI: */
194206 configureUI (application );
195207 }
196208
209+ private Map <String , String > computeOpenAPIFiles (Jooby application ) {
210+ final Map <String , String > filePaths = new LinkedHashMap <>();
211+ if (customFiles .isEmpty ()) {
212+ // Generated by open api
213+ String dir = Optional .ofNullable (application .getBasePackage ()).orElse ("/" ).replace ("." , "/" );
214+ String appName =
215+ application
216+ .getClass ()
217+ .getSimpleName ()
218+ .replace ("Jooby" , "openapi" )
219+ .replace ("Kooby" , "openapi" );
220+ for (Format ext : format ) {
221+ String filename = String .format ("/%s.%s" , appName , ext .name ().toLowerCase ());
222+ String openAPIFileLocation = Router .normalizePath (dir ) + filename ;
223+ filePaths .put ("/openapi." + ext .name ().toLowerCase (), openAPIFileLocation );
224+ }
225+ } else {
226+ // Custom files
227+ for (String file : customFiles ) {
228+ filePaths .put ("/openapi." + Format .from (file ).name ().toLowerCase (), file );
229+ }
230+ }
231+ return filePaths ;
232+ }
233+
197234 private void configureUI (Jooby application ) {
198235 Map <String , Consumer2 <Jooby , AssetSource >> ui = new HashMap <>();
199236 ui .put ("swagger-ui" , this ::swaggerUI );
0 commit comments