Skip to content
Closed
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 @@ -92,8 +92,8 @@ public AbstractGoCodegen() {
"complex128",
"rune",
"byte",
"map[string]interface{}",
"interface{}"
"ObjectType",
"AnyType"
)
);

Expand Down Expand Up @@ -128,9 +128,15 @@ public AbstractGoCodegen() {
// map[string]interface{}, whereas an arbitrary type is implemented
// in golang as interface{}.
// See issue #5387 for more details.
typeMapping.put("object", "map[string]interface{}");
typeMapping.put("interface{}", "interface{}");
typeMapping.put("AnyType", "interface{}");
typeMapping.put("object", "ObjectType");

@wing328 wing328 May 8, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

// A schema that does not specify the 'type' attribute. The value
// can be anything.
typeMapping.put("AnyType", "AnyType");
// Below is the entry to map the JSON 'null' type to golang.
// Note there is no built-in 'null' type in golang, and it's not possible
// to declare a variable whose only possible value is 'nil'.
// For example `a := nil` is not a valid go statement.
typeMapping.put("null", "NullType");

numberTypes = new HashSet<String>(
Arrays.asList(
Expand Down Expand Up @@ -391,7 +397,7 @@ public String getSchemaType(Schema p) {
type = openAPIType;
} else if ("object".equals(openAPIType) && ModelUtils.isAnyTypeSchema(p)) {
// Arbitrary type. Note this is not the same thing as free-form object.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should remove/revise this comment (not added by you) as it's not entirely true.

type = "interface{}";
type = "AnyType";
} else if (typeMapping.containsKey(openAPIType)) {
type = typeMapping.get(openAPIType);
if (languageSpecificPrimitives.contains(type))
Expand Down Expand Up @@ -598,14 +604,58 @@ private void setExportParameterName(List<CodegenParameter> codegenParameters) {
}
}

/**
* OAS type mapping to golang implementation:
*
* AnyType interface{} Any type, e.g. null, string, integer, number, boolean, map, array.
* ObjectType map[string]interface{} Free-form object with no constraint on additional properties.
* map[string]Animal map[string]Animal Free-form object. The value of undeclared properties must be 'Animal'.
* map[string]ObjectType map[string]map[string]interface{} Free-form object. The value of undeclared properties must be an object (map).
* map[string][]interface{} Free-form object. The value of undeclared properties must be an array.
*
*/
@Override
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
// The 'go-experimental/model.mustache' template conditionally generates accessor methods.
// For primitive types and custom types (e.g. interface{}, map[string]interface{}...),
// the generated code has a wrapper type and a Get() function to access the underlying type.
// For containers (e.g. Array, Map), the generated code returns the type directly.
if (property.isContainer || property.isFreeFormObject || property.isAnyType) {
property.vendorExtensions.put("x-golang-is-container", true);
// The 'go-experimental/model.mustache' template generates accessor methods.
// For containers (e.g. Array, Map) and non-nullable primitive types, the go struct field
// is the underlying type, e.g. *int32, *string, *bool...
// In other cases (nullable primitive types, free-form objects...), the generated code has
// a wrapper type and a Get() function to access the underlying type.
boolean hasWrapper = false;
if (property.isAnyType) {
// The wrapper type is the 'AnyType' struct and the wrapped type is 'interface{}'.
hasWrapper = true;
} else if (property.isFreeFormObject && !property.isNullable) { // &&
// Undeclared properties use case.
// The undeclared properties are implemented using a go map.
// The map key is always a string.
// The map value is interface{} when there are no constraints, but the
// 'additionalProperties' attribute may require a specific type.
if (property.items == null) {
// The 'additionalProperties' attribute is not set, so it is a free-form object
// with no constraint on the type of undeclared properties.
// The wrapper type is 'ObjectType'.
// The wrapped type is 'map[string]interface{}'.
hasWrapper = true;
} else if (property.items.isFreeFormObject && !property.items.isNullable) {
// The 'additionalProperties' attribute is set, and the value of the undeclared
// properties must be a free-form object. Hence it's a map of map.
// The wrapper type is map[string]ObjectType.
// The wrapped type is 'map[string]map[string]interface{}'.
hasWrapper = true;
} else {
// The 'additionalProperties' attribute is set, and the value of the undeclared
// properties is not free-form. For example, if the undeclared properties must
// be of type 'Animal', then:
// The wrapper type is map[string]Animal.
// The wrapped type is 'map[string]Animal'.
}
} else if (property.isNullable && !property.isContainer) {
// Primitive types.
hasWrapper = true;
}
if (hasWrapper) {
property.vendorExtensions.put("x-golang-has-wrapper", true);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,37 @@ public Map<String, Object> postProcessModels(Map<String, Object> objs) {
}

for (CodegenProperty param : model.vars) {
// Two golang types are used in the generated code: the type of the
// struct fields and the types of the getter and setter methods.
// The struct fields may use a wrapper type (such as 'NullableBool'),
// whereas the getter method returns the unwrapped type (e.g. 'bool').
// param.dataType is the golang type of the generated struct field.
// "x-go-base-type" is the type in the setter and getter functions.
param.vendorExtensions.put("x-go-base-type", param.dataType);
if (!param.isNullable || param.isMapContainer || param.isListContainer ||
param.isFreeFormObject || param.isAnyType) {
if (!param.vendorExtensions.containsKey("x-golang-has-wrapper")) {
continue;
}
if (param.isAnyType) {
param.vendorExtensions.put("x-go-base-type", "interface{}");
continue;
} else if (param.isFreeFormObject) {
switch (param.dataType) {
case "ObjectType":
param.vendorExtensions.put("x-go-base-type", "map[string]interface{}");
continue;
case "[]ObjectType":
param.vendorExtensions.put("x-go-base-type", "map[string][]interface{}");
continue;
case "map[string]ObjectType":
param.vendorExtensions.put("x-go-base-type", "map[string]map[string]interface{}");
continue;
}
}
/*
if (!param.isNullable || param.isMapContainer || param.isListContainer) {
continue;
}
*/
if (param.isDateTime) {
// Note this could have been done by adding the following line in processOpts(),
// however, we only want to represent the DateTime object as NullableTime if
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,16 @@ func New{{classname}}({{#vars}}{{#required}}{{nameInCamelCase}} {{dataType}}, {{
{{/required}}
{{^required}}
{{#defaultValue}}
{{^vendorExtensions.x-golang-is-container}}
{{#isNullable}}
{{#vendorExtensions.x-golang-has-wrapper}}
var {{nameInCamelCase}} {{{datatypeWithEnum}}} = {{{.}}}
this.{{name}} = *New{{{dataType}}}(&{{nameInCamelCase}})
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
{{^isNullable}}
var {{nameInCamelCase}} {{{dataType}}} = {{{.}}}
this.{{name}} = &{{nameInCamelCase}}
{{/isNullable}}
{{/vendorExtensions.x-golang-is-container}}
{{/defaultValue}}
{{/required}}
{{/vars}}
Expand All @@ -92,17 +92,17 @@ func New{{classname}}WithDefaults() *{{classname}} {
this := {{classname}}{}
{{#vars}}
{{#defaultValue}}
{{^vendorExtensions.x-golang-is-container}}
{{#isNullable}}
{{#vendorExtensions.x-golang-has-wrapper}}
{{!we use datatypeWithEnum here, since it will represent the non-nullable name of the datatype, e.g. int64 for NullableInt64}}
var {{nameInCamelCase}} {{{datatypeWithEnum}}} = {{{.}}}
this.{{name}} = *New{{{dataType}}}(&{{nameInCamelCase}})
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
{{^isNullable}}
var {{nameInCamelCase}} {{{dataType}}} = {{{.}}}
this.{{name}} = {{^required}}&{{/required}}{{nameInCamelCase}}
{{/isNullable}}
{{/vendorExtensions.x-golang-is-container}}
{{/defaultValue}}
{{/vars}}
return &this
Expand All @@ -115,21 +115,26 @@ func New{{classname}}WithDefaults() *{{classname}} {
// If the value is explicit nil, the zero value for {{vendorExtensions.x-go-base-type}} will be returned
{{/isNullable}}
func (o *{{classname}}) Get{{name}}() {{vendorExtensions.x-go-base-type}} {
if o == nil {{#isNullable}}{{^vendorExtensions.x-golang-is-container}}|| o.{{name}}.Get() == nil{{/vendorExtensions.x-golang-is-container}}{{/isNullable}} {
if o == nil {{#isNullable}}{{#vendorExtensions.x-golang-has-wrapper}}|| o.{{name}}.Get() == nil{{/vendorExtensions.x-golang-has-wrapper}}{{/isNullable}} {
var ret {{vendorExtensions.x-go-base-type}}
return ret
}

{{#isNullable}}
{{#vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-has-wrapper}}
return o.{{name}}
{{/vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{#vendorExtensions.x-golang-has-wrapper}}
return *o.{{name}}.Get()
{{/vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
{{^isNullable}}
{{#vendorExtensions.x-golang-has-wrapper}}
return o.{{name}}.Get()
{{/vendorExtensions.x-golang-has-wrapper}}
{{^vendorExtensions.x-golang-has-wrapper}}
return o.{{name}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
}

Expand All @@ -139,31 +144,36 @@ func (o *{{classname}}) Get{{name}}() {{vendorExtensions.x-go-base-type}} {
// NOTE: If the value is an explicit nil, `nil, true` will be returned
{{/isNullable}}
func (o *{{classname}}) Get{{name}}Ok() (*{{vendorExtensions.x-go-base-type}}, bool) {
if o == nil {{#isNullable}}{{#vendorExtensions.x-golang-is-container}}|| o.{{name}} == nil{{/vendorExtensions.x-golang-is-container}}{{/isNullable}} {
if o == nil {{#isNullable}}{{^vendorExtensions.x-golang-has-wrapper}}|| o.{{name}} == nil{{/vendorExtensions.x-golang-has-wrapper}}{{/isNullable}} {
return nil, false
}
{{#isNullable}}
{{#vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-has-wrapper}}
return &o.{{name}}, true
{{/vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{#vendorExtensions.x-golang-has-wrapper}}
return o.{{name}}.Get(), o.{{name}}.IsSet()
{{/vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
{{^isNullable}}
{{#vendorExtensions.x-golang-has-wrapper}}
return &o.{{name}}.Get(), true
{{/vendorExtensions.x-golang-has-wrapper}}
{{^vendorExtensions.x-golang-has-wrapper}}
return &o.{{name}}, true
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
}

// Set{{name}} sets field value
func (o *{{classname}}) Set{{name}}(v {{vendorExtensions.x-go-base-type}}) {
{{#isNullable}}
{{#vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-has-wrapper}}
o.{{name}} = v
{{/vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{#vendorExtensions.x-golang-has-wrapper}}
o.{{name}}.Set(&v)
{{/vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
{{^isNullable}}
o.{{name}} = v
Expand All @@ -174,20 +184,25 @@ func (o *{{classname}}) Set{{name}}(v {{vendorExtensions.x-go-base-type}}) {
{{^required}}
// Get{{name}} returns the {{name}} field value if set, zero value otherwise{{#isNullable}} (both if not set or set to explicit null){{/isNullable}}.
func (o *{{classname}}) Get{{name}}() {{vendorExtensions.x-go-base-type}} {
if o == nil {{^isNullable}}|| o.{{name}} == nil{{/isNullable}}{{#isNullable}}{{^vendorExtensions.x-golang-is-container}}|| o.{{name}}.Get() == nil{{/vendorExtensions.x-golang-is-container}}{{/isNullable}} {
if o == nil {{^isNullable}}|| o.{{name}} == nil{{/isNullable}}{{#isNullable}}{{#vendorExtensions.x-golang-has-wrapper}}|| o.{{name}}.Get() == nil{{/vendorExtensions.x-golang-has-wrapper}}{{/isNullable}} {
var ret {{vendorExtensions.x-go-base-type}}
return ret
}
{{#isNullable}}
{{#vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-has-wrapper}}
return o.{{name}}
{{/vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{#vendorExtensions.x-golang-has-wrapper}}
return *o.{{name}}.Get()
{{/vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
{{^isNullable}}
{{#vendorExtensions.x-golang-has-wrapper}}
return *o.{{name}}.Get()
{{/vendorExtensions.x-golang-has-wrapper}}
{{^vendorExtensions.x-golang-has-wrapper}}
return *o.{{name}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
}

Expand All @@ -197,25 +212,30 @@ func (o *{{classname}}) Get{{name}}() {{vendorExtensions.x-go-base-type}} {
// NOTE: If the value is an explicit nil, `nil, true` will be returned
{{/isNullable}}
func (o *{{classname}}) Get{{name}}Ok() (*{{vendorExtensions.x-go-base-type}}, bool) {
if o == nil {{^isNullable}}|| o.{{name}} == nil{{/isNullable}}{{#isNullable}}{{#vendorExtensions.x-golang-is-container}}|| o.{{name}} == nil{{/vendorExtensions.x-golang-is-container}}{{/isNullable}} {
if o == nil {{^isNullable}}|| o.{{name}} == nil{{/isNullable}}{{#isNullable}}{{^vendorExtensions.x-golang-has-wrapper}}|| o.{{name}} == nil{{/vendorExtensions.x-golang-has-wrapper}}{{/isNullable}} {
return nil, false
}
{{#isNullable}}
{{#vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-has-wrapper}}
return &o.{{name}}, true
{{/vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{#vendorExtensions.x-golang-has-wrapper}}
return o.{{name}}.Get(), o.{{name}}.IsSet()
{{/vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
{{^isNullable}}
{{#vendorExtensions.x-golang-has-wrapper}}
return o.{{name}}.Get(), true
{{/vendorExtensions.x-golang-has-wrapper}}
{{^vendorExtensions.x-golang-has-wrapper}}
return o.{{name}}, true
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
}

// Has{{name}} returns a boolean if a field has been set.
func (o *{{classname}}) Has{{name}}() bool {
if o != nil && {{^isNullable}}o.{{name}} != nil{{/isNullable}}{{#isNullable}}{{#vendorExtensions.x-golang-is-container}}o.{{name}} != nil{{/vendorExtensions.x-golang-is-container}}{{^vendorExtensions.x-golang-is-container}}o.{{name}}.IsSet(){{/vendorExtensions.x-golang-is-container}}{{/isNullable}} {
if o != nil && {{^isNullable}}o.{{name}} != nil{{/isNullable}}{{#isNullable}}{{^vendorExtensions.x-golang-has-wrapper}}o.{{name}} != nil{{/vendorExtensions.x-golang-has-wrapper}}{{#vendorExtensions.x-golang-has-wrapper}}o.{{name}}.IsSet(){{/vendorExtensions.x-golang-has-wrapper}}{{/isNullable}} {
return true
}

Expand All @@ -225,19 +245,24 @@ func (o *{{classname}}) Has{{name}}() bool {
// Set{{name}} gets a reference to the given {{dataType}} and assigns it to the {{name}} field.
func (o *{{classname}}) Set{{name}}(v {{vendorExtensions.x-go-base-type}}) {
{{#isNullable}}
{{#vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-has-wrapper}}
o.{{name}} = v
{{/vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{#vendorExtensions.x-golang-has-wrapper}}
o.{{name}}.Set(&v)
{{/vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
{{^isNullable}}
{{#vendorExtensions.x-golang-has-wrapper}}
o.{{name}}.Set(&v)
{{/vendorExtensions.x-golang-has-wrapper}}
{{^vendorExtensions.x-golang-has-wrapper}}
o.{{name}} = &v
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
}
{{#isNullable}}
{{^vendorExtensions.x-golang-is-container}}
{{#vendorExtensions.x-golang-has-wrapper}}
// Set{{name}}Nil sets the value for {{name}} to be an explicit nil
func (o *{{classname}}) Set{{name}}Nil() {
o.{{name}}.Set(nil)
Expand All @@ -247,7 +272,7 @@ func (o *{{classname}}) Set{{name}}Nil() {
func (o *{{classname}}) Unset{{name}}() {
o.{{name}}.Unset()
}
{{/vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}

{{/required}}
Expand All @@ -269,17 +294,17 @@ func (o {{classname}}) MarshalJSON() ([]byte, error) {
{{#vars}}
{{! if argument is nullable, only serialize it if it is set}}
{{#isNullable}}
{{#vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-has-wrapper}}
{{! support for container fields is not ideal at this point because of lack of Nullable* types}}
if o.{{name}} != nil {
toSerialize["{{baseName}}"] = o.{{name}}
}
{{/vendorExtensions.x-golang-is-container}}
{{^vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{#vendorExtensions.x-golang-has-wrapper}}
if {{#required}}true{{/required}}{{^required}}o.{{name}}.IsSet(){{/required}} {
toSerialize["{{baseName}}"] = o.{{name}}.Get()
}
{{/vendorExtensions.x-golang-is-container}}
{{/vendorExtensions.x-golang-has-wrapper}}
{{/isNullable}}
{{! if argument is not nullable, don't set it if it is nil}}
{{^isNullable}}
Expand Down Expand Up @@ -326,6 +351,7 @@ func (s *{{classname}}) UnmarshalJSON(src []byte) error {
{{/discriminator}}
{{^discriminator}}
{{#oneOf}}
{{! TODO: validate the payload against the JSON schema. Relying on golang JSON unmarshaling is not sufficient to determine the payload validates against the schema}}
var unmarshaled{{{.}}} *{{{.}}} = &{{{.}}}{}
err = json.Unmarshal(src, unmarshaled{{{.}}})
if err == nil {
Expand Down
Loading