44import io .swagger .v3 .oas .models .media .Schema ;
55import io .swagger .v3 .oas .models .security .SecurityScheme ;
66import io .swagger .v3 .oas .models .servers .Server ;
7+ import lombok .Getter ;
78import org .apache .commons .lang3 .StringUtils ;
89import org .openapitools .codegen .*;
910import org .openapitools .codegen .meta .GeneratorMetadata ;
@@ -32,23 +33,20 @@ public class ScalaSttp4JsoniterClientCodegen extends AbstractScalaCodegen implem
3233 "F[Either[ResponseError[ErrorType], ReturnType]]] or to flatten " +
3334 "response's error raising them through enclosing monad (F[ReturnType])." ,
3435 true );
35- private static final StringProperty JODA_TIME_VERSION = new StringProperty ("jodaTimeVersion" , "The version of " +
36- "joda-time library" , "2.10.13" );
3736 private static final StringProperty JSONITER_VERSION = new StringProperty ("jsoniterVersion" ,
3837 "The version of jsoniter-scala " +
3938 "library" ,
4039 "2.31.1" );
4140
42- private static final JsonLibraryProperty JSON_LIBRARY_PROPERTY = new JsonLibraryProperty ();
43-
4441 public static final String DEFAULT_PACKAGE_NAME = "org.openapitools.client" ;
4542 private static final PackageProperty PACKAGE_PROPERTY = new PackageProperty ();
4643
4744 private static final List <Property <?>> properties = Arrays .asList (
48- STTP_CLIENT_VERSION , USE_SEPARATE_ERROR_CHANNEL , JODA_TIME_VERSION ,
49- JSONITER_VERSION , JSON_LIBRARY_PROPERTY , PACKAGE_PROPERTY );
45+ STTP_CLIENT_VERSION , USE_SEPARATE_ERROR_CHANNEL , JSONITER_VERSION , PACKAGE_PROPERTY );
46+
47+ private static final Set <String > NO_JSON_CODEC_TYPES = new HashSet <>(Arrays .asList ("UUID" , "URI" , "URL" , "File" , "Path" ));
5048
51- private final Logger LOGGER = LoggerFactory .getLogger (ScalaSttp4ClientCodegen .class );
49+ private final Logger LOGGER = LoggerFactory .getLogger (ScalaSttp4JsoniterClientCodegen .class );
5250
5351 protected String groupId = "org.openapitools" ;
5452 protected String artifactId = "openapi-client" ;
@@ -57,6 +55,8 @@ public class ScalaSttp4JsoniterClientCodegen extends AbstractScalaCodegen implem
5755 protected boolean renderJavadoc = true ;
5856 protected boolean removeOAuthSecurities = true ;
5957
58+ protected Map <String , String > jsonCodecNeedingTypes = new HashMap <>();
59+
6060 Map <String , ModelsMap > enumRefs = new HashMap <>();
6161
6262 public ScalaSttp4JsoniterClientCodegen () {
@@ -90,19 +90,20 @@ public ScalaSttp4JsoniterClientCodegen() {
9090 apiTemplateFiles .put ("api.mustache" , ".scala" );
9191 embeddedTemplateDir = templateDir = "scala-sttp4-jsoniter" ;
9292
93- String jsonLibrary = JSON_LIBRARY_PROPERTY .getValue (additionalProperties );
94-
9593 String jsonValueClass = "io.circe.Json" ;
9694
9795 additionalProperties .put (CodegenConstants .GROUP_ID , groupId );
9896 additionalProperties .put (CodegenConstants .ARTIFACT_ID , artifactId );
9997 additionalProperties .put (CodegenConstants .ARTIFACT_VERSION , artifactVersion );
98+ additionalProperties .put ("jsonCodecNeedingTypes" , jsonCodecNeedingTypes .entrySet ());
10099 if (renderJavadoc ) {
101100 additionalProperties .put ("javadocRenderer" , new JavadocLambda ());
102101 }
103102 additionalProperties .put ("fnCapitalize" , new CapitalizeLambda ());
104103 additionalProperties .put ("fnCamelize" , new CamelizeLambda (false ));
105104 additionalProperties .put ("fnEnumEntry" , new EnumEntryLambda ());
105+ additionalProperties .put ("fnCodecName" , new CodecNameLambda ());
106+ additionalProperties .put ("fnHandleDownload" , new HandleDownloadLambda ());
106107
107108 // importMapping.remove("Seq");
108109 // importMapping.remove("List");
@@ -156,8 +157,6 @@ public void processOpts() {
156157 supportingFiles .add (new SupportingFile ("additionalTypeSerializers.mustache" , invokerFolder ,
157158 "AdditionalTypeSerializers.scala" ));
158159 supportingFiles .add (new SupportingFile ("project/build.properties.mustache" , "project" , "build.properties" ));
159- // supportingFiles.add(new SupportingFile("dateSerializers.mustache",
160- // invokerFolder, "DateSerializers.scala"));
161160 }
162161
163162 @ Override
@@ -173,7 +172,6 @@ public String getHelp() {
173172 @ Override
174173 public String encodePath (String input ) {
175174 String path = super .encodePath (input );
176-
177175 // The parameter names in the URI must be converted to the same case as
178176 // the method parameter.
179177 StringBuffer buf = new StringBuffer (path .length ());
@@ -185,13 +183,55 @@ public String encodePath(String input) {
185183 return buf .toString ();
186184 }
187185
186+ private PathMetadata encPath (String input ) {
187+ String path = super .encodePath (input );
188+ ArrayList <String > pathParams = new ArrayList <>();
189+
190+ // The parameter names in the URI must be converted to the same case as
191+ // the method parameter.
192+ StringBuffer buf = new StringBuffer (path .length ());
193+ Matcher matcher = Pattern .compile ("[{](.*?)[}]" ).matcher (path );
194+ while (matcher .find ()) {
195+ matcher .appendReplacement (buf , "\\ ${" + toParamName (matcher .group (0 )) + "}" );
196+ pathParams .add (matcher .group (0 ));
197+ }
198+ matcher .appendTail (buf );
199+ return new PathMetadata (buf .toString (), pathParams );
200+ }
201+
188202 @ Override
189203 public CodegenOperation fromOperation (String path ,
190204 String httpMethod ,
191205 Operation operation ,
192206 List <Server > servers ) {
193207 CodegenOperation op = super .fromOperation (path , httpMethod , operation , servers );
194- op .path = encodePath (path );
208+
209+ PathMetadata pathMetadata = encPath (path );
210+
211+ op .path = pathMetadata .getPath ();
212+
213+ for (String pathParam : pathMetadata .getPathParams ()) {
214+ CodegenParameter param = new CodegenParameter ();
215+ param .isPathParam = true ;
216+ param .baseName = pathParam ;
217+ param .paramName = toParamName (pathParam );
218+ param .dataType = "String" ;
219+ param .required = true ;
220+
221+ boolean alreadyExists = false ;
222+ for (CodegenParameter existingParam : op .pathParams ) {
223+ if (existingParam .baseName .equals (param .baseName ) || existingParam .paramName .equals (param .paramName )) {
224+ alreadyExists = true ;
225+ break ;
226+ }
227+ }
228+
229+ if (!alreadyExists ) {
230+ op .pathParams .add (param );
231+ op .allParams .add (param );
232+ }
233+ }
234+
195235 return op ;
196236 }
197237
@@ -310,6 +350,30 @@ private boolean isEnumClass(final String importPath, final Map<String, ModelsMap
310350
311351 @ Override
312352 public OperationsMap postProcessOperationsWithModels (OperationsMap objs , List <ModelMap > allModels ) {
353+ OperationMap ops = objs .getOperations ();
354+
355+ // allModels.forEach(model -> {
356+ // if (model.getModel().name.equals("PluginStatus")) {
357+ // System.out.println("Found plugin status model");
358+ // System.out.println(model.getModel());
359+ // }
360+ // });
361+
362+ for (CodegenOperation operation : ops .getOperation ()) {
363+ if (operation .returnType != null && !NO_JSON_CODEC_TYPES .contains (operation .returnType )) {
364+ String identifier = formatIdentifier (operation .returnType , false ) + "Codec" ;
365+ String type = operation .returnType ;
366+ jsonCodecNeedingTypes .put (identifier , type );
367+ }
368+
369+ if (operation .bodyParam != null && !NO_JSON_CODEC_TYPES .contains (operation .bodyParam .dataType )) {
370+ String identifier = formatIdentifier (operation .bodyParam .dataType , false ) + "Codec" ;
371+ String type = operation .bodyParam .dataType ;
372+
373+ jsonCodecNeedingTypes .put (identifier , type );
374+ }
375+ }
376+
313377 if (registerNonStandardStatusCodes ) {
314378 try {
315379 OperationMap opsMap = objs .getOperations ();
@@ -385,7 +449,14 @@ public String toParamName(String name) {
385449
386450 @ Override
387451 public String toEnumName (CodegenProperty property ) {
388- return formatIdentifier (property .baseName , true );
452+ String identifier = formatIdentifier (property .baseName , true );
453+
454+ // remove backticks because there are no capitalized reserved words in Scala
455+ if (identifier .startsWith ("`" ) && identifier .endsWith ("`" )) {
456+ return identifier .substring (1 , identifier .length () - 1 );
457+ } else {
458+ return identifier ;
459+ }
389460 }
390461
391462 @ Override
@@ -511,29 +582,6 @@ public Boolean getValue(Map<String, Object> additionalProperties) {
511582 }
512583 }
513584
514- public static class JsonLibraryProperty extends StringProperty {
515- private static final String JSON4S = "json4s" ;
516- private static final String CIRCE = "circe" ;
517-
518- public JsonLibraryProperty () {
519- super ("jsonLibrary" , "Json library to use. Possible values are: json4s and circe." , JSON4S );
520- }
521-
522- @ Override
523- public void updateAdditionalProperties (Map <String , Object > additionalProperties ) {
524- String value = getValue (additionalProperties );
525- if (CIRCE .equals (value ) || JSON4S .equals (value )) {
526- additionalProperties .put (CIRCE , CIRCE .equals (value ));
527- additionalProperties .put (JSON4S , JSON4S .equals (value ));
528- } else {
529- IllegalArgumentException exception = new IllegalArgumentException (
530- "Invalid json library: " + value + ". Must be " + CIRCE + " " +
531- "or " + JSON4S );
532- throw exception ;
533- }
534- }
535- }
536-
537585 public static class PackageProperty extends StringProperty {
538586
539587 public PackageProperty () {
@@ -598,8 +646,41 @@ public String formatFragment(String fragment) {
598646 private class EnumEntryLambda extends CustomLambda {
599647 @ Override
600648 public String formatFragment (String fragment ) {
649+ if (fragment .isBlank ()) {
650+ return "NotPresent" ;
651+ }
601652 return formatIdentifier (fragment , true );
602653 }
603654 }
604655
656+ private class CodecNameLambda extends CustomLambda {
657+ @ Override
658+ public String formatFragment (String fragment ) {
659+ // remove backticks because this is used as prefix for Codec generation
660+ return formatIdentifier (fragment , false ).replace ("`" , "" ) + "Codec" ;
661+ }
662+ }
663+
664+ private static class HandleDownloadLambda extends CustomLambda {
665+ @ Override
666+ public String formatFragment (String fragment ) {
667+ if (fragment .equals ("asJson[File]" )) {
668+ return "asFile(File.createTempFile(\" download\" , \" .tmp\" )).mapLeft(errStr => DeserializationException(errStr, new Exception(errStr)))" ;
669+ } else {
670+ return fragment ;
671+ }
672+ }
673+ }
674+
675+ @ Getter
676+ private static class PathMetadata {
677+ private final String path ;
678+ private final ArrayList <String > pathParams ;
679+
680+ PathMetadata (String path , ArrayList <String > pathParams ) {
681+ this .path = path ;
682+ this .pathParams = pathParams ;
683+ }
684+ }
685+
605686}
0 commit comments