Skip to content

Commit 8943585

Browse files
authored
Merge pull request #2979 from guohuang/go_server
issue#2970, [Go] add go server codegen template
2 parents e875ac6 + e39aa3e commit 8943585

File tree

22 files changed

+890
-1
lines changed

22 files changed

+890
-1
lines changed

bin/go-petstore-server.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/bin/sh
2+
3+
SCRIPT="$0"
4+
5+
while [ -h "$SCRIPT" ] ; do
6+
ls=`ls -ld "$SCRIPT"`
7+
link=`expr "$ls" : '.*-> \(.*\)$'`
8+
if expr "$link" : '/.*' > /dev/null; then
9+
SCRIPT="$link"
10+
else
11+
SCRIPT=`dirname "$SCRIPT"`/"$link"
12+
fi
13+
done
14+
15+
if [ ! -d "${APP_DIR}" ]; then
16+
APP_DIR=`dirname "$SCRIPT"`/..
17+
APP_DIR=`cd "${APP_DIR}"; pwd`
18+
fi
19+
20+
executable="./modules/swagger-codegen-cli/target/swagger-codegen-cli.jar"
21+
22+
if [ ! -f "$executable" ]
23+
then
24+
mvn clean package
25+
fi
26+
27+
# if you've executed sbt assembly previously it will use that instead.
28+
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
29+
ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l go-server -o samples/server/petstore/go-api-server -DpackageName=petstoreserver "
30+
31+
java $JAVA_OPTS -Dservice -jar $executable $ags
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
package io.swagger.codegen.languages;
2+
3+
import com.fasterxml.jackson.core.JsonGenerator;
4+
import com.fasterxml.jackson.core.JsonProcessingException;
5+
import com.fasterxml.jackson.databind.JsonSerializer;
6+
import com.fasterxml.jackson.databind.SerializerProvider;
7+
import com.fasterxml.jackson.databind.module.SimpleModule;
8+
import com.google.common.collect.ArrayListMultimap;
9+
import com.google.common.collect.Lists;
10+
import com.google.common.collect.Multimap;
11+
import io.swagger.codegen.*;
12+
import io.swagger.models.*;
13+
import io.swagger.util.Yaml;
14+
import org.slf4j.Logger;
15+
import org.slf4j.LoggerFactory;
16+
17+
import java.io.File;
18+
import java.io.IOException;
19+
import java.math.BigDecimal;
20+
import java.util.*;
21+
import java.util.Map.Entry;
22+
import org.apache.commons.lang3.StringUtils;
23+
24+
public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
25+
26+
private static final Logger LOGGER = LoggerFactory.getLogger(GoServerCodegen.class);
27+
28+
protected String apiVersion = "1.0.0";
29+
protected int serverPort = 8080;
30+
protected String projectName = "swagger-server";
31+
protected String apiPath = "go";
32+
33+
public GoServerCodegen() {
34+
super();
35+
36+
// set the output folder here
37+
outputFolder = "generated-code/go";
38+
39+
/**
40+
* Models. You can write model files using the modelTemplateFiles map.
41+
* if you want to create one template for file, you can do so here.
42+
* for multiple files for model, just put another entry in the `modelTemplateFiles` with
43+
* a different extension
44+
*/
45+
modelTemplateFiles.clear();
46+
47+
/**
48+
* Api classes. You can write classes for each Api file with the apiTemplateFiles map.
49+
* as with models, add multiple entries with different extensions for multiple files per
50+
* class
51+
*/
52+
apiTemplateFiles.put(
53+
"controller.mustache", // the template to use
54+
".go"); // the extension for each file to write
55+
56+
/**
57+
* Template Location. This is the location which templates will be read from. The generator
58+
* will use the resource stream to attempt to read the templates.
59+
*/
60+
embeddedTemplateDir = templateDir = "go-server";
61+
62+
/**
63+
* Reserved words. Override this with reserved words specific to your language
64+
*/
65+
setReservedWordsLowerCase(
66+
Arrays.asList(
67+
"break", "default", "func", "interface", "select",
68+
"case", "defer", "go", "map", "struct",
69+
"chan", "else", "goto", "package", "switch",
70+
"const", "fallthrough", "if", "range", "type",
71+
"continue", "for", "import", "return", "var", "error", "ApiResponse")
72+
// Added "error" as it's used so frequently that it may as well be a keyword
73+
);
74+
75+
defaultIncludes = new HashSet<String>(
76+
Arrays.asList(
77+
"map",
78+
"array")
79+
);
80+
81+
languageSpecificPrimitives = new HashSet<String>(
82+
Arrays.asList(
83+
"string",
84+
"bool",
85+
"uint",
86+
"uint32",
87+
"uint64",
88+
"int",
89+
"int32",
90+
"int64",
91+
"float32",
92+
"float64",
93+
"complex64",
94+
"complex128",
95+
"rune",
96+
"byte")
97+
);
98+
99+
instantiationTypes.clear();
100+
/*instantiationTypes.put("array", "GoArray");
101+
instantiationTypes.put("map", "GoMap");*/
102+
103+
typeMapping.clear();
104+
typeMapping.put("integer", "int32");
105+
typeMapping.put("long", "int64");
106+
typeMapping.put("number", "float32");
107+
typeMapping.put("float", "float32");
108+
typeMapping.put("double", "float64");
109+
typeMapping.put("boolean", "bool");
110+
typeMapping.put("string", "string");
111+
typeMapping.put("date", "time.Time");
112+
typeMapping.put("DateTime", "time.Time");
113+
typeMapping.put("password", "string");
114+
typeMapping.put("File", "*os.File");
115+
typeMapping.put("file", "*os.File");
116+
// map binary to string as a workaround
117+
// the correct solution is to use []byte
118+
typeMapping.put("binary", "string");
119+
typeMapping.put("ByteArray", "string");
120+
121+
importMapping = new HashMap<String, String>();
122+
importMapping.put("time.Time", "time");
123+
importMapping.put("*os.File", "os");
124+
importMapping.put("os", "io/ioutil");
125+
126+
cliOptions.clear();
127+
cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Go package name (convention: lowercase).")
128+
.defaultValue("swagger"));
129+
/**
130+
* Additional Properties. These values can be passed to the templates and
131+
* are available in models, apis, and supporting files
132+
*/
133+
additionalProperties.put("apiVersion", apiVersion);
134+
additionalProperties.put("serverPort", serverPort);
135+
additionalProperties.put("apiPath", apiPath);
136+
/**
137+
* Supporting Files. You can write single files for the generator with the
138+
* entire object tree available. If the input file has a suffix of `.mustache
139+
* it will be processed by the template engine. Otherwise, it will be copied
140+
*/
141+
supportingFiles.add(new SupportingFile("swagger.mustache",
142+
"api",
143+
"swagger.yaml")
144+
);
145+
supportingFiles.add(new SupportingFile("main.mustache", "", "main.go"));
146+
supportingFiles.add(new SupportingFile("routers.mustache", apiPath, "routers.go"));
147+
supportingFiles.add(new SupportingFile("logger.mustache", apiPath, "logger.go"));
148+
supportingFiles.add(new SupportingFile("app.mustache", apiPath, "app.yaml"));
149+
writeOptional(outputFolder, new SupportingFile("README.mustache", apiPath, "README.md"));
150+
}
151+
152+
@Override
153+
public String apiPackage() {
154+
return apiPath;
155+
}
156+
157+
/**
158+
* Configures the type of generator.
159+
*
160+
* @return the CodegenType for this generator
161+
* @see io.swagger.codegen.CodegenType
162+
*/
163+
@Override
164+
public CodegenType getTag() {
165+
return CodegenType.SERVER;
166+
}
167+
168+
/**
169+
* Configures a friendly name for the generator. This will be used by the generator
170+
* to select the library with the -l flag.
171+
*
172+
* @return the friendly name for the generator
173+
*/
174+
@Override
175+
public String getName() {
176+
return "go-server";
177+
}
178+
179+
/**
180+
* Returns human-friendly help for the generator. Provide the consumer with help
181+
* tips, parameters here
182+
*
183+
* @return A string value for the help message
184+
*/
185+
@Override
186+
public String getHelp() {
187+
return "Generates a Go server library using the swagger-tools project. By default, " +
188+
"it will also generate service classes--which you can disable with the `-Dnoservice` environment variable.";
189+
}
190+
191+
@Override
192+
public String toApiName(String name) {
193+
if (name.length() == 0) {
194+
return "DefaultController";
195+
}
196+
return initialCaps(name);
197+
}
198+
199+
/**
200+
* Escapes a reserved word as defined in the `reservedWords` array. Handle escaping
201+
* those terms here. This logic is only called if a variable matches the reseved words
202+
*
203+
* @return the escaped term
204+
*/
205+
@Override
206+
public String escapeReservedWord(String name) {
207+
return "_" + name; // add an underscore to the name
208+
}
209+
210+
/**
211+
* Location to write api files. You can use the apiPackage() as defined when the class is
212+
* instantiated
213+
*/
214+
@Override
215+
public String apiFileFolder() {
216+
return outputFolder + File.separator + apiPackage().replace('.', File.separatorChar);
217+
}
218+
219+
@Override
220+
public String toModelName(String name) {
221+
// camelize the model name
222+
// phone_number => PhoneNumber
223+
return camelize(toModelFilename(name));
224+
}
225+
226+
@Override
227+
public String toOperationId(String operationId) {
228+
// method name cannot use reserved keyword, e.g. return
229+
if (isReservedWord(operationId)) {
230+
LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + camelize(sanitizeName("call_" + operationId)));
231+
operationId = "call_" + operationId;
232+
}
233+
234+
return camelize(operationId);
235+
}
236+
237+
@Override
238+
public String toModelFilename(String name) {
239+
if (!StringUtils.isEmpty(modelNamePrefix)) {
240+
name = modelNamePrefix + "_" + name;
241+
}
242+
243+
if (!StringUtils.isEmpty(modelNameSuffix)) {
244+
name = name + "_" + modelNameSuffix;
245+
}
246+
247+
name = sanitizeName(name);
248+
249+
// model name cannot use reserved keyword, e.g. return
250+
if (isReservedWord(name)) {
251+
LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + camelize("model_" + name));
252+
name = "model_" + name; // e.g. return => ModelReturn (after camelize)
253+
}
254+
255+
return underscore(name);
256+
}
257+
258+
@Override
259+
public String toApiFilename(String name) {
260+
// replace - with _ e.g. created-at => created_at
261+
name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
262+
263+
// e.g. PetApi.go => pet_api.go
264+
return underscore(name);
265+
}
266+
}

modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@ io.swagger.codegen.languages.CsharpDotNet2ClientCodegen
4747
io.swagger.codegen.languages.ClojureClientCodegen
4848
io.swagger.codegen.languages.HaskellServantCodegen
4949
io.swagger.codegen.languages.LumenServerCodegen
50+
io.swagger.codegen.languages.GoServerCodegen
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Go API Server for {{packageName}}
2+
3+
{{#appDescription}}
4+
{{{appDescription}}}
5+
{{/appDescription}}
6+
7+
## Overview
8+
This server was generated by the [swagger-codegen]
9+
(https://github.com/swagger-api/swagger-codegen) project.
10+
By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub.
11+
-
12+
13+
To see how to make this your own, look here:
14+
15+
[README](https://github.com/swagger-api/swagger-codegen/blob/master/README.md)
16+
17+
- API version: {{appVersion}}
18+
- Build date: {{generatedDate}}
19+
{{#infoUrl}}
20+
For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}})
21+
{{/infoUrl}}
22+
23+
24+
### Running the server
25+
To run the server, follow these simple steps:
26+
27+
```
28+
go run main.go
29+
```
30+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
application: {{packageName}}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package {{packageName}}
2+
3+
{{#operations}}
4+
import (
5+
"net/http"
6+
)
7+
8+
type {{classname}} struct {
9+
10+
}
11+
12+
{{#operation}}
13+
func {{nickname}}(w http.ResponseWriter, r *http.Request) {
14+
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
15+
w.WriteHeader(http.StatusOK)
16+
}
17+
18+
{{/operation}}
19+
{{/operations}}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package {{packageName}}
2+
3+
import (
4+
"log"
5+
"net/http"
6+
"time"
7+
)
8+
9+
func Logger(inner http.Handler, name string) http.Handler {
10+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
11+
start := time.Now()
12+
13+
inner.ServeHTTP(w, r)
14+
15+
log.Printf(
16+
"%s %s %s %s",
17+
r.Method,
18+
r.RequestURI,
19+
name,
20+
time.Since(start),
21+
)
22+
})
23+
}

0 commit comments

Comments
 (0)