forked from loafle/openapi-generator-original
Merge pull request #2344 from xhh/ruby-doc
[Ruby] Add auto-generated documentation in Markdown to Ruby client
This commit is contained in:
@@ -3,15 +3,21 @@ package io.swagger.codegen.languages;
|
||||
import io.swagger.codegen.CliOption;
|
||||
import io.swagger.codegen.CodegenConfig;
|
||||
import io.swagger.codegen.CodegenConstants;
|
||||
import io.swagger.codegen.CodegenOperation;
|
||||
import io.swagger.codegen.CodegenParameter;
|
||||
import io.swagger.codegen.CodegenType;
|
||||
import io.swagger.codegen.DefaultCodegen;
|
||||
import io.swagger.codegen.SupportingFile;
|
||||
import io.swagger.models.Model;
|
||||
import io.swagger.models.Operation;
|
||||
import io.swagger.models.Swagger;
|
||||
import io.swagger.models.properties.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
@@ -40,6 +46,8 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
protected String gemDescription = "This gem maps to a swagger API";
|
||||
protected String gemAuthor = "";
|
||||
protected String gemAuthorEmail = "";
|
||||
protected String apiDocPath = "docs/";
|
||||
protected String modelDocPath = "docs/";
|
||||
|
||||
protected static int emptyMethodNameCounter = 0;
|
||||
|
||||
@@ -50,6 +58,8 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
outputFolder = "generated-code" + File.separator + "ruby";
|
||||
modelTemplateFiles.put("model.mustache", ".rb");
|
||||
apiTemplateFiles.put("api.mustache", ".rb");
|
||||
modelDocTemplateFiles.put("model_doc.mustache", ".md");
|
||||
apiDocTemplateFiles.put("api_doc.mustache", ".md");
|
||||
embeddedTemplateDir = templateDir = "ruby";
|
||||
|
||||
modelTestTemplateFiles.put("model_test.mustache", ".rb");
|
||||
@@ -194,6 +204,9 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
setGemAuthorEmail((String) additionalProperties.get(GEM_AUTHOR_EMAIL));
|
||||
}
|
||||
|
||||
// make api and model doc path available in mustache template
|
||||
additionalProperties.put("apiDocPath", apiDocPath);
|
||||
additionalProperties.put("modelDocPath", modelDocPath);
|
||||
|
||||
// use constant model/api package (folder path)
|
||||
setModelPackage("models");
|
||||
@@ -206,8 +219,39 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
supportingFiles.add(new SupportingFile("api_error.mustache", gemFolder, "api_error.rb"));
|
||||
supportingFiles.add(new SupportingFile("configuration.mustache", gemFolder, "configuration.rb"));
|
||||
supportingFiles.add(new SupportingFile("version.mustache", gemFolder, "version.rb"));
|
||||
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
|
||||
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, Map<String, Model> definitions, Swagger swagger) {
|
||||
CodegenOperation op = super.fromOperation(path, httpMethod, operation, definitions, swagger);
|
||||
// Set vendor-extension to be used in template:
|
||||
// x-codegen-hasMoreRequired
|
||||
// x-codegen-hasMoreOptional
|
||||
// x-codegen-hasRequiredParams
|
||||
CodegenParameter lastRequired = null;
|
||||
CodegenParameter lastOptional = null;
|
||||
for (CodegenParameter p : op.allParams) {
|
||||
if (p.required != null && p.required) {
|
||||
lastRequired = p;
|
||||
} else {
|
||||
lastOptional = p;
|
||||
}
|
||||
}
|
||||
for (CodegenParameter p : op.allParams) {
|
||||
if (p == lastRequired) {
|
||||
p.vendorExtensions.put("x-codegen-hasMoreRequired", false);
|
||||
} else if (p == lastOptional) {
|
||||
p.vendorExtensions.put("x-codegen-hasMoreOptional", false);
|
||||
} else {
|
||||
p.vendorExtensions.put("x-codegen-hasMoreRequired", true);
|
||||
p.vendorExtensions.put("x-codegen-hasMoreOptional", true);
|
||||
}
|
||||
}
|
||||
op.vendorExtensions.put("x-codegen-hasRequiredParams", lastRequired != null);
|
||||
return op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodegenType getTag() {
|
||||
@@ -271,6 +315,16 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
return outputFolder + File.separator + specFolder + File.separator + modelPackage.replace("/", File.separator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apiDocFileFolder() {
|
||||
return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String modelDocFileFolder() {
|
||||
return (outputFolder + "/" + modelDocPath).replace('/', File.separatorChar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeDeclaration(Property p) {
|
||||
if (p instanceof ArrayProperty) {
|
||||
@@ -328,7 +382,7 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
String type = null;
|
||||
if (typeMapping.containsKey(swaggerType)) {
|
||||
type = typeMapping.get(swaggerType);
|
||||
if (languageSpecificPrimitives.contains(type)) {
|
||||
if (languageSpecificPrimitives.contains(type)) {
|
||||
return type;
|
||||
}
|
||||
} else {
|
||||
@@ -414,6 +468,11 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
return underscore(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toModelDocFilename(String name) {
|
||||
return toModelName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toApiFilename(String name) {
|
||||
// replace - with _ e.g. created-at => created_at
|
||||
@@ -423,6 +482,11 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
return underscore(name) + "_api";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toApiDocFilename(String name) {
|
||||
return toApiName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toApiTestFilename(String name) {
|
||||
return toApiFilename(name) + "_spec";
|
||||
@@ -466,6 +530,69 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
return gemName + "/" + apiPackage() + "/" + toApiFilename(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameterExampleValue(CodegenParameter p) {
|
||||
String example;
|
||||
|
||||
if (p.defaultValue == null) {
|
||||
example = p.example;
|
||||
} else {
|
||||
example = p.defaultValue;
|
||||
}
|
||||
|
||||
String type = p.baseType;
|
||||
if (type == null) {
|
||||
type = p.dataType;
|
||||
}
|
||||
|
||||
if ("String".equals(type)) {
|
||||
if (example == null) {
|
||||
example = p.paramName + "_example";
|
||||
}
|
||||
example = "\"" + escapeText(example) + "\"";
|
||||
} else if ("Integer".equals(type)) {
|
||||
if (example == null) {
|
||||
example = "56";
|
||||
}
|
||||
} else if ("Float".equals(type)) {
|
||||
if (example == null) {
|
||||
example = "3.4";
|
||||
}
|
||||
} else if ("BOOLEAN".equals(type)) {
|
||||
if (example == null) {
|
||||
example = "true";
|
||||
}
|
||||
} else if ("File".equals(type)) {
|
||||
if (example == null) {
|
||||
example = "/path/to/file";
|
||||
}
|
||||
example = "File.new(\"" + escapeText(example) + "\")";
|
||||
} else if ("Date".equals(type)) {
|
||||
if (example == null) {
|
||||
example = "2013-10-20";
|
||||
}
|
||||
example = "Date.parse(\"" + escapeText(example) + "\")";
|
||||
} else if ("DateTime".equals(type)) {
|
||||
if (example == null) {
|
||||
example = "2013-10-20T19:20:30+01:00";
|
||||
}
|
||||
example = "DateTime.parse(\"" + escapeText(example) + "\")";
|
||||
} else if (!languageSpecificPrimitives.contains(type)) {
|
||||
// type is a model class, e.g. User
|
||||
example = moduleName + "::" + type + ".new";
|
||||
}
|
||||
|
||||
if (example == null) {
|
||||
example = "nil";
|
||||
} else if (Boolean.TRUE.equals(p.isListContainer)) {
|
||||
example = "[" + example + "]";
|
||||
} else if (Boolean.TRUE.equals(p.isMapContainer)) {
|
||||
example = "{'key': " + example + "}";
|
||||
}
|
||||
|
||||
p.example = example;
|
||||
}
|
||||
|
||||
public void setGemName(String gemName) {
|
||||
this.gemName = gemName;
|
||||
}
|
||||
|
||||
107
modules/swagger-codegen/src/main/resources/ruby/README.mustache
Normal file
107
modules/swagger-codegen/src/main/resources/ruby/README.mustache
Normal file
@@ -0,0 +1,107 @@
|
||||
# {{gemName}}
|
||||
|
||||
{{moduleName}} - the Ruby gem for the {{appName}}
|
||||
|
||||
Version: {{gemVersion}}
|
||||
|
||||
Automatically generated by the Ruby Swagger Codegen project:
|
||||
|
||||
- Build date: {{generatedDate}}
|
||||
- Build package: {{generatorClass}}
|
||||
|
||||
## Installation
|
||||
|
||||
### Build a gem
|
||||
|
||||
You can build the generated client into a gem:
|
||||
|
||||
```shell
|
||||
gem build {{{gemName}}}.gemspec
|
||||
```
|
||||
|
||||
Then you can either install the gem:
|
||||
|
||||
```shell
|
||||
gem install ./{{{gemName}}}-{{{gemVersion}}}.gem
|
||||
```
|
||||
|
||||
or publish the gem to a gem server like [RubyGems](https://rubygems.org/).
|
||||
|
||||
Finally add this to your Gemfile:
|
||||
|
||||
gem '{{{gemName}}}', '~> {{{gemVersion}}}'
|
||||
|
||||
### Host as a git repository
|
||||
|
||||
You can also choose to host the generated client as a git repository, e.g. on github:
|
||||
https://github.com/YOUR_USERNAME/YOUR_REPO
|
||||
|
||||
Then you can reference it in Gemfile:
|
||||
|
||||
gem '{{{gemName}}}', :git => 'https://github.com/YOUR_USERNAME/YOUR_REPO.git'
|
||||
|
||||
### Use without installation
|
||||
|
||||
You can also use the client directly like this:
|
||||
|
||||
```shell
|
||||
ruby -Ilib script.rb
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
```ruby
|
||||
require '{{{gemName}}}'
|
||||
|
||||
{{{moduleName}}}.configure do |config|
|
||||
# Use the line below to configure API key authorization if needed:
|
||||
#config.api_key['api_key'] = 'your api key'
|
||||
|
||||
config.host = 'petstore.swagger.io'
|
||||
config.base_path = '/v2'
|
||||
# Enable debugging (default is disabled).
|
||||
config.debugging = true
|
||||
end
|
||||
|
||||
# Assuming there's a `PetApi` containing a `get_pet_by_id` method
|
||||
# which returns a model object:
|
||||
pet_api = {{{moduleName}}}::PetApi.new
|
||||
pet = pet_api.get_pet_by_id(5)
|
||||
puts pet.to_body
|
||||
```
|
||||
|
||||
## Documentation for API Endpoints
|
||||
|
||||
All URIs are relative to *{{basePath}}*
|
||||
|
||||
Class | Method | HTTP request | Description
|
||||
------------ | ------------- | ------------- | -------------
|
||||
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{moduleName}}::{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}}
|
||||
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
|
||||
|
||||
## Documentation for Models
|
||||
|
||||
{{#models}}{{#model}} - [{{moduleName}}::{{classname}}]({{modelDocPath}}{{classname}}.md)
|
||||
{{/model}}{{/models}}
|
||||
|
||||
## Documentation for Authorization
|
||||
|
||||
{{^authMethods}} All endpoints do not require authorization.
|
||||
{{/authMethods}}{{#authMethods}}{{#last}} Authentication schemes defined for the API:{{/last}}{{/authMethods}}
|
||||
{{#authMethods}}### {{name}}
|
||||
|
||||
{{#isApiKey}}- **Type**: API key
|
||||
- **API key parameter name**: {{keyParamName}}
|
||||
- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}}
|
||||
{{/isApiKey}}
|
||||
{{#isBasic}}- **Type**: HTTP basic authentication
|
||||
{{/isBasic}}
|
||||
{{#isOAuth}}- **Type**: OAuth
|
||||
- **Flow**: {{flow}}
|
||||
- **Authorizatoin URL**: {{authorizationUrl}}
|
||||
- **Scopes**: {{^scopes}}N/A{{/scopes}}
|
||||
{{#scopes}}-- {{scope}}: {{description}}
|
||||
{{/scopes}}
|
||||
{{/isOAuth}}
|
||||
|
||||
{{/authMethods}}
|
||||
@@ -0,0 +1,73 @@
|
||||
# {{moduleName}}::{{classname}}{{#description}}
|
||||
{{description}}{{/description}}
|
||||
|
||||
All URIs are relative to *{{basePath}}*
|
||||
|
||||
Method | HTTP request | Description
|
||||
------------- | ------------- | -------------
|
||||
{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}}
|
||||
{{/operation}}{{/operations}}
|
||||
|
||||
{{#operations}}
|
||||
{{#operation}}
|
||||
# **{{operationId}}**
|
||||
> {{#returnType}}{{returnType}} {{/returnType}}{{operationId}}{{#hasParams}}({{#allParams}}{{#required}}{{{paramName}}}{{#vendorExtensions.x-codegen-hasMoreRequired}}, {{/vendorExtensions.x-codegen-hasMoreRequired}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{{#vendorExtensions.x-codegen-hasRequiredParams}}, {{/vendorExtensions.x-codegen-hasRequiredParams}}opts{{/hasOptionalParams}}){{/hasParams}}
|
||||
|
||||
{{summary}}{{#notes}}
|
||||
|
||||
{{notes}}{{/notes}}
|
||||
|
||||
### Example
|
||||
```ruby{{#hasAuthMethods}}
|
||||
{{{moduleName}}}.configure do |config|{{#authMethods}}{{#isBasic}}
|
||||
# Configure HTTP basic authorization: {{{name}}}
|
||||
config.username = 'YOUR USERNAME'
|
||||
config.password = 'YOUR PASSWORD'{{/isBasic}}{{#isApiKey}}
|
||||
# Configure API key authorization: {{{name}}}
|
||||
config.api_key['{{{keyParamName}}}'] = "YOUR API KEY"
|
||||
# Uncomment the following line to set a prefix for the API key, e.g. "Token" (defaults to nil)
|
||||
#config.api_key_prefix['{{{keyParamName}}}'] = "Token"{{/isApiKey}}{{#isOAuth}}
|
||||
# Configure OAuth2 access token for authorization: {{{name}}}
|
||||
config.access_token = "YOUR ACCESS TOKEN"{{/isOAuth}}
|
||||
{{/authMethods}}end
|
||||
{{/hasAuthMethods}}
|
||||
|
||||
api = {{{moduleName}}}::{{{classname}}}.new{{#hasParams}}
|
||||
{{#vendorExtensions.x-codegen-hasRequiredParams}}{{#allParams}}{{#required}}
|
||||
{{{paramName}}} = {{{example}}} # [{{{dataType}}}] {{{description}}}
|
||||
{{/required}}{{/allParams}}{{/vendorExtensions.x-codegen-hasRequiredParams}}{{#hasOptionalParams}}
|
||||
opts = { {{#allParams}}{{^required}}
|
||||
{{{paramName}}}: {{{example}}}{{#vendorExtensions.x-codegen-hasMoreOptional}},{{/vendorExtensions.x-codegen-hasMoreOptional}} # [{{{dataType}}}] {{{description}}}{{/required}}{{/allParams}}
|
||||
}{{/hasOptionalParams}}{{/hasParams}}
|
||||
|
||||
begin
|
||||
{{#returnType}}result = {{/returnType}}api.{{{operationId}}}{{#hasParams}}({{#allParams}}{{#required}}{{{paramName}}}{{#vendorExtensions.x-codegen-hasMoreRequired}}, {{/vendorExtensions.x-codegen-hasMoreRequired}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{{#vendorExtensions.x-codegen-hasRequiredParams}}, {{/vendorExtensions.x-codegen-hasRequiredParams}}opts{{/hasOptionalParams}}){{/hasParams}}
|
||||
rescue {{{moduleName}}}::ApiError => e
|
||||
puts "Exception when calling {{{operationId}}}: #{e}"
|
||||
end
|
||||
```
|
||||
|
||||
### Parameters
|
||||
{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}}
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}}
|
||||
{{#allParams}} **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{^required}}[optional] {{/required}}{{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}
|
||||
{{/allParams}}
|
||||
|
||||
### Return type
|
||||
|
||||
{{#returnType}}{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{returnType}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}nil (empty response body){{/returnType}}
|
||||
|
||||
### Authorization
|
||||
|
||||
{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{name}}](../README.md#{{name}}){{^-last}}, {{/-last}}{{/authMethods}}
|
||||
|
||||
### HTTP reuqest headers
|
||||
|
||||
- **Content-Type**: {{#consumes}}{{mediaType}}{{#hasMore}}, {{/hasMore}}{{/consumes}}{{^consumes}}Not defined{{/consumes}}
|
||||
- **Accept**: {{#produces}}{{mediaType}}{{#hasMore}}, {{/hasMore}}{{/produces}}{{^produces}}Not defined{{/produces}}
|
||||
|
||||
|
||||
|
||||
{{/operation}}
|
||||
{{/operations}}
|
||||
@@ -0,0 +1,36 @@
|
||||
*.gem
|
||||
*.rbc
|
||||
/.config
|
||||
/coverage/
|
||||
/InstalledFiles
|
||||
/pkg/
|
||||
/spec/reports/
|
||||
/spec/examples.txt
|
||||
/test/tmp/
|
||||
/test/version_tmp/
|
||||
/tmp/
|
||||
|
||||
## Specific to RubyMotion:
|
||||
.dat*
|
||||
.repl_history
|
||||
build/
|
||||
|
||||
## Documentation cache and generated files:
|
||||
/.yardoc/
|
||||
/_yardoc/
|
||||
/doc/
|
||||
/rdoc/
|
||||
|
||||
## Environment normalization:
|
||||
/.bundle/
|
||||
/vendor/bundle
|
||||
/lib/bundler/man/
|
||||
|
||||
# for a library or gem, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# Gemfile.lock
|
||||
# .ruby-version
|
||||
# .ruby-gemset
|
||||
|
||||
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
||||
.rvmrc
|
||||
@@ -0,0 +1,9 @@
|
||||
{{#models}}{{#model}}# {{moduleName}}::{{classname}}
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
{{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{datatype}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{datatype}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#readOnly}}[readonly] {{/readOnly}}{{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}
|
||||
{{/vars}}
|
||||
|
||||
{{/model}}{{/models}}
|
||||
Reference in New Issue
Block a user