Skip to content

[Go] Rewrite of Go Client, Server generator #5037

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 8 commits into from
Apr 21, 2017
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 1 addition & 1 deletion bin/go-petstore-server.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ fi

# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
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 "
ags="$@ generate -t modules/swagger-codegen/src/main/resources/go-server -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l go-server -o samples/server/petstore/go-api-server -DpackageName=petstoreserver "

java $JAVA_OPTS -Dservice -jar $executable $ags
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public GoClientCodegen() {
typeMapping.put("boolean", "bool");
typeMapping.put("string", "string");
typeMapping.put("UUID", "string");
typeMapping.put("date", "time.Time");
typeMapping.put("date", "SwaggerDateType");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@antihax Shall we keep time.time so that the auto-generated code at least compile?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did it fail to compile with the template? time.Time used in this manner will cause a runtime error.

There may be a better way to handle it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about mapping it as "string" instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a possibility.

I have an app in production using the date type so it should help determine any issues that arise from using string.

I will test that tonight and convert if needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String worked with minimal changes for the user. It can be converted by using
dateObject, err := time.Parse("2006-01-02", dateString)

typeMapping.put("DateTime", "time.Time");
typeMapping.put("password", "string");
typeMapping.put("File", "*os.File");
Expand Down Expand Up @@ -273,8 +273,6 @@ public String toApiFilename(String name) {
return underscore(name) + "_api";
}



/**
* Overrides postProcessParameter to add a vendor extension "x-exportParamName".
* This is useful when paramName starts with a lowercase letter, but we need that
Expand Down Expand Up @@ -302,7 +300,6 @@ public void postProcessParameter(CodegenParameter parameter){
parameter.vendorExtensions.put("x-exportParamName", sb.toString());
}


@Override
public String apiDocFileFolder() {
return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar);
Expand Down Expand Up @@ -405,9 +402,10 @@ public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
if (_import.startsWith(apiPackage()))
iterator.remove();
}
// if the return type is not primitive, import encoding/json

// if their is a return type, import encoding/json
for (CodegenOperation operation : operations) {
if(operation.returnBaseType != null && needToImport(operation.returnBaseType)) {
if(operation.returnBaseType != null ) {
imports.add(createMapping("import", "encoding/json"));
break; //just need to import once
}
Expand All @@ -421,7 +419,6 @@ public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
}
}


// recursively add import for mapping one type to multiple imports
List<Map<String, String>> recursiveImports = (List<Map<String, String>>) objs.get("imports");
if (recursiveImports == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
import io.swagger.codegen.*;
import io.swagger.models.*;
import io.swagger.util.Yaml;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.MapProperty;
import io.swagger.models.properties.Property;
import io.swagger.models.parameters.Parameter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -64,6 +69,11 @@ public GoServerCodegen() {
*/
setReservedWordsLowerCase(
Arrays.asList(
// data type
"string", "bool", "uint", "uint8", "uint16", "uint32", "uint64",
"int", "int8", "int16", "int32", "int64", "float32", "float64",
"complex64", "complex128", "rune", "byte", "uintptr",

"break", "default", "func", "interface", "select",
"case", "defer", "go", "map", "struct",
"chan", "else", "goto", "package", "switch",
Expand Down Expand Up @@ -108,7 +118,8 @@ public GoServerCodegen() {
typeMapping.put("double", "float64");
typeMapping.put("boolean", "bool");
typeMapping.put("string", "string");
typeMapping.put("date", "time.Time");
typeMapping.put("UUID", "string");
typeMapping.put("date", "SwaggerDateType");
typeMapping.put("DateTime", "time.Time");
typeMapping.put("password", "string");
typeMapping.put("File", "*os.File");
Expand All @@ -117,6 +128,7 @@ public GoServerCodegen() {
// the correct solution is to use []byte
typeMapping.put("binary", "string");
typeMapping.put("ByteArray", "string");
typeMapping.put("object", "interface{}");

importMapping = new HashMap<String, String>();
importMapping.put("time.Time", "time");
Expand Down Expand Up @@ -237,11 +249,11 @@ public String toOperationId(String operationId) {
@Override
public String toModelFilename(String name) {
if (!StringUtils.isEmpty(modelNamePrefix)) {
name = modelNamePrefix + "_" + name;
name = modelNamePrefix + name;
}

if (!StringUtils.isEmpty(modelNameSuffix)) {
name = name + "_" + modelNameSuffix;
name = name + modelNameSuffix;
}

name = sanitizeName(name);
Expand All @@ -252,7 +264,54 @@ public String toModelFilename(String name) {
name = "model_" + name; // e.g. return => ModelReturn (after camelize)
}

return underscore(name);
return camelize(name);
}

@Override
public String getTypeDeclaration(Property p) {
if(p instanceof ArrayProperty) {
ArrayProperty ap = (ArrayProperty) p;
Property inner = ap.getItems();
return "[]" + getTypeDeclaration(inner);
}
else if (p instanceof MapProperty) {
MapProperty mp = (MapProperty) p;
Property inner = mp.getAdditionalProperties();

return getSwaggerType(p) + "[string]" + getTypeDeclaration(inner);
}
//return super.getTypeDeclaration(p);

// Not using the supertype invocation, because we want to UpperCamelize
// the type.
String swaggerType = getSwaggerType(p);
if (typeMapping.containsKey(swaggerType)) {
return typeMapping.get(swaggerType);
}

if(typeMapping.containsValue(swaggerType)) {
return swaggerType;
}

if(languageSpecificPrimitives.contains(swaggerType)) {
return swaggerType;
}

return toModelName(swaggerType);
}

@Override
public String getSwaggerType(Property p) {
String swaggerType = super.getSwaggerType(p);
String type = null;
if(typeMapping.containsKey(swaggerType)) {
type = typeMapping.get(swaggerType);
if(languageSpecificPrimitives.contains(type))
return (type);
}
else
type = swaggerType;
return type;
}

@Override
Expand Down
40 changes: 32 additions & 8 deletions modules/swagger-codegen/src/main/resources/go/README.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,49 @@ Class | Method | HTTP request | Description
{{/model}}{{/models}}

## Documentation For Authorization

{{^authMethods}} All endpoints do not require authorization.
{{^authMethods}} Endpoints do not require authorization.
{{/authMethods}}{{#authMethods}}{{#last}} Authentication schemes defined for the API:{{/last}}{{/authMethods}}
{{#authMethods}}## {{{name}}}

{{#authMethods}}
## {{{name}}}
{{#isApiKey}}- **Type**: API key
- **API key parameter name**: {{{keyParamName}}}
- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}}
!!! NOT IMPLEMENTED !!!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@antihax so API key (via header or query parameter) is no longer supported?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is correct. I could not see in the original where it was supported as a reference of how to implement it.

I could take another look at some other implementations and try to determine. Is there a way to write tests for it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recall how I became confused here...

API Key in Query String: https://github.com/antihax/swagger-codegen/blob/New-GoClient-GoServer/samples/client/petstore/go/go-petstore/pet_api.go#L99

I will add the header equivalent tonight and alter the documentation.


{{/isApiKey}}
{{#isBasic}}- **Type**: HTTP basic authentication

Example
```
auth := context.WithValue(oauth2.NoContext, sw.ContextBasicAuth, sw.BasicAuth{
UserName: "username",
Password: "password",
})
r, err := client.Service.Operation(auth, args)
```
{{/isBasic}}
{{#isOAuth}}- **Type**: OAuth
- **Flow**: {{{flow}}}
- **Authorizatoin URL**: {{{authorizationUrl}}}
- **Authorization URL**: {{{authorizationUrl}}}
- **Scopes**: {{^scopes}}N/A{{/scopes}}
{{#scopes}} - **{{{scope}}}**: {{{description}}}
{{/scopes}}
{{/isOAuth}}

Example
```
auth := context.WithValue(oauth2.NoContext, sw.ContextAccessToken, "ACCESSTOKENSTRING")
r, err := client.Service.Operation(auth, args)
```

Or via OAuth2 module to automaticly refresh tokens and perform user authentication.
```
import "golang.org/x/oauth2"

/ .. Perform OAuth2 round trip request and obtain a token .. //

tokenSource := oauth2cfg.TokenSource(createContext(httpClient), &token)
auth := context.WithValue(oauth2.NoContext, sw.ContextOAuth2, tokenSource)
r, err := client.Service.Operation(auth, args)
```
{{/isOAuth}}
{{/authMethods}}

## Author
Expand Down
Loading