Skip to content

Commit

Permalink
Fix populating any type (#947)
Browse files Browse the repository at this point in the history
* Fix populating any type

Strcuts with fields of any type were always sending a JSON null as the
address of the field was never nil.
Reverted rawjson-as-bytes for some test cases to see and test changes.

* improve test

* further test refinement

* bump version for upcoming release
  • Loading branch information
jhendrixMSFT authored Apr 10, 2023
1 parent 7ab7dc3 commit 959ddb7
Show file tree
Hide file tree
Showing 13 changed files with 1,863 additions and 1,787 deletions.
4 changes: 2 additions & 2 deletions .scripts/regeneration.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const network = 'https://raw.githubusercontent.com/Azure/azure-rest-api-specs/22
generateFromReadme("armnetwork", network, 'package-2020-03', 'test/network/2020-03-01/armnetwork', '--module=armnetwork --azure-arm=true --remove-unreferenced-types');

const compute = 'https://raw.githubusercontent.com/Azure/azure-rest-api-specs/228cf296647f6e41182cee7d1a403990e6a8fe3c/specification/compute/resource-manager/readme.md';
generateFromReadme("armcompute", compute, 'package-2019-12-01', 'test/compute/2019-12-01/armcompute', '--module=armcompute --azure-arm=true --remove-unreferenced-types --rawjson-as-bytes');
generateFromReadme("armcompute", compute, 'package-2019-12-01', 'test/compute/2019-12-01/armcompute', '--module=armcompute --azure-arm=true --remove-unreferenced-types');

const synapseArtifacts = 'https://raw.githubusercontent.com/Azure/azure-rest-api-specs/228cf296647f6e41182cee7d1a403990e6a8fe3c/specification/synapse/data-plane/readme.md';
generateFromReadme("azartifacts", synapseArtifacts, 'package-artifacts-2019-06-01-preview', 'test/synapse/2019-06-01/azartifacts', '--security=AADToken --security-scopes="https://dev.azuresynapse.net/.default" --module="azartifacts" --openapi-type="data-plane"');
Expand All @@ -123,7 +123,7 @@ generateFromReadme("armdataboxedge", databoxedge, 'package-2021-02-01', 'test/da
const acr = 'https://github.com/Azure/azure-rest-api-specs/blob/195cd610db0accd0422c3e00a72df739ab4de677/specification/containerregistry/data-plane/Azure.ContainerRegistry/stable/2021-07-01/containerregistry.json';
generate("azacr", acr, 'test/acr/2021-07-01/azacr', '--module="azacr" --openapi-type="data-plane" --rawjson-as-bytes');

generate("azalias", 'test/swagger/alias.json', 'test/maps/azalias', '--security=AzureKey --module="azalias" --openapi-type="data-plane" --rawjson-as-bytes');
generate("azalias", 'test/swagger/alias.json', 'test/maps/azalias', '--security=AzureKey --module="azalias" --openapi-type="data-plane"');

function should_generate(name) {
if (filter !== undefined) {
Expand Down
21 changes: 17 additions & 4 deletions src/generator/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export async function generateModels(session: Session<CodeModel>): Promise<model
let needsJSONPopulate = false;
let needsJSONUnpopulate = false;
let needsJSONPopulateByteArray = false;
let needsJSONPopulateAny = false;
let serdeTextBody = '';
structs.sort((a: StructDef, b: StructDef) => { return sortAscending(a.Language.name, b.Language.name) });
for (const struct of values(structs)) {
Expand Down Expand Up @@ -65,6 +66,9 @@ export async function generateModels(session: Session<CodeModel>): Promise<model
if (struct.HasJSONByteArray) {
needsJSONPopulateByteArray = true;
}
if (struct.HasAny) {
needsJSONPopulateAny = true;
}
}
if (needsJSONPopulate) {
serdeTextBody += 'func populate(m map[string]any, k string, v any) {\n';
Expand All @@ -77,6 +81,17 @@ export async function generateModels(session: Session<CodeModel>): Promise<model
serdeTextBody += '\t}\n';
serdeTextBody += '}\n\n';
}
if (needsJSONPopulateAny) {
serdeTextBody += 'func populateAny(m map[string]any, k string, v any) {\n';
serdeTextBody += '\tif v == nil {\n';
serdeTextBody += '\t\treturn\n';
serdeTextBody += '\t} else if azcore.IsNullValue(v) {\n';
serdeTextBody += '\t\tm[k] = nil\n';
serdeTextBody += '\t} else {\n';
serdeTextBody += '\t\tm[k] = v\n';
serdeTextBody += '\t}\n';
serdeTextBody += '}\n\n';
}
if (needsJSONPopulateByteArray) {
serdeImports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime');
serdeTextBody += 'func populateByteArray(m map[string]any, k string, b []byte, f runtime.Base64Encoding) {\n';
Expand Down Expand Up @@ -262,14 +277,12 @@ function generateJSONMarshallerBody(obj: ObjectSchema, structDef: StructDef, imp
marshaller += `\tif ${receiver}.${prop.language.go!.name} == nil {\n\t\t${receiver}.${prop.language.go!.name} = to.Ptr(${getClientDefaultValue(prop)})\n\t}\n`;
}
let populate = 'populate';
let addr = '';
if (prop.schema.language.go!.internalTimeType) {
populate += capitalize(prop.schema.language.go!.internalTimeType);
} else if (prop.schema.type === SchemaType.Any) {
// for fields that are any we pass their address so populate() IsNil() doesn't panic
addr = '&';
populate += 'Any';
}
marshaller += `\t${populate}(objectMap, "${prop.serializedName}", ${addr}${receiver}.${prop.language.go!.name})\n`;
marshaller += `\t${populate}(objectMap, "${prop.serializedName}", ${receiver}.${prop.language.go!.name})\n`;
}
}
if (addlProps) {
Expand Down
4 changes: 4 additions & 0 deletions src/generator/structs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class StructDef {
readonly Methods: StructMethod[];
readonly ComposedOf: string[];
HasJSONByteArray: boolean;
HasAny: boolean;

constructor(language: Language, props?: Property[], params?: Parameter[]) {
this.Language = language;
Expand All @@ -41,6 +42,7 @@ export class StructDef {
this.Methods = new Array<StructMethod>();
this.ComposedOf = new Array<string>();
this.HasJSONByteArray = false;
this.HasAny = false;
}

text(): string {
Expand Down Expand Up @@ -167,6 +169,8 @@ export function generateStruct(imports: ImportManager, lang: Language, props?: P
imports.addImportForSchemaType(prop.schema);
if (prop.language.go!.embeddedType) {
st.ComposedOf.push(prop.schema.language.go!.name);
} else if (prop.schema.type === SchemaType.Any && !prop.schema.language.go!.rawJSONAsBytes) {
st.HasAny = true;
}
}
return st;
Expand Down
2 changes: 1 addition & 1 deletion src/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@autorest/go",
"version": "4.0.0-preview.47",
"version": "4.0.0-preview.48",
"description": "AutoRest Go Generator",
"main": "dist/exports.js",
"typings": "dist/exports.d.ts",
Expand Down
16 changes: 8 additions & 8 deletions test/compute/2019-12-01/armcompute/zz_models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 26 additions & 16 deletions test/compute/2019-12-01/armcompute/zz_models_serde.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 6 additions & 11 deletions test/maps/azalias/models_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,13 @@ func TestPolicyAssignmentProperties(t *testing.T) {
t.Fatal(err)
}
var s string
if err := json.Unmarshal(paprops.Parameters["effect"].Value, &s); err != nil {
t.Fatal(err)
}
s, ok := paprops.Parameters["effect"].Value.(string)
require.True(t, ok)
if s != "Audit" {
t.Fatalf("got %s, want Audit", s)
}
sl := []string{}
if err := json.Unmarshal(paprops.Parameters["listOfResourceTypesNotAllowed"].Value, &sl); err != nil {
t.Fatal(err)
}
sl, ok := paprops.Parameters["listOfResourceTypesNotAllowed"].Value.([]any)
require.True(t, ok)
if len(sl) != 1 {
t.Fatal("unexpected slice len")
}
Expand All @@ -35,10 +32,8 @@ func TestPolicyAssignmentProperties(t *testing.T) {
if !ok {
t.Fatal("missing one")
}
mm := map[string]any{}
if err := json.Unmarshal(m.Value, &mm); err != nil {
t.Fatal(err)
}
mm, ok := m.Value.(map[string]any)
require.True(t, ok)
if v := mm["key"]; v != "value" {
t.Fatalf("got %s want value", v)
}
Expand Down
20 changes: 17 additions & 3 deletions test/maps/azalias/polymorphic_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"testing"
"time"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/stretchr/testify/require"
)

func TestGeoObjectNamedCollectionRoundTrip(t *testing.T) {
Expand Down Expand Up @@ -96,7 +98,7 @@ func TestInterfaceRoundTrip(t *testing.T) {
props1 := ScheduleCreateOrUpdateProperties{
Aliases: []*string{to.Ptr("foo")},
Description: to.Ptr("funky"),
Interval: []byte("false"),
Interval: false,
StartTime: to.Ptr(time.Now().UTC()),
}
b, err := json.Marshal(props1)
Expand All @@ -114,8 +116,10 @@ func TestInterfaceRoundTrip(t *testing.T) {
if *props1.Description != *props2.Description {
t.Fatalf("expected %v, got %v", *props1.Description, *props2.Description)
}
i1 := string(props1.Interval)
i2 := string(props2.Interval)
i1, ok := props1.Interval.(bool)
require.True(t, ok)
i2, ok := props2.Interval.(bool)
require.True(t, ok)
if i1 != i2 {
t.Fatalf("expected %v, got %v", props1.Interval, props2.Interval)
}
Expand Down Expand Up @@ -154,3 +158,13 @@ func TestInterfaceNil(t *testing.T) {
t.Fatal("expected nil Aliases")
}
}

func TestInterfaceJSONNull(t *testing.T) {
props1 := TypeWithRawJSON{
AnyObject: azcore.NullValue[*any](),
}
b, err := json.Marshal(props1)
require.NoError(t, err)
require.Contains(t, string(b), `"anyObject":null`)
require.NotContains(t, string(b), "anything")
}
Loading

0 comments on commit 959ddb7

Please sign in to comment.