Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kotlin generator migration #26

Merged
merged 46 commits into from
Apr 10, 2018
Merged

Conversation

tcslater
Copy link
Contributor

@tcslater tcslater commented Feb 28, 2018

Migration of kotlin generator files from swagger-codegen.
Updated for compatability with 3.0 changes and template migration from mustache to handlebars as per https://github.com/swagger-api/swagger-codegen/wiki/Swagger-Codegen-migration-from-Mustache-and-Handlebars-templates

Related PR for cleanup in swaggen-codegen 3.0.0 swagger-api/swagger-codegen#7739

@webron
Copy link
Contributor

webron commented Feb 28, 2018

Thanks, @tcslater!

@jimschubert - do you have time to help us review this PR?

@jmini
Copy link
Contributor

jmini commented Mar 2, 2018

This looks good. Thank you!

I have checked all the points of my migration guide:
https://github.com/swagger-api/swagger-codegen/wiki/Swagger-Codegen-migration-(swagger-codegen-generators-repository)

Here my review points:

  1. I asked myself: will you not use the occasion of a major version change (Swagger v3) to update the name to kotlin-client instead of just kotlin ?
    This way it would be clear in the language list that there is a client and a server.

  2. I have tried your generators on the petstore example (the version I have used is here: petstore.json)
    I get following error (with the client and with the server):

WARN io.swagger.codegen.languages.kotlin.AbstractKotlinCodegen - No Type defined for Property body
java.lang.NullPointerException
	at io.swagger.codegen.languages.kotlin.AbstractKotlinCodegen.toModelName(AbstractKotlinCodegen.java:473)
	at io.swagger.codegen.languages.kotlin.AbstractKotlinCodegen.getSchemaType(AbstractKotlinCodegen.java:300)
	at io.swagger.codegen.languages.DefaultCodegenConfig.fromModel(DefaultCodegenConfig.java:1285)
	at io.swagger.codegen.languages.DefaultCodegenConfig.fromRequestBody(DefaultCodegenConfig.java:2369)
	at io.swagger.codegen.languages.DefaultCodegenConfig.fromOperation(DefaultCodegenConfig.java:1943)
	at io.swagger.codegen.DefaultGenerator.processOperation(DefaultGenerator.java:855)
	at io.swagger.codegen.DefaultGenerator.processPaths(DefaultGenerator.java:782)
	at io.swagger.codegen.DefaultGenerator.generateApis(DefaultGenerator.java:421)
	at io.swagger.codegen.DefaultGenerator.generate(DefaultGenerator.java:729)
	at <my code>.convert(Swagger3ConverterMain.java:111)
Exception in thread "main" java.lang.RuntimeException: Could not process operation:
  Tag: class Tag {
    name: pet
    description: null
    externalDocs: null
}
  Operation: updatePetWithForm
  Resource: post /pet/{petId}
  Exception: null
	at io.swagger.codegen.DefaultGenerator.processOperation(DefaultGenerator.java:880)
	at io.swagger.codegen.DefaultGenerator.processPaths(DefaultGenerator.java:782)
	at io.swagger.codegen.DefaultGenerator.generateApis(DefaultGenerator.java:421)
	at io.swagger.codegen.DefaultGenerator.generate(DefaultGenerator.java:729)
	at <my code>.convert(Swagger3ConverterMain.java:111)
Caused by: java.lang.NullPointerException
	at io.swagger.codegen.languages.kotlin.AbstractKotlinCodegen.toModelName(AbstractKotlinCodegen.java:473)
	at io.swagger.codegen.languages.kotlin.AbstractKotlinCodegen.getSchemaType(AbstractKotlinCodegen.java:300)
	at io.swagger.codegen.languages.DefaultCodegenConfig.fromModel(DefaultCodegenConfig.java:1285)
	at io.swagger.codegen.languages.DefaultCodegenConfig.fromRequestBody(DefaultCodegenConfig.java:2369)
	at io.swagger.codegen.languages.DefaultCodegenConfig.fromOperation(DefaultCodegenConfig.java:1943)
	at io.swagger.codegen.DefaultGenerator.processOperation(DefaultGenerator.java:855)
	... 6 more

