swagger-api / swagger-codegen

swagger-codegen contains a template-driven engine to generate documentation, API clients and server stubs in different languages by parsing your OpenAPI / Swagger definition.
http://swagger.io
Apache License 2.0
17.04k stars 6.03k forks source link

Output template variables as single JSON #2553

Open kenjitayama opened 8 years ago

kenjitayama commented 8 years ago

Currently, when both -DdebugModels and -DdebugOperations are used, template variables are output as separate JSONs.

If they are merged as a single JSON, it would be easier for developers to use their favorite template engines, such as EJS. Actually, I am having a hard time writing mustache templates to match our own client HTTP library, and I am starting to use EJS.

I am using this modification to accomplish this. https://github.com/swagger-api/swagger-codegen/compare/master...kenjitayama:singleJSONDebugInfo

For this use case, output would be input for other scripts, not just for debug information. So maybe an option like --outputTemplateVariables (without needing -DdebugModels and -DdebugOperations) would make more sense.

How do you think of this kind of output?

wing328 commented 8 years ago

RE: hard time writing mustache templates.

We've recently merged https://github.com/swagger-api/swagger-codegen/pull/2360 to support new features in Mustache such as https://github.com/samskivert/jmustache#newline-trimming

I found it much better than before.

Is that something you would give a second try?

kenjitayama commented 8 years ago

Thanks for introducing the updates in JMustache. Newline Trimming sounds great, but I wanted more functionality in my templates.

My templates became to have heavily nested "if/else", so I want to shift those to helper methods. With EJS, I can write helper methods in template files using Javascript, which can help the output part of the template become more readable. Other template engines that allow arbitrary code inside the template, are ERB(Ruby), JSP(Java), and plain PHP.

Supporting template variables output in a single JSON would open the doors to having freedom in using any template engine you like.

fehguy commented 8 years ago

Sorry, these are logic-less so no if/else logic is possible. You'll have to do this in your pre-processing logic.

You can do this, but strategically we are not supporting template logic. You can set variables and do a {{#YOUR_VAR}} or {{^YOUR_VAR}} which simulates if/else

kenjitayama commented 8 years ago

You'll have to do this in your pre-processing logic.

I might be missing something. Where can I put pre-processing logic? Do I need to write a custom generator class?

My current method is something like this. It's really a workaround, but this way I can use any template engine.

java 
-DsingleJSONDebugInfo # in my current modification to support template variables output in single JSON \
-DdebugModels \
-DdebugOperations \
-Dmodels \
-Dapis \
-jar swagger-codegen-cli-2.1.6.jar generate \
-i Swagger.yml \
-t templates \
-l swift \
| my_script # consumes the template variable JSON and uses EJS for output
fehguy commented 8 years ago

Yes, you would write a java class to mutate the output

kenjitayama commented 8 years ago

You can do this, but strategically we are not supporting template logic

I understand it's important to separate logic from template.

Yes, you would write a java class to mutate the output

Thanks, I'll consider that as an option too.

wing328 commented 8 years ago

@kenjitayama can you please elaborate a bit more on the logic you need in the template? Some examples would be helpful.

wing328 commented 8 years ago

(you can do it later as it's late in Japan, 1am JST)

kenjitayama commented 8 years ago

I mentioned about heavily nested if/else ( using {{#SOME_VAR}} and {{^SOME_VAR}} ), but here is a simpler example I experienced.

I have my own HTTP client library written in Swift, and I am writing mustache templates to generate code that can be used with it. It uses a JSON library called SwiftyJSON, and the syntax for retreiving string value from a json object is jsonObject["someKey"].string. But the provided template variables have String (as dataType), but doesn't have string (need to lowercase the S). So what I did was a mix-in at the Swift side to provide syntax jsonObject["someKey"].getString() (can use the dataType template variable).

If there is more flexibilty in the code generation side, I wouldn't have do this kind of workaround at the Swift side. I learned that you can add more template variables by writing custom generater classes in Java, but that forces a developer to build swagger-codegen instead of just using the jar file (or write custom classes in jvm languages like Groovy).

wing328 commented 8 years ago

@kenjitayama Can your custom HTTP client library extends RequestBuilder <T> similar to what AlamofireRequestBuilder has done? (so that you don't need to retrieve the string value of the jsonObject in the template)

Or can you write a wrapper on top of your HTTP client library so that it can extends RequestBuilder <T>?

kenjitayama commented 8 years ago

The point is that some developers have their own preference of HTTP client code, and want control over them. I have no issues blocking development at our project for now.

It might not match the strategy of swagger-codegen, but I thought giving freedom to use template engine of their choice would enable diversity of generated client code. Maybe better client code will keep evolving.

wing328 commented 8 years ago

@kenjitayama I definitely want to give freedom to other developers to leverage template engine of their choice but it comes down to resource we've to make it happen.

Have you looked at the Java SDK? It supports 5 different HTTP libraries (e.g. Jersey 1.x, 2.x, Retrofit 1.x, 2.x, etc) thanks to a swappable ApiClient.java. If Swift API client has something similar, would that help meet your requirement?

kenjitayama commented 8 years ago

Sorry for replying late. I found the mustache files for those client libraries. It's great that many client libraries are supported but sometimes people already have HTTP libraries of their own and need to use them. The default Swift client library uses Alamofire and we do too, but we use RxSwift instead of PromiseKit, have our way of request retrying, and so on.

I definitely want to give freedom to other developers to leverage template engine of their choice but it comes down to resource we've to make it happen.

I think there are two approaches for this.

  1. command line option for choosing template language
  2. command line option to output all template variables in single JSON to STDOUT (Run template engine by yourself style).

1 is like for everyone, and 2 is more DIY. I enabled 2 with a quick modification in DefaultGenerator.java (diff link in my first comment), and rewrote all of my templates with EJS, and I'm quite happy now. I thought of doing a PR, but the modification was too much a sloppy workaround.

Hope someone (or me, if I can get more time) can work on a PR…

wing328 commented 8 years ago

@kenjitayama thanks for sharing additional details with us.

My view is that in this case you're using a customized HTTP library to meet your requirement, which is perfectly fine. To me, it's trade off between cost of customizations (with ongoing maintenance) and the benefit provided by those customized changes/libraries.

If I might, I want to attack this problem from a different angle. For your requirements (e.g. retry), can those be added to Alarmofire or PromiseKit?

For the 2 approaches that you suggested, I think 2 is more doable.

kenjitayama commented 8 years ago

Topics regarding this issue seems to be spreading… but as for retry feature:

Looks like retry feature is not implemented in Alamofire and the reason might be because it is out of scope of the library.

I have no experience in PromiseKit, but I found an open issue for that.

We are using a modified version of this RxSwift example code to retry on returning from a lost-connection state. It uses retry feature of RxSwift (Defined in Rx spec).

Thanks for considering this feature :)