Skip to content

Commit 6a4b1b2

Browse files
wing328MikailBag
authored andcommitted
[Go][experimental] provide code samples in the API doc (OpenAPITools#6115)
* provide code samples in api doc * update petstore samples
1 parent 18067cc commit 6a4b1b2

File tree

15 files changed

+2215
-6
lines changed

15 files changed

+2215
-6
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoClientExperimentalCodegen.java

Lines changed: 163 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818

1919
import io.swagger.v3.oas.models.media.Schema;
2020
import io.swagger.v3.oas.models.security.SecurityScheme;
21-
import org.openapitools.codegen.CodegenModel;
22-
import org.openapitools.codegen.CodegenProperty;
23-
import org.openapitools.codegen.CodegenSecurity;
24-
import org.openapitools.codegen.SupportingFile;
21+
import org.apache.commons.lang3.StringUtils;
22+
import org.openapitools.codegen.*;
2523
import org.openapitools.codegen.meta.GeneratorMetadata;
2624
import org.openapitools.codegen.meta.Stability;
2725
import org.openapitools.codegen.utils.ModelUtils;
@@ -36,6 +34,7 @@
3634
public class GoClientExperimentalCodegen extends GoClientCodegen {
3735

3836
private static final Logger LOGGER = LoggerFactory.getLogger(GoClientExperimentalCodegen.class);
37+
protected String goImportAlias = "openapiclient";
3938

4039
public GoClientExperimentalCodegen() {
4140
super();
@@ -83,11 +82,22 @@ public void processOpts() {
8382

8483
// Generate the 'signing.py' module, but only if the 'HTTP signature' security scheme is specified in the OAS.
8584
Map<String, SecurityScheme> securitySchemeMap = openAPI != null ?
86-
(openAPI.getComponents() != null ? openAPI.getComponents().getSecuritySchemes() : null) : null;
85+
(openAPI.getComponents() != null ? openAPI.getComponents().getSecuritySchemes() : null) : null;
8786
List<CodegenSecurity> authMethods = fromSecurity(securitySchemeMap);
8887
if (ProcessUtils.hasHttpSignatureMethods(authMethods)) {
8988
supportingFiles.add(new SupportingFile("signing.mustache", "", "signing.go"));
9089
}
90+
91+
if (additionalProperties.containsKey("goImportAlias")) {
92+
setGoImportAlias(additionalProperties.get("goImportAlias").toString());
93+
} else {
94+
additionalProperties.put("goImportAlias", goImportAlias);
95+
}
96+
97+
}
98+
99+
public void setGoImportAlias(String goImportAlias) {
100+
this.goImportAlias = goImportAlias;
91101
}
92102

93103
@Override
@@ -180,7 +190,7 @@ public Map<String, Object> postProcessModels(Map<String, Object> objs) {
180190
param.dataType = "NullableTime";
181191
} else {
182192
param.dataType = "Nullable" + Character.toUpperCase(param.dataType.charAt(0))
183-
+ param.dataType.substring(1);
193+
+ param.dataType.substring(1);
184194
}
185195
}
186196
}
@@ -199,4 +209,151 @@ public void addImportsToOneOfInterface(List<Map<String, String>> imports) {
199209
}
200210
}
201211
}
212+
213+
@Override
214+
public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> objs, List<Object> allModels) {
215+
objs = super.postProcessOperationsWithModels(objs, allModels);
216+
Map<String, Object> operations = (Map<String, Object>) objs.get("operations");
217+
HashMap<String, CodegenModel> modelMaps = new HashMap<String, CodegenModel>();
218+
HashMap<String, Integer> processedModelMaps = new HashMap<String, Integer>();
219+
220+
for (Object o : allModels) {
221+
HashMap<String, Object> h = (HashMap<String, Object>) o;
222+
CodegenModel m = (CodegenModel) h.get("model");
223+
modelMaps.put(m.classname, m);
224+
}
225+
226+
List<CodegenOperation> operationList = (List<CodegenOperation>) operations.get("operation");
227+
for (CodegenOperation op : operationList) {
228+
for (CodegenParameter p : op.allParams) {
229+
p.vendorExtensions.put("x-go-example", constructExampleCode(p, modelMaps, processedModelMaps));
230+
}
231+
}
232+
233+
processedModelMaps.clear();
234+
for (CodegenOperation operation : operationList) {
235+
for (CodegenParameter cp : operation.allParams) {
236+
cp.vendorExtensions.put("x-go-example", constructExampleCode(cp, modelMaps, processedModelMaps));
237+
}
238+
}
239+
240+
return objs;
241+
}
242+
243+
private String constructExampleCode(CodegenParameter codegenParameter, HashMap<String, CodegenModel> modelMaps, HashMap<String, Integer> processedModelMap) {
244+
if (codegenParameter.isListContainer) { // array
245+
return codegenParameter.dataType + "{" + constructExampleCode(codegenParameter.items, modelMaps, processedModelMap) + "}";
246+
} else if (codegenParameter.isMapContainer) {
247+
return "map[string]string{ \"Key\" = \"Value\" }";
248+
} else if (codegenParameter.isPrimitiveType) { // primitive type
249+
if (codegenParameter.isString) {
250+
if (StringUtils.isEmpty(codegenParameter.example)) {
251+
return "\"" + codegenParameter.example + "\"";
252+
} else {
253+
return "\"" + codegenParameter.paramName + "_example\"";
254+
}
255+
} else if (codegenParameter.isBoolean) { // boolean
256+
if (Boolean.parseBoolean(codegenParameter.example)) {
257+
return "true";
258+
} else {
259+
return "false";
260+
}
261+
} else if (codegenParameter.isUri) { // URL
262+
return "URL(string: \"https://example.com\")!";
263+
} else if (codegenParameter.isDateTime || codegenParameter.isDate) { // datetime or date
264+
return "Get-Date";
265+
} else{ // numeric
266+
if (StringUtils.isEmpty(codegenParameter.example)) {
267+
return codegenParameter.example;
268+
} else {
269+
return "987";
270+
}
271+
}
272+
} else { // model
273+
// look up the model
274+
if (modelMaps.containsKey(codegenParameter.dataType)) {
275+
return constructExampleCode(modelMaps.get(codegenParameter.dataType), modelMaps, processedModelMap);
276+
} else {
277+
//LOGGER.error("Error in constructing examples. Failed to look up the model " + codegenParameter.dataType);
278+
return "TODO";
279+
}
280+
}
281+
}
282+
283+
private String constructExampleCode(CodegenProperty codegenProperty, HashMap<String, CodegenModel> modelMaps, HashMap<String, Integer> processedModelMap) {
284+
if (codegenProperty.isListContainer) { // array
285+
return codegenProperty.dataType + "{" + constructExampleCode(codegenProperty.items, modelMaps, processedModelMap) + ")";
286+
} else if (codegenProperty.isMapContainer) { // map
287+
return "map[string]string{ \"Key\" = \"Value\" }";
288+
} else if (codegenProperty.isPrimitiveType) { // primitive type
289+
if (codegenProperty.isString) {
290+
if (StringUtils.isEmpty(codegenProperty.example)) {
291+
return "\"" + codegenProperty.example + "\"";
292+
} else {
293+
return "\"" + codegenProperty.name + "_example\"";
294+
}
295+
} else if (codegenProperty.isBoolean) { // boolean
296+
if (Boolean.parseBoolean(codegenProperty.example)) {
297+
return "true";
298+
} else {
299+
return "false";
300+
}
301+
} else if (codegenProperty.isUri) { // URL
302+
return "\"https://example.com\")!";
303+
} else if (codegenProperty.isDateTime || codegenProperty.isDate) { // datetime or date
304+
return "time.Now()";
305+
} else{ // numeric
306+
String example;
307+
if (StringUtils.isEmpty(codegenProperty.example)) {
308+
example = codegenProperty.example;
309+
} else {
310+
example = "123";
311+
}
312+
313+
if (codegenProperty.isLong) {
314+
return "int64(" + example + ")";
315+
} else {
316+
return example;
317+
}
318+
}
319+
} else {
320+
// look up the model
321+
if (modelMaps.containsKey(codegenProperty.dataType)) {
322+
return constructExampleCode(modelMaps.get(codegenProperty.dataType), modelMaps, processedModelMap);
323+
} else {
324+
//LOGGER.error("Error in constructing examples. Failed to look up the model " + codegenProperty.dataType);
325+
return "\"TODO\"";
326+
}
327+
}
328+
}
329+
330+
private String constructExampleCode(CodegenModel codegenModel, HashMap<String, CodegenModel> modelMaps, HashMap<String, Integer> processedModelMap) {
331+
String example;
332+
333+
// break infinite recursion. Return, in case a model is already processed in the current context.
334+
String model = codegenModel.name;
335+
if (processedModelMap.containsKey(model)) {
336+
int count = processedModelMap.get(model);
337+
if (count == 1) {
338+
processedModelMap.put(model, 2);
339+
} else if (count == 2) {
340+
return "";
341+
} else {
342+
throw new RuntimeException("Invalid count when constructing example: " + count);
343+
}
344+
} else {
345+
processedModelMap.put(model, 1);
346+
}
347+
348+
example = "" + goImportAlias + "." + codegenModel.name + "{";
349+
List<String> propertyExamples = new ArrayList<>();
350+
for (CodegenProperty codegenProperty : codegenModel.allVars) {
351+
propertyExamples.add(codegenProperty.name + ": " + constructExampleCode(codegenProperty, modelMaps, processedModelMap));
352+
}
353+
example += StringUtils.join(propertyExamples, ", ");
354+
example += "}";
355+
return example;
356+
}
357+
358+
202359
}

