diff --git a/.changelog/v0.5.0.toml b/.changelog/v0.5.0.toml index b7ec3e5..49c6643 100644 --- a/.changelog/v0.5.0.toml +++ b/.changelog/v0.5.0.toml @@ -7,8 +7,8 @@ title = "" description = "" [[bugs]] -title = "" -description = "" +title = "Type fields" +description = "All arrays that are nullable in the API no longer have `omitempty` to avoid panics if unset. [#283](https://github.com/oxidecomputer/oxide.go/pull/283)" [[enhancements]] title = "" diff --git a/internal/generate/exceptions.go b/internal/generate/exceptions.go index 905ceed..5aea495 100644 --- a/internal/generate/exceptions.go +++ b/internal/generate/exceptions.go @@ -4,15 +4,6 @@ package main -// Returns a list of types that should not be omitted when empty -// for json serialisation -func omitemptyExceptions() []string { - return []string{ - "[]VpcFirewallRuleUpdate", - "[]NameOrId", - } -} - func emptyTypes() []string { return []string{ "BgpMessageHistory", @@ -20,8 +11,9 @@ func emptyTypes() []string { } } -// TODO: Actually handle nullable fields properly func nullable() []string { + // TODO: This type has a nested required "Type" field, which hinders + // the usage of this type. Remove when this is fixed in the upstream API return []string{ "InstanceDiskAttachment", } diff --git a/internal/generate/types.go b/internal/generate/types.go index f2aff78..b4d1513 100644 --- a/internal/generate/types.go +++ b/internal/generate/types.go @@ -541,12 +541,12 @@ func createTypeObject(schema *openapi3.Schema, name, typeName, description strin field.Name = strcase.ToCamel(k) field.Type = typeName + serInfo := fmt.Sprintf("`json:\"%s,omitempty\" yaml:\"%s,omitempty\"`", k, k) - // There are a few types we do not want to omit if empty - // TODO: Keep an eye out to see if there is a way to identify all of - if sliceContains(omitemptyExceptions(), typeName) { + if isNullableArray(v) { serInfo = fmt.Sprintf("`json:\"%s\" yaml:\"%s\"`", k, k) } + field.SerializationInfo = serInfo fields = append(fields, field) diff --git a/internal/generate/utils.go b/internal/generate/utils.go index 554c0c4..27481ae 100644 --- a/internal/generate/utils.go +++ b/internal/generate/utils.go @@ -59,6 +59,10 @@ func isObjectArray(v *openapi3.SchemaRef) bool { return false } +func isNullableArray(v *openapi3.SchemaRef) bool { + return v.Value.Type.Is("array") && v.Value.Nullable +} + // formatStringType converts a string schema to a valid Go type. func formatStringType(t *openapi3.Schema) string { var format string diff --git a/oxide/types.go b/oxide/types.go index a1dea40..ac0f99e 100644 --- a/oxide/types.go +++ b/oxide/types.go @@ -3427,7 +3427,7 @@ type InstanceCpuCount uint16 // - Ncpus type InstanceCreate struct { // AntiAffinityGroups is anti-Affinity groups which this instance should be added. - AntiAffinityGroups []NameOrId `json:"anti_affinity_groups" yaml:"anti_affinity_groups"` + AntiAffinityGroups []NameOrId `json:"anti_affinity_groups,omitempty" yaml:"anti_affinity_groups,omitempty"` // AutoRestartPolicy is the auto-restart policy for this instance. // // This policy determines whether the instance should be automatically restarted by the control plane on failure. @@ -4501,7 +4501,7 @@ type PingStatus string // - Timestamps // - Values type Points struct { - StartTimes []string `json:"start_times,omitempty" yaml:"start_times,omitempty"` + StartTimes []string `json:"start_times" yaml:"start_times"` Timestamps []string `json:"timestamps,omitempty" yaml:"timestamps,omitempty"` Values []Values `json:"values,omitempty" yaml:"values,omitempty"` } @@ -5957,7 +5957,7 @@ type SwitchPortSettingsCreate struct { // BgpPeers is bGP peers indexed by interface name. BgpPeers BgpPeerConfig `json:"bgp_peers,omitempty" yaml:"bgp_peers,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` - Groups []NameOrId `json:"groups" yaml:"groups"` + Groups []NameOrId `json:"groups,omitempty" yaml:"groups,omitempty"` // Interfaces is interfaces indexed by link name. Interfaces SwitchInterfaceConfigCreate `json:"interfaces,omitempty" yaml:"interfaces,omitempty"` // Links is links indexed by phy name. On ports that are not broken out, this is always phy0. On a 2x breakout @@ -6612,11 +6612,11 @@ type VpcFirewallRuleDirection string type VpcFirewallRuleFilter struct { // Hosts is if present, host filters match the "other end" of traffic from the target’s perspective: for // an inbound rule, they match the source of traffic. For an outbound rule, they match the destination. - Hosts []VpcFirewallRuleHostFilter `json:"hosts,omitempty" yaml:"hosts,omitempty"` + Hosts []VpcFirewallRuleHostFilter `json:"hosts" yaml:"hosts"` // Ports is if present, the destination ports or port ranges this rule applies to. - Ports []L4PortRange `json:"ports,omitempty" yaml:"ports,omitempty"` + Ports []L4PortRange `json:"ports" yaml:"ports"` // Protocols is if present, the networking protocols this rule applies to. - Protocols []VpcFirewallRuleProtocol `json:"protocols,omitempty" yaml:"protocols,omitempty"` + Protocols []VpcFirewallRuleProtocol `json:"protocols" yaml:"protocols"` } // VpcFirewallRuleHostFilterType is the type definition for a VpcFirewallRuleHostFilterType. @@ -6808,7 +6808,7 @@ type VpcFirewallRuleUpdate struct { // Required fields: // - Rules type VpcFirewallRuleUpdateParams struct { - Rules []VpcFirewallRuleUpdate `json:"rules" yaml:"rules"` + Rules []VpcFirewallRuleUpdate `json:"rules,omitempty" yaml:"rules,omitempty"` } // VpcFirewallRules is collection of a Vpc's firewall rules