@@ -33,8 +33,14 @@ private class Schema {
3333 private String description ;
3434 private String strictType ;
3535
36+ public Schema () {
37+ this .type = "object" ;
38+ this .strictType = "Object" ;
39+ }
40+
3641 public Schema (final String type ) {
3742 this .type = type ;
43+ this .strictType = type ;
3844 }
3945
4046 public Schema (String type , String strictType ) {
@@ -68,7 +74,7 @@ public ImmutableMap<String, String> generateComponents() {
6874 HashMap <String , String > generatedComponents = new HashMap <>();
6975 JSONObject joSchemas = jo .getJSONObject ("components" ).getJSONObject ("schemas" );
7076 List <String > schemas = Lists .newArrayList (joSchemas .keys ());
71- schemas .forEach ((schema ) -> generatedComponents .put (schema , generateComponent (joSchemas .getJSONObject (schema ), schema ). toString ( )));
77+ schemas .forEach ((schema ) -> generatedComponents .put (schema , generateComponent2 (joSchemas .getJSONObject (schema ), schema , null ). get ( "type" )));
7278 return ImmutableMap .copyOf (generatedComponents );
7379 }
7480
@@ -79,26 +85,67 @@ public ImmutableMap<String, String> generateComponents() {
7985 * @return a String containing the generated code for a component.
8086 */
8187 private ClassOrInterfaceDeclaration generateComponent (JSONObject joComponent , String componentName ) {
82- JSONObject joProperties = joComponent .getJSONObject ("properties" );
83- List <String > properties = Lists .newArrayList (joProperties .keys ());
84- ClassOrInterfaceDeclaration myComponent = new ClassOrInterfaceDeclaration ();
85-
86- myComponent .setName (componentName );
87- properties .forEach ((property ) -> {
88- Schema parameter = parseSchema (joProperties .getJSONObject (property ));
89- if (parameter .type .equals ("Object" )) {
90- myComponent .addMember (generateComponent (joProperties .getJSONObject (property ), Utils .capitalizeFirstLetter (property )));
91- FieldDeclaration field = myComponent .addField (Utils .capitalizeFirstLetter (property ), property );
92- } else {
93- FieldDeclaration field = myComponent .addField (parameter .type , property );
94- field .setJavadocComment ("Type of " + parameter .strictType );
88+ ClassOrInterfaceDeclaration myComponent = new ClassOrInterfaceDeclaration ();
89+ myComponent .setName (componentName );
90+ if (joComponent .has ("properties" )) {
91+ JSONObject joProperties = joComponent .getJSONObject ("properties" );
92+ List <String > properties = Lists .newArrayList (joProperties .keys ());
93+ properties .forEach ((property ) -> {
94+ Schema parameter = parseSchema (joProperties .getJSONObject (property ));
95+ if (parameter .type .equals ("Object" )) {
96+ myComponent .addMember (generateComponent (joProperties .getJSONObject (property ), Utils .capitalizeFirstLetter (property )));
97+ FieldDeclaration field = myComponent .addField (Utils .capitalizeFirstLetter (property ), property );
98+ field .setJavadocComment ("Type of " + parameter .strictType );
99+ } else {
100+ FieldDeclaration field = myComponent .addField (parameter .type , property );
101+ field .setJavadocComment ("Type of " + parameter .strictType );
102+ }
103+ });
104+ // If there is no properties field, this should an array.
105+ } else {
106+ JSONObject joItems = joComponent .getJSONObject ("items" );
107+ Schema parameter = parseSchema (joItems );
108+ }
109+ return myComponent ;
110+ }
111+
112+ private ImmutableMap <String , String > generateComponent2 (JSONObject joComponent , String componentName , ClassOrInterfaceDeclaration parentClass ) {
113+ Schema type = parseSchema (joComponent );
114+ if (type .type .equalsIgnoreCase ("object" )) {
115+ ClassOrInterfaceDeclaration newClass = new ClassOrInterfaceDeclaration ();
116+ JSONObject joProperties = joComponent .getJSONObject ("properties" );
117+ List <String > properties = Lists .newArrayList (joProperties .keys ());
118+ newClass .setName (Utils .capitalizeFirstLetter (componentName ));
119+ properties .forEach (property -> {
120+ ImmutableMap <String , String > propertyType = generateComponent2 (joProperties .getJSONObject (property ), property , newClass );
121+ FieldDeclaration field = newClass .addField (propertyType .get ("type" ), property );
122+ field .setJavadocComment ("Type of " + propertyType .get ("strictType" ));
123+ });
124+ if (parentClass == null ) {
125+ return ImmutableMap .of ("type" , newClass .toString ());
126+ }
127+
128+ parentClass .addMember (newClass );
129+ return ImmutableMap .of ("type" , newClass .getNameAsString (), "strictType" , newClass .getNameAsString ());
130+ } else if (type .type .equals ("array" )) {
131+ if (parentClass == null ) {
132+ ClassOrInterfaceDeclaration newClass = new ClassOrInterfaceDeclaration ();
133+ String arrayType = generateComponent2 (joComponent .getJSONObject ("items" ), "ArrayType" , newClass ).get ("type" );
134+ newClass .setName (Utils .capitalizeFirstLetter (componentName ));
135+ newClass .addField (arrayType + "[]" , componentName + "Array" );
136+ return ImmutableMap .of ("type" , newClass .toString ());
95137 }
96- });
97- return myComponent ;
138+ String arrayType = generateComponent2 (joComponent .getJSONObject ("items" ), "ArrayType" , parentClass ).get ("type" );
139+ return ImmutableMap .of ("type" , arrayType + "[]" , "strictType" , arrayType + "[]" );
140+ } else {
141+ return ImmutableMap .of ("type" , parseSchema (joComponent ).type , "strictType" , parseSchema (joComponent ).strictType );
142+ }
98143 }
99144
100145 /**
101- * @return Routes interface containing routes from OpenAPI Spec
146+ * @return Map with two key-value pairs: routes and tests.
147+ * routes is the code for the generated routes interface.
148+ * tests is the code for generated tests for all routes.
102149 */
103150 public ImmutableMap <String , String > generateRoutesAndTests () {
104151 HashMap <String , String > routesAndTests = new HashMap <>();
@@ -125,10 +172,18 @@ public ImmutableMap<String, String> generateRoutesAndTests() {
125172 return ImmutableMap .copyOf (routesAndTests );
126173 }
127174
175+ /**
176+ * @return the base url for all http requests
177+ */
128178 private String getBaseURL () {
129179 return this .jo .getJSONArray ("servers" ).getJSONObject (0 ).getString ("url" );
130180 }
131181
182+ /**
183+ * Given a route, generates a test corresponding the route.
184+ * @param routesInterface
185+ * @param joRoute
186+ */
132187 private void generateTest (ClassOrInterfaceDeclaration routesInterface , JSONObject joRoute ) {
133188 String classType = generateRouteType (joRoute .getJSONObject ("responses" )).type ;
134189 MethodDeclaration methodDeclaration = routesInterface .addMethod (joRoute .getString ("operationId" ) + "Test" );
@@ -165,6 +220,13 @@ private void generateTest(ClassOrInterfaceDeclaration routesInterface, JSONObjec
165220 methodDeclaration .setBody (methodBody );
166221 }
167222
223+ /**
224+ * Some parameter names are common such as firstname and can be
225+ * generated more precisely. This method handles parameters that
226+ * aren't common.
227+ * @param type of the parameter to generate mock data for
228+ * @return the mock data for the given parameter
229+ */
168230 private String generateMockDataForUnrecognizedName (String type ) {
169231 if (type .equals ("String" )) {
170232 return faker .food ().fruit ();
@@ -177,6 +239,10 @@ private String generateMockDataForUnrecognizedName(String type) {
177239 }
178240 }
179241
242+ /**
243+ * @param schema for which to generate the mock data
244+ * @return the mock data for the given schema.
245+ */
180246 private String generateMockDataForType (Schema schema ) {
181247 String parameter = schema .name + "=" ;
182248 switch (schema .name ) {
@@ -191,11 +257,11 @@ private String generateMockDataForType(Schema schema) {
191257 }
192258
193259 /**
194- *
260+ * generates the route code for a given route in the JSONObject.
195261 * @param routesInterface
196262 * @param joRoute
197263 * @param routeName
198- * @param operation
264+ * @param operation such as GET or POST
199265 */
200266 private MethodDeclaration generateRoute (ClassOrInterfaceDeclaration routesInterface , JSONObject joRoute , String routeName , String operation ) {
201267 MethodDeclaration methodDeclaration = routesInterface .addMethod (joRoute .getString ("operationId" ))
@@ -260,6 +326,10 @@ private Schema parseSchema(JSONObject joSchema) {
260326 return new Schema (Utils .getOpenAPIToJavaTypes ().get (joSchema .get ("format" )), joSchema .getString ("format" ));
261327 }
262328
329+ if (!joSchema .has ("type" )) {
330+ return new Schema ();
331+ }
332+
263333 return new Schema (Utils .getOpenAPIToJavaTypes ().get (joSchema .get ("type" )), joSchema .getString ("type" ));
264334 }
265335
@@ -298,6 +368,11 @@ private String generateJavadocForRoute(JSONObject joRoute, String routeName, Str
298368 return javaDocForRoute .toString ();
299369 }
300370
371+ /**
372+ * @param joResponse
373+ * @param responseName
374+ * @return the javadoc return statement
375+ */
301376 private String generateJavadocReturn (JSONObject joResponse , String responseName ) {
302377 return joResponse .getString ("description" ) + " (Status Code " + responseName + "), " ;
303378 }
0 commit comments