Skip to content

Commit

Permalink
feat!: add support for nullable and optional types in go (asyncapi#1939)
Browse files Browse the repository at this point in the history
  • Loading branch information
Souvikns authored and jonaslagoni committed Apr 29, 2024
1 parent f68c879 commit d2fa78a
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 10 deletions.
13 changes: 13 additions & 0 deletions docs/migrations/version-3-to-4.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,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
}
```
58 changes: 50 additions & 8 deletions src/generators/go/GoConstrainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
Expand All @@ -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(),
Expand Down
116 changes: 116 additions & 0 deletions test/generators/go/GoConstrainer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ConstrainedFloatModel,
ConstrainedIntegerModel,
ConstrainedObjectModel,
ConstrainedObjectPropertyModel,
ConstrainedReferenceModel,
ConstrainedStringModel,
ConstrainedTupleModel,
Expand Down Expand Up @@ -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', () => {
Expand All @@ -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', () => {
Expand All @@ -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', () => {
Expand All @@ -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', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/generators/go/GoGenerator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
Expand Down
2 changes: 1 addition & 1 deletion test/generators/go/__snapshots__/GoGenerator.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit d2fa78a

Please sign in to comment.