schema/v1/inputs.go (124 lines of code) (raw):
package schema
import (
"encoding/json"
"fmt"
"reflect"
"google.golang.org/protobuf/types/known/structpb"
"gitlab.com/gitlab-org/step-runner/proto"
)
type Input struct {
// AdditionalProperties corresponds to the JSON schema field
// "additionalProperties".
AdditionalProperties any `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty" mapstructure:"additionalProperties,omitempty"`
// Default is the default input value. Its type must match `type`.
Default any `json:"default,omitempty" yaml:"default,omitempty" mapstructure:"default,omitempty"`
// Sensitive implies the input is of sensitive nature and effort should be made to
// prevent accidental disclosure.
Sensitive *bool `json:"sensitive,omitempty" yaml:"sensitive,omitempty" mapstructure:"sensitive,omitempty"`
// Type is the value type of the input.
Type *InputType `json:"type,omitempty" yaml:"type,omitempty" mapstructure:"type,omitempty"`
}
func (i *Input) compile() (*proto.Spec_Content_Input, error) {
protoInput, err := i.compileToProto()
if err != nil {
return nil, err
}
err = i.verifyDefaultValueMatchesType(protoInput)
if err != nil {
return nil, err
}
return protoInput, nil
}
func (i *Input) compileToProto() (*proto.Spec_Content_Input, error) {
protoInput := &proto.Spec_Content_Input{}
if i.Type == nil {
return nil, fmt.Errorf("missing input type")
}
switch *i.Type {
case InputTypeBoolean:
protoInput.Type = proto.ValueType_boolean
case InputTypeArray:
protoInput.Type = proto.ValueType_array
case InputTypeNumber:
protoInput.Type = proto.ValueType_number
case InputTypeString:
protoInput.Type = proto.ValueType_string
case InputTypeStruct:
protoInput.Type = proto.ValueType_struct
default:
return nil, fmt.Errorf("unsupported input type: %v", i.Type)
}
if i.Default != nil {
protoV, err := (&valueCompiler{i.Default}).compile()
if err != nil {
return nil, fmt.Errorf("compiling default %v: %w", i.Default, err)
}
protoInput.Default = protoV
}
if i.Sensitive != nil && *i.Sensitive {
protoInput.Sensitive = true
}
return protoInput, nil
}
func (i *Input) verifyDefaultValueMatchesType(protoInput *proto.Spec_Content_Input) error {
if i.Default == nil || protoInput.Default == nil {
return nil
}
if i.Type == nil {
return nil
}
var defaultType InputType
switch *i.Type {
case InputTypeBoolean:
if _, ok := protoInput.Default.Kind.(*structpb.Value_BoolValue); ok {
defaultType = InputTypeBoolean
}
case InputTypeArray:
if _, ok := protoInput.Default.Kind.(*structpb.Value_ListValue); ok {
defaultType = InputTypeArray
}
case InputTypeNumber:
if _, ok := protoInput.Default.Kind.(*structpb.Value_NumberValue); ok {
defaultType = InputTypeNumber
}
case InputTypeString:
if _, ok := protoInput.Default.Kind.(*structpb.Value_StringValue); ok {
defaultType = InputTypeString
}
case InputTypeStruct:
if _, ok := protoInput.Default.Kind.(*structpb.Value_StructValue); ok {
defaultType = InputTypeStruct
}
default:
return fmt.Errorf("unsupported type: %v", i.Type)
}
if defaultType != *i.Type {
return fmt.Errorf("input type %v and default value type %v must match", i.Type, defaultType)
}
return nil
}
type InputType string
const InputTypeArray InputType = "array"
const InputTypeBoolean InputType = "boolean"
const InputTypeNumber InputType = "number"
const InputTypeString InputType = "string"
const InputTypeStruct InputType = "struct"
var enumValues_InputType = []any{
"string",
"number",
"boolean",
"struct",
"array",
}
// UnmarshalJSON implements json.Unmarshaler.
func (j *InputType) UnmarshalJSON(b []byte) error {
var v string
if err := json.Unmarshal(b, &v); err != nil {
return err
}
var ok bool
for _, expected := range enumValues_InputType {
if reflect.DeepEqual(v, expected) {
ok = true
break
}
}
if !ok {
return fmt.Errorf("invalid value (expected one of %#v): %#v", enumValues_InputType, v)
}
*j = InputType(v)
return nil
}