Skip to content

[Python] automatically generate documentation (markdown) for Python API client #2472

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

Merged
merged 4 commits into from
Apr 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.swagger.codegen.CliOption;
import io.swagger.codegen.CodegenConfig;
import io.swagger.codegen.CodegenConstants;
import io.swagger.codegen.CodegenParameter;
import io.swagger.codegen.CodegenType;
import io.swagger.codegen.DefaultCodegen;
import io.swagger.codegen.SupportingFile;
Expand All @@ -17,6 +18,8 @@
public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig {
protected String packageName;
protected String packageVersion;
protected String apiDocPath = "docs/";
protected String modelDocPath = "docs/";

public PythonClientCodegen() {
super();
Expand All @@ -28,6 +31,9 @@ public PythonClientCodegen() {
apiTemplateFiles.put("api.mustache", ".py");
embeddedTemplateDir = templateDir = "python";

modelDocTemplateFiles.put("model_doc.mustache", ".md");
apiDocTemplateFiles.put("api_doc.mustache", ".md");

languageSpecificPrimitives.clear();
languageSpecificPrimitives.add("int");
languageSpecificPrimitives.add("float");
Expand All @@ -36,6 +42,7 @@ public PythonClientCodegen() {
languageSpecificPrimitives.add("str");
languageSpecificPrimitives.add("datetime");
languageSpecificPrimitives.add("date");
languageSpecificPrimitives.add("object");

typeMapping.clear();
typeMapping.put("integer", "int");
Expand Down Expand Up @@ -97,6 +104,10 @@ public void processOpts() {
additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName);
additionalProperties.put(CodegenConstants.PACKAGE_VERSION, packageVersion);

// make api and model doc path available in mustache template
additionalProperties.put("apiDocPath", apiDocPath);
additionalProperties.put("modelDocPath", modelDocPath);

String swaggerFolder = packageName;

modelPackage = swaggerFolder + File.separatorChar + "models";
Expand Down Expand Up @@ -138,6 +149,27 @@ public String escapeReservedWord(String name) {
return "_" + name;
}

@Override
public String apiDocFileFolder() {
return (outputFolder + "/" + apiDocPath);
}

@Override
public String modelDocFileFolder() {
return (outputFolder + "/" + modelDocPath);
}

@Override
public String toModelDocFilename(String name) {
return toModelName(name);
}

@Override
public String toApiDocFilename(String name) {
return toApiName(name);
}


@Override
public String apiFileFolder() {
return outputFolder + File.separatorChar + apiPackage().replace('.', File.separatorChar);
Expand Down Expand Up @@ -388,4 +420,70 @@ public String toDefaultValue(Property p) {
return null;
}

@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".equalsIgnoreCase(type) || "str".equalsIgnoreCase(type)) {
if (example == null) {
example = p.paramName + "_example";
}
example = "'" + escapeText(example) + "'";
} else if ("Integer".equals(type) || "int".equals(type)) {
if (example == null) {
example = "56";
}
} else if ("Float".equalsIgnoreCase(type) || "Double".equalsIgnoreCase(type)) {
if (example == null) {
example = "3.4";
}
} else if ("BOOLEAN".equalsIgnoreCase(type) || "bool".equalsIgnoreCase(type)) {
if (example == null) {
example = "True";
}
} else if ("file".equalsIgnoreCase(type)) {
if (example == null) {
example = "/path/to/file";
}
example = "'" + escapeText(example) + "'";
} else if ("Date".equalsIgnoreCase(type)) {
if (example == null) {
example = "2013-10-20";
}
example = "'" + escapeText(example) + "'";
} else if ("DateTime".equalsIgnoreCase(type)) {
if (example == null) {
example = "2013-10-20T19:20:30+01:00";
}
example = "'" + escapeText(example) + "'";
} else if (!languageSpecificPrimitives.contains(type)) {
// type is a model class, e.g. User
example = this.packageName + "." + type + "()";
} else {
LOGGER.warn("Type " + type + " not handled properly in setParameterExampleValue");
}

if (example == null) {
example = "NULL";
} else if (Boolean.TRUE.equals(p.isListContainer)) {
example = "[" + example + "]";
} else if (Boolean.TRUE.equals(p.isMapContainer)) {
example = "{'key': " + example + "}";
}

p.example = example;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{{/appDescription}}
This SDK is automatically generated by the [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) project:

- API verion: {{appVersion}}
- API version: {{appVersion}}
- Package version: {{projectVersion}}
- Build date: {{generatedDate}}
- Build package: {{generatorClass}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Automatically generated by the [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) project:

- API verion: {{appVersion}}
- API version: {{appVersion}}
- Package version: {{moduleVersion}}
- Build date: {{generatedDate}}
- Build package: {{generatorClass}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

This PHP package is automatically generated by the [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) project:

- API verion: {{appVersion}}
- API version: {{appVersion}}
- Package version: {{artifactVersion}}
- Build date: {{generatedDate}}
- Build package: {{generatorClass}}
Expand Down
141 changes: 95 additions & 46 deletions modules/swagger-codegen/src/main/resources/python/README.mustache
Original file line number Diff line number Diff line change
@@ -1,73 +1,122 @@
# {{packageName}}
{{#appDescription}}
{{{appDescription}}}
{{/appDescription}}

This Python package is automatically generated by the [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) project:

- API version: {{appVersion}}
- Package version: {{packageVersion}}
- Build date: {{generatedDate}}
- Build package: {{generatorClass}}
{{#infoUrl}}
For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}})
{{/infoUrl}}

## Requirements.
Python 2.7 and later.

## Setuptools
You can install the bindings via [Setuptools](http://pypi.python.org/pypi/setuptools).
Python 2.7 and 3.4+

## Installation & Usage
### pip install

If the python package is hosted on Github, you can install directly from Github

```sh
python setup.py install
pip install git+https://github.com/{{{gitUserId}}}/{{{gitRepoId}}}.git
```
(you may need to run `pip` with root permission: `sudo pip install git+https://github.com/{{{gitUserId}}}/{{{gitRepoId}}}.git`)

Or you can install from Github via pip:
Then import the package:
```python
import {{{packageName}}}
```

### Setuptools

Install via [Setuptools](http://pypi.python.org/pypi/setuptools).

```sh
pip install git+https://github.com/geekerzp/swagger_client.git
python setup.py install --user
```
(or `sudo python setup.py install` to install the package for all users)

To use the bindings, import the pacakge:

Then import the package:
```python
import swagger_client
import {{{packageName}}}
```

## Manual Installation
If you do not wish to use setuptools, you can download the latest release.
Then, to use the bindings, import the package:
## Getting Started

Please follow the [installation procedure](#installation--usage) and then run the following:

```python
import path.to.swagger_client
import time
import {{{packageName}}}
from {{{packageName}}}.rest import ApiException
from pprint import pprint
{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}}{{#hasAuthMethods}}{{#authMethods}}{{#isBasic}}
# Configure HTTP basic authorization: {{{name}}}
{{{packageName}}}.configuration.username = 'YOUR_USERNAME'
{{{packageName}}}.configuration.password = 'YOUR_PASSWORD'{{/isBasic}}{{#isApiKey}}
# Configure API key authorization: {{{name}}}
{{{packageName}}}.configuration.api_key['{{{keyParamName}}}'] = 'YOUR_API_KEY'
# Uncomment below to setup prefix (e.g. BEARER) for API key, if needed
# {{{packageName}}}.configuration.api_key_prefix['{{{keyParamName}}}'] = 'BEARER'{{/isApiKey}}{{#isOAuth}}
# Configure OAuth2 access token for authorization: {{{name}}}
{{{packageName}}}.configuration.access_token = 'YOUR_ACCESS_TOKEN'{{/isOAuth}}{{/authMethods}}
{{/hasAuthMethods}}
# create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}
{{#allParams}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}
{{/allParams}}

try:
{{#summary}} # {{{.}}}
{{/summary}} {{#returnType}}api_response = {{/returnType}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}}
pprint(api_response){{/returnType}}
except ApiException as e:
print "Exception when calling {{classname}}->{{operationId}}: %s\n" % e
{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}}
```

## Getting Started
## Documentation for API Endpoints

TODO
All URIs are relative to *{{basePath}}*

## Documentation
Class | Method | HTTP request | Description
------------ | ------------- | ------------- | -------------
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}}
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}

TODO
## Documentation For Models

## Tests
{{#models}}{{#model}} - [{{{classname}}}]({{modelDocPath}}{{{classname}}}.md)
{{/model}}{{/models}}

(Please make sure you have [virtualenv](http://docs.python-guide.org/en/latest/dev/virtualenvs/) installed)
## Documentation For Authorization

Execute the following command to run the tests in the current Python (v2 or v3) environment:
{{^authMethods}} All endpoints do not require authorization.
{{/authMethods}}{{#authMethods}}{{#last}} Authentication schemes defined for the API:{{/last}}{{/authMethods}}
{{#authMethods}}## {{{name}}}

```sh
$ make test
[... magically installs dependencies and runs tests on your virtualenv]
Ran 7 tests in 19.289s
{{#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}}

OK
```
or
{{/authMethods}}

```
$ mvn integration-test -rf :PythonPetstoreClientTests
Using 2195432783 as seed
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 37.594 s
[INFO] Finished at: 2015-05-16T18:00:35+08:00
[INFO] Final Memory: 11M/156M
[INFO] ------------------------------------------------------------------------
```
If you want to run the tests in all the python platforms:
## Author

```sh
$ make test-all
[... tox creates a virtualenv for every platform and runs tests inside of each]
py27: commands succeeded
py34: commands succeeded
congratulations :)
```
{{#apiInfo}}{{#apis}}{{^hasMore}}{{infoEmail}}
{{/hasMore}}{{/apis}}{{/apiInfo}}
10 changes: 5 additions & 5 deletions modules/swagger-codegen/src/main/resources/python/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class {{classname}}(object):
self.api_client = config.api_client
{{#operation}}

def {{nickname}}(self, {{#sortParamsByRequiredFlag}}{{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{/sortParamsByRequiredFlag}}**kwargs):
def {{operationId}}(self, {{#sortParamsByRequiredFlag}}{{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{/sortParamsByRequiredFlag}}**kwargs):
"""
{{{summary}}}
{{{notes}}}
Expand All @@ -59,10 +59,10 @@ class {{classname}}(object):
>>> pprint(response)
>>>
{{#sortParamsByRequiredFlag}}
>>> thread = api.{{nickname}}({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}callback=callback_function)
>>> thread = api.{{operationId}}({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}callback=callback_function)
{{/sortParamsByRequiredFlag}}
{{^sortParamsByRequiredFlag}}
>>> thread = api.{{nickname}}({{#allParams}}{{#required}}{{paramName}}={{paramName}}_value, {{/required}}{{/allParams}}callback=callback_function)
>>> thread = api.{{operationId}}({{#allParams}}{{#required}}{{paramName}}={{paramName}}_value, {{/required}}{{/allParams}}callback=callback_function)
{{/sortParamsByRequiredFlag}}

:param callback function: The callback function
Expand All @@ -83,7 +83,7 @@ class {{classname}}(object):
if key not in all_params:
raise TypeError(
"Got an unexpected keyword argument '%s'"
" to method {{nickname}}" % key
" to method {{operationId}}" % key
)
params[key] = val
del params['kwargs']
Expand All @@ -92,7 +92,7 @@ class {{classname}}(object):
{{#required}}
# verify the required parameter '{{paramName}}' is set
if ('{{paramName}}' not in params) or (params['{{paramName}}'] is None):
raise ValueError("Missing the required parameter `{{paramName}}` when calling `{{nickname}}`")
raise ValueError("Missing the required parameter `{{paramName}}` when calling `{{operationId}}`")
{{/required}}
{{/allParams}}

Expand Down
Loading