v2/tools/generator/internal/config/property_configuration.go (141 lines of code) (raw):
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*/
package config
import (
"strings"
"github.com/rotisserie/eris"
"gopkg.in/yaml.v3"
)
// PropertyConfiguration contains additional information about a specific property and forms part of a hierarchy
// containing information to supplement the schema and swagger sources consumed by the generator.
//
// ┌──────────────────────────┐ ┌────────────────────┐ ┌──────────────────────┐ ┌───────────────────┐ ╔═══════════════════════╗
// │ │ │ │ │ │ │ │ ║ ║
// │ ObjectModelConfiguration │───────│ GroupConfiguration │───────│ VersionConfiguration │───────│ TypeConfiguration │───────║ PropertyConfiguration ║
// │ │1 1..n│ │1 1..n│ │1 1..n│ │1 1..n║ ║
// └──────────────────────────┘ └────────────────────┘ └──────────────────────┘ └───────────────────┘ ╚═══════════════════════╝
type PropertyConfiguration struct {
name string
// Configurable properties here (alphabetical, please)
Description configurable[string] // Specify a description override for this property
ImportConfigMapMode configurable[ImportConfigMapMode] // The config map mode
IsSecret configurable[bool] // Specify whether this property is a secret
NameInNextVersion configurable[string] // Name this property has in the next version
PayloadType configurable[PayloadType] // Specify how this property should be serialized for ARM
ReferenceType configurable[ReferenceType] // Specify whether this property is an ARM reference or some other kind
RenameTo configurable[string] // Name this property should be renamed to
ResourceLifecycleOwnedByParent configurable[string] // Name of the parent resource which owns the lifecycle of the sub-resource.
}
type ImportConfigMapMode string
const (
ImportConfigMapModeOptional = "optional"
ImportConfigMapModeRequired = "required"
)
type ReferenceType string
const (
ReferenceTypeARM = ReferenceType("arm") // An ARM reference
ReferenceTypeSimple = ReferenceType("simple") // A simple reference requiring no special handling
)
// Tags used in yaml files to specify configurable properties. Alphabetical please.
const (
descriptionTag = "$description" // String overriding the properties default description
exportAsConfigMapPropertyNameTag = "$exportAsConfigMapPropertyName" // String specifying the name of the property set to export this property as a config map.
importConfigMapModeTag = "$importConfigMapMode" // string specifying the ImportConfigMapMode mode
isSecretTag = "$isSecret" // Bool specifying whether a property contains a secret
referenceTypeTag = "$referenceType" // String specifying what kind of reference we have
renamePropertyToTag = "$renameTo" // String specifying the name this property should be renamed to
resourceLifecycleOwnedByParentTag = "$resourceLifecycleOwnedByParent" // String specifying whether a property represents a subresource whose lifecycle is owned by the parent resource (and what that parent resource is)
)
// NewPropertyConfiguration returns a new (empty) property configuration
func NewPropertyConfiguration(name string) *PropertyConfiguration {
scope := "property " + name
return &PropertyConfiguration{
name: name,
// Initialize configurable properties here (alphabetical, please)
Description: makeConfigurable[string](descriptionTag, scope),
ImportConfigMapMode: makeConfigurable[ImportConfigMapMode](importConfigMapModeTag, scope),
IsSecret: makeConfigurable[bool](isSecretTag, scope),
NameInNextVersion: makeConfigurable[string](nameInNextVersionTag, scope),
ReferenceType: makeConfigurable[ReferenceType](referenceTypeTag, scope),
RenameTo: makeConfigurable[string](renamePropertyToTag, scope),
ResourceLifecycleOwnedByParent: makeConfigurable[string](resourceLifecycleOwnedByParentTag, scope),
}
}
// UnmarshalYAML populates our instance from the YAML.
// The slice node.Content contains pairs of nodes, first one for an ID, then one for the value.
func (pc *PropertyConfiguration) UnmarshalYAML(value *yaml.Node) error {
if value.Kind != yaml.MappingNode {
return eris.New("expected mapping")
}
var lastID string
for i, c := range value.Content {
// Grab identifiers and loop to handle the associated value
if i%2 == 0 {
lastID = strings.ToLower(c.Value)
continue
}
// $nameInNextVersion: <string>
if strings.EqualFold(lastID, nameInNextVersionTag) && c.Kind == yaml.ScalarNode {
pc.NameInNextVersion.Set(c.Value)
continue
}
// $isSecret: <bool>
if strings.EqualFold(lastID, isSecretTag) && c.Kind == yaml.ScalarNode {
var isSecret bool
err := c.Decode(&isSecret)
if err != nil {
return eris.Wrapf(err, "decoding %s", isSecretTag)
}
pc.IsSecret.Set(isSecret)
continue
}
// $resourceLifecycleOwnedByParent: string
if strings.EqualFold(lastID, resourceLifecycleOwnedByParentTag) && c.Kind == yaml.ScalarNode {
var resourceLifecycleOwnedByParent string
err := c.Decode(&resourceLifecycleOwnedByParent)
if err != nil {
return eris.Wrapf(err, "decoding %s", resourceLifecycleOwnedByParentTag)
}
pc.ResourceLifecycleOwnedByParent.Set(resourceLifecycleOwnedByParent)
continue
}
// $referenceType: <string>
if strings.EqualFold(lastID, referenceTypeTag) && c.Kind == yaml.ScalarNode {
switch strings.ToLower(c.Value) {
case string(ReferenceTypeARM):
pc.ReferenceType.Set(ReferenceTypeARM)
case string(ReferenceTypeSimple):
pc.ReferenceType.Set(ReferenceTypeSimple)
default:
return eris.Errorf("unknown %s value: %s.", referenceTypeTag, c.Value)
}
continue
}
// $ImportConfigMapMode: <string>
if strings.EqualFold(lastID, importConfigMapModeTag) && c.Kind == yaml.ScalarNode {
switch strings.ToLower(c.Value) {
case ImportConfigMapModeOptional:
pc.ImportConfigMapMode.Set(ImportConfigMapModeOptional)
case ImportConfigMapModeRequired:
pc.ImportConfigMapMode.Set(ImportConfigMapModeRequired)
default:
return eris.Errorf("unknown %s value: %s.", importConfigMapModeTag, c.Value)
}
continue
}
// renameTo: string
if strings.EqualFold(lastID, renamePropertyToTag) && c.Kind == yaml.ScalarNode {
var renameTo string
err := c.Decode(&renameTo)
if err != nil {
return eris.Wrapf(err, "decoding %s", renamePropertyToTag)
}
pc.RenameTo.Set(renameTo)
continue
}
// $payloadType: <string>
if strings.EqualFold(lastID, payloadTypeTag) && c.Kind == yaml.ScalarNode {
switch strings.ToLower(c.Value) {
case string(OmitEmptyProperties):
pc.PayloadType.Set(OmitEmptyProperties)
case string(ExplicitCollections):
pc.PayloadType.Set(ExplicitCollections)
case string(ExplicitEmptyCollections):
pc.PayloadType.Set(ExplicitEmptyCollections)
case string(ExplicitProperties):
pc.PayloadType.Set(ExplicitProperties)
default:
return eris.Errorf("unknown %s value: %s.", payloadTypeTag, c.Value)
}
continue
}
// description: string
if strings.EqualFold(lastID, descriptionTag) && c.Kind == yaml.ScalarNode {
var description string
err := c.Decode(&description)
if err != nil {
return eris.Wrapf(err, "decoding %s", descriptionTag)
}
pc.Description.Set(description)
continue
}
// No handler for this value, return an error
return eris.Errorf(
"property configuration, unexpected yaml value %s: %s (line %d col %d)", lastID, c.Value, c.Line, c.Column)
}
return nil
}