modules/openapi-generator/src/main/resources/go-experimental/api_doc.mustache

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,37 @@ Method | HTTP request | Description
2020

2121
{{{unespacedNotes}}}{{/notes}}
2222

23+
### Example
24+
25+
```go
26+
package main
27+
28+
import (
29+
"context"
30+
"fmt"
31+
"os"
32+
{{goImportAlias}} "./openapi"
33+
)
34+
35+
func main() {
36+
{{#allParams}}
37+
{{paramName}} := {{{vendorExtensions.x-go-example}}} // {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}
38+
{{/allParams}}
39+
40+
configuration := {{goImportAlias}}.NewConfiguration()
41+
api_client := {{goImportAlias}}.NewAPIClient(configuration)
42+
resp, r, err := api_client.{{classname}}.{{operationId}}(context.Background(), {{#requiredParams}}{{paramName}}{{^-last}}, {{/-last}}{{/requiredParams}}){{#optionalParams}}.{{{vendorExtensions.x-export-param-name}}}({{{paramName}}}){{/optionalParams}}.Execute()
43+
if err != nil {
44+
fmt.Fprintf(os.Stderr, "Error when calling `{{classname}}.{{operationId}}``: %v\n", err)
45+
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
46+
}
47+
{{#returnType}}
48+
// response from `{{operationId}}`: {{{.}}}
49+
fmt.Fprintf(os.Stdout, "Response from `{{classname}}.{{operationId}}`: %v\n", resp)
50+
{{/returnType}}
51+
}
52+
```
53+
2354
### Path Parameters
2455

2556
{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#pathParams}}{{#-last}}

samples/client/petstore/go-experimental/go-petstore/docs/AnotherFakeApi.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,33 @@ To test special tags
1616

1717

1818

19+
### Example
20+
21+
```go
22+
package main
23+
24+
import (
25+
"context"
26+
"fmt"
27+
"os"
28+
openapiclient "./openapi"
29+
)
30+
31+
func main() {
32+
body := openapiclient.Client{Client: "Client_example"} // Client | client model
33+
34+
configuration := openapiclient.NewConfiguration()
35+
api_client := openapiclient.NewAPIClient(configuration)
36+
resp, r, err := api_client.AnotherFakeApi.Call123TestSpecialTags(context.Background(), body).Execute()
37+
if err != nil {
38+
fmt.Fprintf(os.Stderr, "Error when calling `AnotherFakeApi.Call123TestSpecialTags``: %v\n", err)
39+
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
40+
}
41+
// response from `Call123TestSpecialTags`: Client
42+
fmt.Fprintf(os.Stdout, "Response from `AnotherFakeApi.Call123TestSpecialTags`: %v\n", resp)
43+
}
44+
```
45+
1946
### Path Parameters
2047

2148

0 commit comments

Comments
 (0)