Can you have a look at it?

Copy link
Contributor

@jmini jmini left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have started to remove the guava dependency. See #5.


// This is here to potentially warn the user when an option is not supoprted by the target framework.
private Map<String, List<String>> optionsSupportedPerFramework = new ImmutableMap.Builder<String, List<String>>()
.put(Constants.KTOR, Arrays.asList(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use Collections.singletonMap(Constants.KTOR, Arrays.asList(..)); instead of guava.

@jmini
Copy link
Contributor

jmini commented Mar 6, 2018

Nice! Thank you for the update.

By reviewing your code, I have noticed, tha you are using ImmutableMap from guava.
In this project, we have migrated guava logic to plain java elements. See #5.

I think you should do it too.

KotlinServerCodegen.<init>(KotlinServerCodegen.java:29)

@jmini
Copy link
Contributor

jmini commented Mar 6, 2018

An other point:

When I have tried to generate the kotlin-server I got this error:

Exception in thread "main" java.lang.RuntimeException: Could not generate api file for 'Pet'
	at io.swagger.codegen.DefaultGenerator.generateApis(DefaultGenerator.java:524)
	at io.swagger.codegen.DefaultGenerator.generate(DefaultGenerator.java:728)
	at <my code>.Swagger3ConverterMain.convert(Swagger3ConverterMain.java:132)
Caused by: java.io.FileNotFoundException: /v2/kotlin-server/api.mustache
	at com.github.jknack.handlebars.io.URLTemplateLoader.sourceAt(URLTemplateLoader.java:70)
	at com.github.jknack.handlebars.Handlebars.compile(Handlebars.java:357)
	at com.github.jknack.handlebars.Handlebars.compile(Handlebars.java:343)
	at io.swagger.codegen.DefaultGenerator.getHandlebars(DefaultGenerator.java:1018)
	at io.swagger.codegen.DefaultGenerator.processTemplateToFile(DefaultGenerator.java:741)
	at io.swagger.codegen.DefaultGenerator.generateApis(DefaultGenerator.java:483)
	... 4 more

I needed to add:

config.additionalProperties().put(CodegenConstants.LIBRARY, KotlinServerCodegen.DEFAULT_LIBRARY);

Maybe you could set the default library (when noting is set).

I am not using the CLI, I use the swagger-codegen-generators artifact and its dependencies directly.
I did not try kotlin with the maven plugin.

@tcslater
Copy link
Contributor Author

tcslater commented Mar 6, 2018

Thanks. I have removed the guava dependency and fixed the default library option.

* @return sanitized string
*/
private String sanitizeKotlinSpecificNames(final String name) {
String word = removeNonNameElementToCamelCase(name);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might cause problems. Whatever removeNonNameElementToCamelCase does, if it changes name at all, it may make enums stop working.

For instance, if an enum is last-call, this method previously would have converted this to last_call. If removeNonNameElementToCamelCase changes name to lastCall or lastcall, that expectation won't be met. I couldn't find the implementation of removeNonNameElementToCamelCase, so I can't say for certain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was to fix an issue I was having where path parameters that were not in a valid kotlin format were not being converted for use as attributes in the generated data classes in Paths.kt. I'll look into it some more to make sure enums still work correctly.

}

public String getName() {
return "kotlin-client";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really part of this PR, but this seems as good a place as any to comment.

I think it's weird that this value is still used as an option to "language" when generating code. It makes sense that this generator's name is "kotlin-client" (more sense than when I generate scala client code at work using custom templates with -l scalatra -- which is a server generator and not a language).

I had suggested in an issue in the swagger-codegen repo that there are multiple levels of generation that I think should be addressed:

  • language (e.g. Kotlin)
  • generator type (e.g. Client)
  • framework (e.g. Default, OkHttp, Retrofit)
  • Options: language version, framework version, etc.

To support a lot of this cleanly, you'd want shared templates that can be reused across framework options and across language/framework versions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great comment, @jimschubert. This might help with another issue we're looking to solve with regard to versioning. @HugoMario can you track that please?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree regarding duplication. All of the model templates for kotlin-client and kotlin-server are currently duplicated. These should be shared. I was going to try and refactor this but the only way I could think of getting it to work at the moment was to make the client generator a library option... so "-l kotlin --library=client" and "-l kotlin --library=ktor-server". This seemed a little weird so I opted not to pursue it in this PR

@jimschubert
Copy link

Sorry for the late response. Being from a repository I don't watch, this was sorted into a mail folder I hardly ever look at.

@jmini
Copy link
Contributor

jmini commented Mar 7, 2018

@jimschubert:
Thank you for this answer!

I think it's weird that this value is still used as an option to "language" when generating code. It makes sense that this generator's name is "kotlin-client" (more sense than when I generate scala client code at work using custom templates with -l scalatra -- which is a server generator and not a language).

I had suggested in an issue in the swagger-codegen repo that there are multiple levels of generation that I think should be addressed:

  • language (e.g. Kotlin)
  • generator type (e.g. Client)
  • framework (e.g. Default, OkHttp, Retrofit)
  • Options: language version, framework version, etc.

To support a lot of this cleanly, you'd want shared templates that can be reused across framework options and across language/framework versions.

I totally share this vision!

Redundancy in templates:

I have discovered this issue: swagger-api/swagger-codegen#4937 => not so much activity there.

Generator names:

For the moment -l is used to select the generator. This is the only switch that exists.
This PR introduces a new generator: kotlin-server. For the client generator, I prefer kotlin-client than kotlin.

@webron
Copy link
Contributor

webron commented Mar 7, 2018

@jimschubert thanks for the review, no worries about the delay. If you don't think it's too much overload, maybe star this repo now. In the long run, the goal is to move all generators here.

Copy link
Contributor

@jmini jmini left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With #40 I have proposed to avoid fields declaration that hides fields or variables of a parent class. It would be nice if you could adopt this convention for kotlin.

  • declare the LOGGER as private static in your 3 classes
  • do not redeclare enumPropertyNaming in KotlinClientCodegen.

Thank you in advance.


public static final String DATE_LIBRARY = "dateLibrary";
protected CodegenConstants.ENUM_PROPERTY_NAMING_TYPE enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.camelCase;
static Logger LOGGER = LoggerFactory.getLogger(KotlinClientCodegen.class);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOGGER declaration should be private static

public class KotlinClientCodegen extends AbstractKotlinCodegen {

public static final String DATE_LIBRARY = "dateLibrary";
protected CodegenConstants.ENUM_PROPERTY_NAMING_TYPE enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.camelCase;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enumPropertyNaming is already declared in AbstractKotlinCodegen. You just need to change the value (in the constructor for example) in the child class.

import java.util.Map;

public abstract class AbstractKotlinCodegen extends DefaultCodegenConfig {
static Logger LOGGER = LoggerFactory.getLogger(AbstractKotlinCodegen.class);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOGGER declaration should be private static

public static final String DEFAULT_LIBRARY = Constants.KTOR;
public static final String GENERATE_APIS = "generateApis";

static Logger LOGGER = LoggerFactory.getLogger(KotlinServerCodegen.class);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOGGER declaration should be private static

@jmini
Copy link
Contributor

jmini commented Apr 5, 2018

Is there something to do here? I think this could be merged.

@tcslater
Copy link
Contributor Author

tcslater commented Apr 9, 2018

Nothing more to be done that I'm aware of. I'd be happy for it to be merged.

@HugoMario
Copy link
Contributor

thanks a lot @tcslater !!!

@HugoMario HugoMario merged commit c1b806c into swagger-api:master Apr 10, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants