dsl

package
v0.9.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 12, 2023 License: Apache-2.0 Imports: 31 Imported by: 0

README

Hops DSL

The hops dsl package provides definitions of the hops syntax and parsing logic.

Core functions and expressions within the syntax are handled here, though dispatch and orchestration logic are not.

Documentation

Overview

c matches character c (c != '\\', '-', ']') '\\' c matches character c lo '-' hi matches character c for lo <= c <= hi

Index

Constants

View Source
const (
	InvalidRequired  string = "Required"
	InvalidNotString string = "Should be a string"
	InvalidNotText   string = "Should be text"
	InvalidNotNumber string = "Should be a number"
	InvalidNotBool   string = "Should be a boolean"
)
View Source
const MaxLabelLength = 50

Variables

View Source
var (
	ErrorAttr  = "error"
	ResultAttr = "result"
	IfAttr     = "if"
	NameAttr   = "name"

	HopSchema = &hcl.BodySchema{
		Attributes: []hcl.AttributeSchema{},
		Blocks: []hcl.BlockHeaderSchema{
			{
				Type:       OnID,
				LabelNames: []string{"eventType"},
			},
			{
				Type:       TaskID,
				LabelNames: []string{"Name"},
			},
			{
				Type:       ScheduleID,
				LabelNames: []string{"Name"},
			},
		},
	}

	OnID     = "on"
	OnSchema = &hcl.BodySchema{
		Blocks: []hcl.BlockHeaderSchema{
			{
				Type:       CallID,
				LabelNames: []string{"taskType"},
			},
			{
				Type: DoneID,
			},
		},
		Attributes: []hcl.AttributeSchema{
			{Name: "name", Required: false},
			{Name: IfAttr, Required: false},
		},
	}

	CallID = "call"

	DoneID = "done"

	TaskID = "task"

	ParamID    = "param"    // Schema defined via tags on the struct
	ScheduleID = "schedule" // Schema defined via tags on the struct
)
View Source
var AllTrueFunc = function.New(&function.Spec{
	VarParam: &function.Parameter{
		Name: "clauses",
		Type: cty.Bool,
	},
	Type: function.StaticReturnType(cty.Bool),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		return AllTrue(args)
	},
})

alltrue() takes a variable number of bool arguments and returns true if all of them are true.

View Source
var AnyTrueFunc = function.New(&function.Spec{
	VarParam: &function.Parameter{
		Name: "clauses",
		Type: cty.Bool,
	},
	Type: function.StaticReturnType(cty.Bool),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		if len(args) == 0 {
			return cty.False, nil
		}

		for _, clause := range args {
			if clause.True() {
				return cty.True, nil
			}
		}

		return cty.False, nil
	},
})

anytrue() takes a variable number of bool arguments and returns true if any of them are true.

View Source
var DefaultFunctions = map[string]function.Function{
	"abs":             stdlib.AbsoluteFunc,
	"alltrue":         AllTrueFunc,
	"anytrue":         AnyTrueFunc,
	"versiontmpl":     VersionTemplateFunc,
	"can":             tryfunc.CanFunc,
	"ceil":            stdlib.CeilFunc,
	"chomp":           stdlib.ChompFunc,
	"coalesce":        stdlib.CoalesceFunc,
	"compact":         stdlib.CompactFunc,
	"concat":          stdlib.ConcatFunc,
	"csv":             stdlib.CSVDecodeFunc,
	"env":             EnvFunc,
	"flatten":         stdlib.FlattenFunc,
	"floor":           stdlib.FloorFunc,
	"format":          stdlib.FormatFunc,
	"formatdate":      stdlib.FormatDateFunc,
	"glob":            GlobFunc,
	"indent":          stdlib.IndentFunc,
	"index":           stdlib.IndexFunc,
	"int":             stdlib.IntFunc,
	"join":            stdlib.JoinFunc,
	"jsondecode":      stdlib.JSONDecodeFunc,
	"jsonencode":      stdlib.JSONEncodeFunc,
	"keys":            stdlib.KeysFunc,
	"length":          stdlib.LengthFunc,
	"lookup":          stdlib.LookupFunc,
	"lower":           stdlib.LowerFunc,
	"max":             stdlib.MaxFunc,
	"merge":           stdlib.MergeFunc,
	"min":             stdlib.MinFunc,
	"range":           stdlib.RangeFunc,
	"regex":           stdlib.RegexAllFunc,
	"regexreplace":    stdlib.RegexReplaceFunc,
	"replace":         stdlib.ReplaceFunc,
	"reverse":         stdlib.ReverseFunc,
	"setintersection": stdlib.SetIntersectionFunc,
	"setproduct":      stdlib.SetProductFunc,
	"setunion":        stdlib.SetUnionFunc,
	"slice":           stdlib.SliceFunc,
	"sort":            stdlib.SortFunc,
	"split":           stdlib.SplitFunc,
	"strlen":          stdlib.StrlenFunc,
	"substr":          stdlib.SubstrFunc,
	"timeadd":         stdlib.TimeAddFunc,
	"title":           stdlib.TitleFunc,
	"tobool":          stdlib.MakeToFunc(cty.Bool),
	"tolist":          stdlib.MakeToFunc(cty.List(cty.DynamicPseudoType)),
	"tomap":           stdlib.MakeToFunc(cty.Map(cty.DynamicPseudoType)),
	"tonumber":        stdlib.MakeToFunc(cty.Number),
	"toset":           stdlib.MakeToFunc(cty.Set(cty.DynamicPseudoType)),
	"tostring":        stdlib.MakeToFunc(cty.String),
	"trim":            stdlib.TrimFunc,
	"trimprefix":      stdlib.TrimPrefixFunc,
	"trimspace":       stdlib.TrimSpaceFunc,
	"trimsuffix":      stdlib.TrimSuffixFunc,
	"try":             tryfunc.TryFunc,
	"upper":           stdlib.UpperFunc,
	"values":          stdlib.ValuesFunc,
	"xglob":           ExclusiveGlobFunc,
	"zipmap":          stdlib.ZipmapFunc,
}

TODO: Add encode/decode b64

View Source
var EnvFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name: "envVarName",
			Type: cty.String,
		},
		{
			Name: "defaultValue",
			Type: cty.String,
		},
	},
	Type: function.StaticReturnType(cty.String),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		envVarName := args[0]
		defaultValue := args[1]
		return Env(envVarName, defaultValue)
	},
})

EnvFunc is a cty.Function that returns an env var or the default value if it doesn't exist

View Source
var ExclusiveGlobFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name: "patterns",
			Type: cty.DynamicPseudoType,
		},
		{
			Name: "values",
			Type: cty.DynamicPseudoType,
		},
	},
	Type: function.StaticReturnType(cty.Bool),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		values := args[0]
		patterns := args[1]
		return XGlob(values, patterns)
	},
})

A cty.Function that matches a string or list of strings against a glob pattern or list of glob patterns. Returns true if _all_ values match at least one pattern.

View Source
var GlobFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name: "patterns",
			Type: cty.DynamicPseudoType,
		},
		{
			Name: "values",
			Type: cty.DynamicPseudoType,
		},
	},
	Type: function.StaticReturnType(cty.Bool),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		values := args[0]
		patterns := args[1]
		return Glob(values, patterns)
	},
})

GlobFunc is a cty.Function that matches a string or list of strings against a glob pattern or list of glob patterns. Returns true if _any_ values match at least one pattern.

View Source
var VersionTemplateFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name: "template",
			Type: cty.String,
		},
	},
	Type: function.StaticReturnType(cty.String),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		templateVal := args[0]
		template := templateVal.AsString()

		version, err := TemplateVersion(template)

		return cty.StringVal(version), err
	},
})

VersionTemplateFunc generates a calver or pet version according to a template

Functions

func AllTrue added in v0.2.0

func AllTrue(values []cty.Value) (cty.Value, error)

func AnyJSONToCtyValue

func AnyJSONToCtyValue(jsonStr []byte) (cty.Value, error)

TODO: This method effectively parses the JSON string twice. Once via unmarshal called directly, then again via ctyjson.ImpliedType which runs a decoder. It is likely worth the time to write a decoder that directly takes the unmarshalled json and manually maps to cty values. This function is quite expensive as an overall portion of the runtime. Taking around 200-250µs for a single change event (around 20-25% of hops parsing time).

func AnyTrue added in v0.2.0

func AnyTrue(values []cty.Value) (cty.Value, error)

func CreateSourceEvent

func CreateSourceEvent(rawEvent map[string]any, source string, event string, action string) ([]byte, string, error)

func DecodeCallBlock

func DecodeCallBlock(ctx context.Context, hop *HopAST, on *OnAST, block *hcl.Block, idx int, evalctx *hcl.EvalContext, logger zerolog.Logger) error

func DecodeConditionalAttr

func DecodeConditionalAttr(attr *hcl.Attribute, defaultValue bool, ctx *hcl.EvalContext) (bool, error)

func DecodeHopsBody

func DecodeHopsBody(ctx context.Context, hop *HopAST, hopsContent *hcl.BodyContent, evalctx *hcl.EvalContext, logger zerolog.Logger) error

func DecodeNameAttr

func DecodeNameAttr(attr *hcl.Attribute) (string, error)

func DecodeOnBlock

func DecodeOnBlock(ctx context.Context, hop *HopAST, block *hcl.Block, idx int, evalctx *hcl.EvalContext, logger zerolog.Logger) error

func DecodeParamBlock

func DecodeParamBlock(block *hcl.Block, task *TaskAST, hop *HopAST, evalctx *hcl.EvalContext) error

func DecodeScheduleBlock added in v0.7.0

func DecodeScheduleBlock(block *hcl.Block, hop *HopAST, evalctx *hcl.EvalContext) error

func DecodeSchedules added in v0.7.0

func DecodeSchedules(hop *HopAST, hopsContent *hcl.BodyContent, evalctx *hcl.EvalContext) error

func DecodeTaskBlock

func DecodeTaskBlock(ctx context.Context, hop *HopAST, block *hcl.Block, evalctx *hcl.EvalContext) error

func DecodeTasks

func DecodeTasks(ctx context.Context, hop *HopAST, hopsContent *hcl.BodyContent, evalctx *hcl.EvalContext) error

func Env

func Env(envVarName, defaultValue cty.Value) (cty.Value, error)

func Glob

func Glob(values, patterns cty.Value) (cty.Value, error)

func ReadHopsFileContents added in v0.3.0

func ReadHopsFileContents(hopsFileContent []FileContent) (*hcl.BodyContent, string, error)

func TemplateVersion added in v0.3.0

func TemplateVersion(template string) (string, error)

func ValidateLabels

func ValidateLabels(labels ...string) error

func XGlob

func XGlob(values, patterns cty.Value) (cty.Value, error)

Types

type CallAST

type CallAST struct {
	Slug     string
	TaskType string
	Name     string
	Inputs   []byte
	ConditionalAST
}

type ConditionalAST

type ConditionalAST struct {
	IfClause bool
}

type DoneAST added in v0.6.0

type DoneAST struct {
	Error  error
	Result []byte
}

func DecodeDoneBlock added in v0.6.0

func DecodeDoneBlock(ctx context.Context, hop *HopAST, on *OnAST, block *hcl.Block, evalctx *hcl.EvalContext, logger zerolog.Logger) (*DoneAST, error)

type FileContent added in v0.3.0

type FileContent struct {
	File    string `json:"file"`
	Content []byte `json:"content"`
}

type HopAST

type HopAST struct {
	Ons          []OnAST
	Schedules    []ScheduleAST
	SlugRegister map[string]bool
	StartedAt    time.Time
	Tasks        []TaskAST
}

func ParseHops

func ParseHops(ctx context.Context, hopsContent *hcl.BodyContent, eventBundle map[string][]byte, logger zerolog.Logger) (*HopAST, error)

func ParseHopsSchedules added in v0.7.0

func ParseHopsSchedules(hopsContent *hcl.BodyContent, logger zerolog.Logger) (*HopAST, error)

func ParseHopsTasks

func ParseHopsTasks(ctx context.Context, hopsContent *hcl.BodyContent) (*HopAST, error)

func (*HopAST) GetTask

func (h *HopAST) GetTask(taskName string) (TaskAST, error)

func (*HopAST) ListSchedules added in v0.7.0

func (h *HopAST) ListSchedules() []ScheduleAST

func (*HopAST) ListTasks

func (h *HopAST) ListTasks() []TaskAST

type HopsFiles added in v0.3.0

type HopsFiles struct {
	Hash        string
	BodyContent *hcl.BodyContent
	Files       []FileContent
}

func ReadHopsFilePath added in v0.3.0

func ReadHopsFilePath(filePath string) (*HopsFiles, error)

ReadHopsFilePath loads and pre-parses the content of .hops files either from a single file or from all .hops files in a directory. It returns a merged hcl.Body and a sha hash of the contents

type OnAST

type OnAST struct {
	Slug      string
	EventType string
	Name      string
	Calls     []CallAST
	Done      *DoneAST
	ConditionalAST
}

type ParamAST

type ParamAST struct {
	// We use HCL tags to auto-decode as params need very little custom decoding logic
	Name        string `hcl:"name,label" json:"name"`
	DisplayName string `hcl:"display_name,optional" json:"display_name"`
	Type        string `hcl:"type,optional" json:"type"`
	Default     any    `hcl:"default,optional" json:"default"`
	Help        string `hcl:"help,optional" json:"help"`
	Flag        string `hcl:"flag,optional" json:"flag"`
	ShortFlag   string `hcl:"shortflag,optional" json:"shortflag"`
	Required    bool   `hcl:"required,optional" json:"required"`
}

type ScheduleAST added in v0.7.0

type ScheduleAST struct {
	Name   string         `hcl:"name,label" json:"name"`
	Cron   string         `hcl:"cron,attr" json:"cron"`
	Inputs []byte         `json:"inputs"` // Inputs is decoded explicitly from remain
	Remain hcl.Attributes `hcl:",remain"`
}

type SourceMeta

type SourceMeta struct {
	Source string `json:"source"`
	Event  string `json:"event"`
	Action string `json:"action"`
}

type TaskAST

type TaskAST struct {
	Name        string     `json:"name"`
	DisplayName string     `json:"display_name"`
	Summary     string     `json:"summary"`
	Description string     `json:"description"`
	Emoji       string     `json:"emoji"`
	Params      []ParamAST `json:"params"`
}

func (*TaskAST) ValidateInput

func (c *TaskAST) ValidateInput(input map[string]any) map[string][]string

ValidateInput validates a struct of param inputs against a task

Returns a map of parameter names with an array of validation error messages if any. Map will be empty (but not nil) if all input is valid.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL