diff --git a/docs/migrations/version-3-to-4.md b/docs/migrations/version-3-to-4.md index b8a29bfd97..5ab1163d9e 100644 --- a/docs/migrations/version-3-to-4.md +++ b/docs/migrations/version-3-to-4.md @@ -238,4 +238,17 @@ const generator = new GoGenerator({ unionArrModelName: 'ModelinaArrType', unionDictModelName: 'ModelinaDictType' }); +``` + +### Nullable and required properties + +Modelina now has support for nullable and required properties in go structs. This support exists for generic types like `int`, `string`, `bool`, `float64`. + +```go +type info struct { + name string // required + description *string // nullable + version *float64 + isDevelopment *bool +} ``` \ No newline at end of file diff --git a/src/generators/go/GoConstrainer.ts b/src/generators/go/GoConstrainer.ts index 9af9de4f2f..dbec4caa46 100644 --- a/src/generators/go/GoConstrainer.ts +++ b/src/generators/go/GoConstrainer.ts @@ -6,6 +6,10 @@ import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer' import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; import { defaultConstantConstraints } from './constrainer/ConstantConstrainer'; import { GoTypeMapping } from './GoGenerator'; +import { + ConstrainedMetaModel, + ConstrainedObjectPropertyModel +} from '../../models'; export const GoDefaultTypeMapping: GoTypeMapping = { Object({ constrainedModel }): string { @@ -17,17 +21,37 @@ export const GoDefaultTypeMapping: GoTypeMapping = { Any(): string { return 'interface{}'; }, - Float(): string { - return 'float64'; + Float({ constrainedModel, partOfProperty }): string { + return getType({ + constrainedModel, + partOfProperty, + typeWhenNullableOrOptional: '*float64', + type: 'float64' + }); }, - Integer(): string { - return 'int'; + Integer({ constrainedModel, partOfProperty }): string { + return getType({ + constrainedModel, + partOfProperty, + typeWhenNullableOrOptional: '*int', + type: 'int' + }); }, - String(): string { - return 'string'; + String({ constrainedModel, partOfProperty }): string { + return getType({ + constrainedModel, + partOfProperty, + typeWhenNullableOrOptional: '*string', + type: 'string' + }); }, - Boolean(): string { - return 'bool'; + Boolean({ constrainedModel, partOfProperty }): string { + return getType({ + constrainedModel, + partOfProperty, + typeWhenNullableOrOptional: '*bool', + type: 'bool' + }); }, Tuple(): string { //Because Go have no notion of tuples (and no custom implementation), we have to render it as a list of any value. @@ -48,6 +72,24 @@ export const GoDefaultTypeMapping: GoTypeMapping = { } }; +function getType({ + constrainedModel, + partOfProperty, + typeWhenNullableOrOptional, + type +}: { + constrainedModel: ConstrainedMetaModel; + partOfProperty: ConstrainedObjectPropertyModel | undefined; + typeWhenNullableOrOptional: string; + type: string; +}) { + const required = partOfProperty ? partOfProperty.required : false; + if (constrainedModel.options.isNullable && !required) { + return typeWhenNullableOrOptional; + } + return type; +} + export const GoDefaultConstraints = { enumKey: defaultEnumKeyConstraints(), enumValue: defaultEnumValueConstraints(), diff --git a/test/generators/go/GoConstrainer.spec.ts b/test/generators/go/GoConstrainer.spec.ts index 03813351a8..21692e0d87 100644 --- a/test/generators/go/GoConstrainer.spec.ts +++ b/test/generators/go/GoConstrainer.spec.ts @@ -8,6 +8,7 @@ import { ConstrainedFloatModel, ConstrainedIntegerModel, ConstrainedObjectModel, + ConstrainedObjectPropertyModel, ConstrainedReferenceModel, ConstrainedStringModel, ConstrainedTupleModel, @@ -66,6 +67,35 @@ describe('GoConstrainer', () => { }); expect(type).toEqual('float64'); }); + + test('nullable', () => { + const model = new ConstrainedFloatModel( + 'test', + undefined, + { isNullable: true }, + '' + ); + const type = GoDefaultTypeMapping.Float({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('*float64'); + }); + + test('requried', () => { + const model = new ConstrainedFloatModel('test', undefined, {}, ''); + const type = GoDefaultTypeMapping.Float({ + constrainedModel: model, + partOfProperty: new ConstrainedObjectPropertyModel( + 'object', + '', + true, + model + ), + ...defaultOptions + }); + expect(type).toEqual('float64'); + }); }); describe('Integer', () => { test('should render type', () => { @@ -76,6 +106,34 @@ describe('GoConstrainer', () => { }); expect(type).toEqual('int'); }); + + test('nullable', () => { + const model = new ConstrainedIntegerModel( + 'test', + undefined, + { isNullable: true }, + '' + ); + const type = GoDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('*int'); + }); + test('requried', () => { + const model = new ConstrainedIntegerModel('test', undefined, {}, ''); + const type = GoDefaultTypeMapping.Integer({ + constrainedModel: model, + partOfProperty: new ConstrainedObjectPropertyModel( + 'object', + '', + true, + model + ), + ...defaultOptions + }); + expect(type).toEqual('int'); + }); }); describe('String', () => { test('should render type', () => { @@ -86,6 +144,35 @@ describe('GoConstrainer', () => { }); expect(type).toEqual('string'); }); + + test('nullable', () => { + const model = new ConstrainedStringModel( + 'test', + undefined, + { isNullable: true }, + '' + ); + const type = GoDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('*string'); + }); + + test('requried', () => { + const model = new ConstrainedStringModel('test', undefined, {}, ''); + const type = GoDefaultTypeMapping.String({ + constrainedModel: model, + partOfProperty: new ConstrainedObjectPropertyModel( + 'object', + '', + true, + model + ), + ...defaultOptions + }); + expect(type).toEqual('string'); + }); }); describe('Boolean', () => { test('should render type', () => { @@ -96,6 +183,35 @@ describe('GoConstrainer', () => { }); expect(type).toEqual('bool'); }); + + test('nullable', () => { + const model = new ConstrainedBooleanModel( + 'test', + undefined, + { isNullable: true }, + '' + ); + const type = GoDefaultTypeMapping.Boolean({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('*bool'); + }); + + test('requried', () => { + const model = new ConstrainedBooleanModel('test', undefined, {}, ''); + const type = GoDefaultTypeMapping.Boolean({ + constrainedModel: model, + partOfProperty: new ConstrainedObjectPropertyModel( + 'object', + '', + true, + model + ), + ...defaultOptions + }); + expect(type).toEqual('bool'); + }); }); describe('Tuple', () => { diff --git a/test/generators/go/GoGenerator.spec.ts b/test/generators/go/GoGenerator.spec.ts index a5118328e9..cffcd8ccdf 100644 --- a/test/generators/go/GoGenerator.spec.ts +++ b/test/generators/go/GoGenerator.spec.ts @@ -168,7 +168,7 @@ describe('GoGenerator', () => { $id: 'Address', type: 'object', properties: { - street_name: { type: 'string' }, + street_name: [{ type: 'string' }, { type: 'null' }], city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, diff --git a/test/generators/go/__snapshots__/GoGenerator.spec.ts.snap b/test/generators/go/__snapshots__/GoGenerator.spec.ts.snap index efa72ec1a6..0dcb95d424 100644 --- a/test/generators/go/__snapshots__/GoGenerator.spec.ts.snap +++ b/test/generators/go/__snapshots__/GoGenerator.spec.ts.snap @@ -109,7 +109,7 @@ package some_package // Address represents a Address model. type Address struct { - StreetName string + StreetName interface{} City string State string HouseNumber float64