11package org .offscale ;
22
3- import com .github .javaparser .ast .body .ClassOrInterfaceDeclaration ;
4- import com .github .javaparser .ast .body .FieldDeclaration ;
5- import com .github .javaparser .ast .body .MethodDeclaration ;
3+ import com .github .javafaker .Faker ;
4+ import com .github .javaparser .ast .Modifier ;
5+ import com .github .javaparser .ast .body .*;
6+ import com .github .javaparser .ast .expr .MethodCallExpr ;
67import com .github .javaparser .ast .expr .NormalAnnotationExpr ;
7- import com .google . common . annotations . VisibleForTesting ;
8+ import com .github . javaparser . ast . stmt . BlockStmt ;
89import com .google .common .collect .ImmutableMap ;
910import com .google .common .collect .Lists ;
1011import org .json .JSONArray ;
1112import org .json .JSONObject ;
12- import org .yaml .snakeyaml .Yaml ;
13- import java .io .InputStream ;
13+
1414import java .util .ArrayList ;
1515import java .util .HashMap ;
1616import java .util .List ;
17- import java .util .Map ;
17+ import java .util .Optional ;
1818import java .util .regex .Matcher ;
1919import java .util .regex .Pattern ;
2020
2424 */
2525public class Create {
2626 private final JSONObject jo ;
27+ Faker faker = new Faker ();
2728 private static final ImmutableMap <String , String > OPEN_API_TO_JAVA = Utils .getOpenAPIToJavaTypes ();
28-
29- private class Parameter {
29+ private static final String GET_METHOD_METHOD_NAME = "run" ;
30+ private class Schema {
3031 private String type ;
3132 private String name ;
32-
3333 private String description ;
34-
3534 private String strictType ;
3635
37- public Parameter ( String type ) {
36+ public Schema ( final String type ) {
3837 this .type = type ;
3938 }
4039
41- public Parameter (String type , String strictType ) {
40+ public Schema (String type , String strictType ) {
4241 this .type = type ;
4342 this .strictType = strictType ;
4443 }
4544
46- public void setName (String name ) {
45+ public Schema setName (String name ) {
4746 this .name = name ;
47+ return this ;
4848 }
4949
5050 public void setType (String type ) {
@@ -68,7 +68,7 @@ public ImmutableMap<String, String> generateComponents() {
6868 HashMap <String , String > generatedComponents = new HashMap <>();
6969 JSONObject joSchemas = jo .getJSONObject ("components" ).getJSONObject ("schemas" );
7070 List <String > schemas = Lists .newArrayList (joSchemas .keys ());
71- schemas .forEach ((schema ) -> generatedComponents .put (schema , generateComponent (joSchemas .getJSONObject (schema ), schema )));
71+ schemas .forEach ((schema ) -> generatedComponents .put (schema , generateComponent (joSchemas .getJSONObject (schema ), schema ). toString () ));
7272 return ImmutableMap .copyOf (generatedComponents );
7373 }
7474
@@ -78,35 +78,116 @@ public ImmutableMap<String, String> generateComponents() {
7878 * @param componentName name of the component to generate.
7979 * @return a String containing the generated code for a component.
8080 */
81- private String generateComponent (JSONObject joComponent , String componentName ) {
81+ private ClassOrInterfaceDeclaration generateComponent (JSONObject joComponent , String componentName ) {
8282 JSONObject joProperties = joComponent .getJSONObject ("properties" );
8383 List <String > properties = Lists .newArrayList (joProperties .keys ());
8484 ClassOrInterfaceDeclaration myComponent = new ClassOrInterfaceDeclaration ();
8585
8686 myComponent .setName (componentName );
8787 properties .forEach ((property ) -> {
88- Parameter parameter = parseSchema (joProperties .getJSONObject (property ));
89- FieldDeclaration field = myComponent .addField (parameter .type , property );
90- field .setJavadocComment ("Type of " + parameter .strictType );
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 );
95+ }
9196 });
92- return myComponent . toString () ;
97+ return myComponent ;
9398 }
9499
95100 /**
96101 * @return Routes interface containing routes from OpenAPI Spec
97102 */
98- public String generateRoutes () {
103+ public ImmutableMap <String , String > generateRoutesAndTests () {
104+ HashMap <String , String > routesAndTests = new HashMap <>();
99105 JSONObject joPaths = jo .getJSONObject ("paths" );
100106 List <String > paths = Lists .newArrayList (joPaths .keys ());
101- ClassOrInterfaceDeclaration myInterface = new ClassOrInterfaceDeclaration ();
102- myInterface .setInterface (true );
103- myInterface .setName ("Routes" );
107+ ClassOrInterfaceDeclaration routesInterface = new ClassOrInterfaceDeclaration ()
108+ .setInterface (true ).setName ("Routes" );
109+ ClassOrInterfaceDeclaration testsClass = new ClassOrInterfaceDeclaration ().setName ("Tests" );
110+ testsClass .addField ("OkHttpClient" , "client" ,Modifier .Keyword .PRIVATE , Modifier .Keyword .FINAL ).getVariable (0 ).setInitializer ("new OkHttpClient()" );
111+ MethodDeclaration runMethod = testsClass .addMethod (GET_METHOD_METHOD_NAME );
112+ MethodDeclaration runMethodWithBody = Utils .generateGetRequestMethod ();
113+ runMethod .setParameters (runMethodWithBody .getParameters ());
114+ runMethod .setType (runMethodWithBody .getType ());
115+ runMethod .setBody (runMethodWithBody .getBody ().get ());
116+
104117 for (String path : paths ) {
105118 for (String operation : Lists .newArrayList (joPaths .getJSONObject (path ).keys ())) {
106- generateRoute (myInterface , joPaths .getJSONObject (path ).getJSONObject (operation ), path , operation );
119+ generateRoute (routesInterface , joPaths .getJSONObject (path ).getJSONObject (operation ), path , operation );
120+ generateTest (testsClass , joPaths .getJSONObject (path ).getJSONObject (operation ));
107121 }
108122 }
109- return myInterface .toString ();
123+ routesAndTests .put ("routes" , routesInterface .toString ());
124+ routesAndTests .put ("tests" , testsClass .toString ());
125+ return ImmutableMap .copyOf (routesAndTests );
126+ }
127+
128+ private String getBaseURL () {
129+ return this .jo .getJSONArray ("servers" ).getJSONObject (0 ).getString ("url" );
130+ }
131+
132+ private void generateTest (ClassOrInterfaceDeclaration routesInterface , JSONObject joRoute ) {
133+ String classType = generateRouteType (joRoute .getJSONObject ("responses" )).type ;
134+ MethodDeclaration methodDeclaration = routesInterface .addMethod (joRoute .getString ("operationId" ) + "Test" );
135+ methodDeclaration .addAnnotation ("Test" );
136+ StringBuilder getURL = new StringBuilder ("\" " + getBaseURL () + "?" );
137+ if (joRoute .has ("parameters" )) {
138+ generateRouteParameters (joRoute .getJSONArray ("parameters" ))
139+ .forEach (parameter -> getURL .append (generateMockDataForType (parameter )));
140+ }
141+ getURL .append ("\" " );
142+ BlockStmt methodBody = new BlockStmt ();
143+ MethodCallExpr runCall = new MethodCallExpr ();
144+ runCall .setName (GET_METHOD_METHOD_NAME );
145+ runCall .addArgument (getURL .toString ());
146+ FieldDeclaration getResponse = new FieldDeclaration ();
147+ if (!classType .equals ("void" )) {
148+ FieldDeclaration gson = new FieldDeclaration ();
149+ FieldDeclaration parsedResponse = new FieldDeclaration ();
150+
151+ Utils .initializeField (getResponse , "String" , "getResponse" , runCall .toString () + ".body().string()" );
152+ Utils .initializeField (gson , "Gson" , "gson" , "new GsonBuilder().create()" );
153+ Utils .initializeField (parsedResponse , Utils .getPrimitivesToClassTypes (classType ),
154+ "response" , "gson.fromJson(getResponse, " + classType + ".class)" );
155+ Utils .addDeclarationsToBlock (methodBody , getResponse , gson , parsedResponse );
156+ } else {
157+ Utils .initializeField (getResponse , "int" , "statusCode" , runCall .toString () + ".code()" );
158+ MethodCallExpr assertEqualsCall = new MethodCallExpr ();
159+ assertEqualsCall .setName ("assertEquals" );
160+ assertEqualsCall .addArgument ("statusCode" );
161+ assertEqualsCall .addArgument ("200" );
162+ Utils .addDeclarationsToBlock (methodBody , getResponse );
163+ methodBody .addStatement (assertEqualsCall );
164+ }
165+ methodDeclaration .setBody (methodBody );
166+ }
167+
168+ private String generateMockDataForUnrecognizedName (String type ) {
169+ if (type .equals ("String" )) {
170+ return faker .food ().fruit ();
171+ } else if (type .equals ("long" ) || type .equals ("int" )) {
172+ return String .valueOf (faker .number ().numberBetween (1 , 100 ));
173+ } else if (type .equals ("boolean" )) {
174+ return "true" ;
175+ } else {
176+ return "UNKNOWN" ;
177+ }
178+ }
179+
180+ private String generateMockDataForType (Schema schema ) {
181+ String parameter = schema .name + "=" ;
182+ switch (schema .name ) {
183+ case "name" : return parameter + faker .name ().name ();
184+ case "fullname" : return parameter + faker .name ().fullName ();
185+ case "firstname" : return parameter + faker .name ().firstName ();
186+ case "lastname" : return parameter + faker .name ().lastName ();
187+ case "address" : return parameter + faker .address ().fullAddress ();
188+ default : return parameter +
189+ generateMockDataForUnrecognizedName (schema .type );
190+ }
110191 }
111192
112193 /**
@@ -117,12 +198,17 @@ public String generateRoutes() {
117198 * @param operation
118199 */
119200 private MethodDeclaration generateRoute (ClassOrInterfaceDeclaration routesInterface , JSONObject joRoute , String routeName , String operation ) {
120- MethodDeclaration methodDeclaration = routesInterface .addMethod (joRoute .getString ("operationId" ));
121- methodDeclaration .setJavadocComment (generateJavadocForRoute (joRoute , routeName , operation ));
201+ MethodDeclaration methodDeclaration = routesInterface .addMethod (joRoute .getString ("operationId" ))
202+ .removeBody ()
203+ .setJavadocComment (generateJavadocForRoute (joRoute , routeName , operation ));
122204 NormalAnnotationExpr expr = methodDeclaration .addAndGetAnnotation (operation .toUpperCase ());
123205 expr .addPair ("path" , "\" " + routeName + "\" " );
124- methodDeclaration .removeBody ();
125- methodDeclaration .setType (generateRouteType (joRoute .getJSONObject ("responses" )));
206+ String routeType = generateRouteType (joRoute .getJSONObject ("responses" )).type ;
207+ if (routeType .equals ("void" )) {
208+ methodDeclaration .setType ("void" );
209+ } else {
210+ methodDeclaration .setType ("Call<" + routeType + ">" );
211+ }
126212 if (joRoute .has ("parameters" )) {
127213 generateRouteParameters (joRoute .getJSONArray ("parameters" )).forEach (param -> methodDeclaration .addParameter (param .type , param .name ));
128214 }
@@ -134,11 +220,11 @@ private MethodDeclaration generateRoute(ClassOrInterfaceDeclaration routesInterf
134220 * @param joRouteParameters the openAPI representation of the route parameters
135221 * @return a List of route parameters
136222 */
137- private List <Parameter > generateRouteParameters (JSONArray joRouteParameters ) {
138- List <Parameter > routeParameters = new ArrayList <>();
223+ private List <Schema > generateRouteParameters (JSONArray joRouteParameters ) {
224+ List <Schema > routeParameters = new ArrayList <>();
139225 for (int i = 0 ; i < joRouteParameters .length (); i ++) {
140226 JSONObject joParameter = joRouteParameters .getJSONObject (i );
141- Parameter parameter = parseSchema (joParameter .getJSONObject ("schema" ));
227+ Schema parameter = parseSchema (joParameter .getJSONObject ("schema" ));
142228 parameter .setName (joParameter .getString ("name" ));
143229 parameter .setDescription (joParameter .getString ("description" ));
144230 routeParameters .add (parameter );
@@ -151,42 +237,33 @@ private List<Parameter> generateRouteParameters(JSONArray joRouteParameters) {
151237 * @param joRouteResponse the OpenAPI representation of the route response.
152238 * @return the type of the route
153239 */
154- private String generateRouteType (JSONObject joRouteResponse ) {
155- StringBuilder type = new StringBuilder ();
240+ private Schema generateRouteType (JSONObject joRouteResponse ) {
156241 List <String > responses = Lists .newArrayList (joRouteResponse .keys ());
157- responses .forEach (response -> {
158- if (!response .equals ("default" )) {
159- if (joRouteResponse .getJSONObject (response ).has ("content" )) {
160- Parameter parameter = parseSchema (joRouteResponse .getJSONObject (response ).getJSONObject ("content" ).getJSONObject ("application/json" ).getJSONObject ("schema" ));
161- parameter .setType ("Call<" + parameter .type + ">" );
162- type .append (parameter .type );
163- }
164- }
165- });
166- if (type .isEmpty ()) {
167- return "void" ;
168- } else {
169- return type .toString ();
242+ Optional <String > response = responses .stream ().filter (r -> r .equals ("200" )).findFirst ();
243+ if (!response .isEmpty () && joRouteResponse .getJSONObject (response .get ()).has ("content" )) {
244+ return parseSchema (joRouteResponse .getJSONObject (response .get ()).getJSONObject ("content" ).getJSONObject ("application/json" ).getJSONObject ("schema" ));
170245 }
246+ return new Schema ("void" );
171247 }
172248
173249 /**
174250 *
175- * @param schema which is essentially a type
251+ * @param joSchema which is essentially a type
176252 * @return a Parameter with type information
177253 */
178- private Parameter parseSchema (JSONObject schema ) {
179- if (schema .has ("$ref" )) {
180- return new Parameter (parseSchemaRef (schema .getString ("$ref" )));
254+ private Schema parseSchema (JSONObject joSchema ) {
255+ if (joSchema .has ("$ref" )) {
256+ return new Schema (parseSchemaRef (joSchema .getString ("$ref" )));
181257 }
182258
183- if (schema .has ("format" )) {
184- return new Parameter (Utils .getOpenAPIToJavaTypes ().get (schema .get ("format" )), schema .getString ("format" ));
259+ if (joSchema .has ("format" )) {
260+ return new Schema (Utils .getOpenAPIToJavaTypes ().get (joSchema .get ("format" )), joSchema .getString ("format" ));
185261 }
186262
187- return new Parameter (Utils .getOpenAPIToJavaTypes ().get (schema .get ("type" )), schema .getString ("type" ));
263+ return new Schema (Utils .getOpenAPIToJavaTypes ().get (joSchema .get ("type" )), joSchema .getString ("type" ));
188264 }
189265
266+
190267 /**
191268 * Uses regex to parse out the component name in the reference.
192269 * @param ref of a schema, maps to a component
0 commit comments