Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introducing template strings and strconcat function #122

Merged
merged 3 commits into from
Sep 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/semaphore/daemon/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
import (
"github.com/jexia/semaphore"
"github.com/jexia/semaphore/cmd/semaphore/daemon/providers"
"github.com/jexia/semaphore/cmd/semaphore/functions"
"github.com/jexia/semaphore/cmd/semaphore/middleware"
"github.com/jexia/semaphore/pkg/broker"
"github.com/jexia/semaphore/pkg/broker/logger"
Expand Down Expand Up @@ -119,6 +120,7 @@ func NewCore(ctx *broker.Context, flags *Daemon) (semaphore.Options, error) {
semaphore.WithCaller(micro.NewCaller("micro-grpc", microGRPC.NewService())),
semaphore.WithCaller(grpc.NewCaller()),
semaphore.WithCaller(http.NewCaller()),
semaphore.WithFunctions(functions.Default),
}

for _, path := range flags.Files {
Expand Down
43 changes: 43 additions & 0 deletions cmd/semaphore/functions/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package functions

import (
"fmt"

"github.com/jexia/semaphore/pkg/prettyerr"
"github.com/jexia/semaphore/pkg/specs"
"github.com/jexia/semaphore/pkg/specs/types"
)

type wrapErr struct {
Inner error
}

func (i wrapErr) Unwrap() error {
return i.Inner
}

// ErrInvalidArgument is thrown when a given argument is invalid
type ErrInvalidArgument struct {
wrapErr
Function string
Property *specs.Property
Expected types.Type
}

// Error returns a description of the given error as a string
func (e ErrInvalidArgument) Error() string {
return fmt.Sprintf("invalid argument %s in %s expected %s", e.Property.Type, e.Function, e.Expected)
}

// Prettify returns the prettified version of the given error
func (e ErrInvalidArgument) Prettify() prettyerr.Error {
return prettyerr.Error{
Code: "InvalidArgument",
Message: e.Error(),
Details: map[string]interface{}{
"Function": e.Function,
"Type": e.Property.Type,
"Expected": e.Expected,
},
}
}
8 changes: 8 additions & 0 deletions cmd/semaphore/functions/functions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package functions

import "github.com/jexia/semaphore/pkg/functions"

// Default represents the default functions collection
var Default = functions.Custom{
"strconcat": Strconcat,
}
60 changes: 60 additions & 0 deletions cmd/semaphore/functions/strconcat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package functions

import (
"strings"

"github.com/jexia/semaphore/pkg/functions"
"github.com/jexia/semaphore/pkg/references"
"github.com/jexia/semaphore/pkg/specs"
"github.com/jexia/semaphore/pkg/specs/labels"
"github.com/jexia/semaphore/pkg/specs/types"
)

// Strconcat compiles the given arguments and constructs a new executable
// function for the given arguments.
func Strconcat(args ...*specs.Property) (*specs.Property, functions.Exec, error) {
result := &specs.Property{
Name: "concat",
Type: types.String,
Label: labels.Optional,
}

for _, arg := range args {
if arg.Type != types.String {
return nil, nil, ErrInvalidArgument{
Property: arg,
Expected: types.String,
Function: "strconcat",
}
}
}

handle := func(store references.Store) error {
result := strings.Builder{}

for _, arg := range args {
var value string
jeroenrinzema marked this conversation as resolved.
Show resolved Hide resolved

if arg.Default != nil {
value = arg.Default.(string)
}

if arg.Reference != nil {
ref := store.Load(arg.Reference.Resource, arg.Reference.Path)
if ref != nil {
value = ref.Value.(string)
}
}

_, err := result.WriteString(value)
if err != nil {
return err
}
}

store.StoreValue("", ".", result.String())
return nil
}

return result, handle, nil
}
17 changes: 12 additions & 5 deletions pkg/lookup/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,11 @@ func GetAvailableResources(flow specs.FlowInterface, breakpoint string) map[stri
}

if breakpoint == template.OutputResource {
references[template.ErrorResource] = ReferenceMap{
template.ResponseResource: OnErrLookup(template.OutputResource, flow.GetOnError()),
}

if flow.GetOnError() != nil {
references[template.ErrorResource][template.ParamsResource] = ParamsLookup(flow.GetOnError().Params, flow, "")
references[template.ErrorResource] = ReferenceMap{
template.ResponseResource: OnErrLookup(template.OutputResource, flow.GetOnError()),
template.ParamsResource: ParamsLookup(flow.GetOnError().Params, flow, ""),
}
}
}

Expand Down Expand Up @@ -138,6 +137,14 @@ func GetAvailableResources(flow specs.FlowInterface, breakpoint string) map[stri
}
}

if flow.GetOutput() != nil {
if flow.GetOutput().Stack != nil {
for key, returns := range flow.GetOutput().Stack {
references[template.StackResource][key] = PropertyLookup(returns)
}
}
}

return references
}

Expand Down
43 changes: 43 additions & 0 deletions pkg/lookup/lookup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,32 @@ func NewMockFlow(name string) *specs.Flow {
Input: &specs.ParameterMap{
Property: NewInputMockProperty(),
},
OnError: &specs.OnError{
Response: &specs.ParameterMap{
Property: NewResultMockProperty(),
Params: map[string]*specs.Property{
"message": {
Path: "message",
Default: "hello world",
Type: types.String,
Label: labels.Optional,
},
"name": {
Path: "message",
Default: "hello world",
Type: types.String,
Label: labels.Optional,
},
"reference": {
Path: "reference",
Reference: &specs.PropertyReference{
Resource: name,
Path: "message",
},
},
},
},
},
Nodes: specs.NodeList{
NewMockCall("first"),
NewMockCall("second"),
Expand Down Expand Up @@ -508,6 +534,23 @@ func TestGetAvailableResources(t *testing.T) {
result := GetAvailableResources(flow, "output")
return expected, result
},
"output only": func() ([]string, map[string]ReferenceMap) {
flow := NewMockFlow("first")

flow.OnError = nil
flow.Input = nil
flow.Nodes = nil
flow.Output = &specs.ParameterMap{
Stack: map[string]*specs.Property{
"hash": NewResultMockProperty(),
},
}

expected := []string{template.StackResource}

result := GetAvailableResources(flow, "output")
return expected, result
},
"stack lookup request": func() ([]string, map[string]ReferenceMap) {
flow := NewMockFlow("first")
expected := []string{template.StackResource, template.ErrorResource, "input", "first", "second", "third"}
Expand Down
16 changes: 16 additions & 0 deletions pkg/specs/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import (
"github.com/jexia/semaphore/pkg/broker/logger"
"github.com/jexia/semaphore/pkg/broker/trace"
"github.com/jexia/semaphore/pkg/specs"
"github.com/jexia/semaphore/pkg/specs/labels"
"github.com/jexia/semaphore/pkg/specs/types"
"go.uber.org/zap"
)

var (
// ReferencePattern is the matching pattern for references
ReferencePattern = regexp.MustCompile(`^[a-zA-Z0-9_\-\.]*:[a-zA-Z0-9\^\&\%\$@_\-\.]*$`)
// StringPattern is the matching pattern for strings
StringPattern = regexp.MustCompile(`^\'(.+)\'$`)
)

const (
Expand Down Expand Up @@ -117,6 +121,18 @@ func ParseContent(path string, name string, content string) (*specs.Property, er
return ParseReference(path, name, content)
}

if StringPattern.MatchString(content) {
matched := StringPattern.FindStringSubmatch(content)

return &specs.Property{
Name: name,
Path: path,
Type: types.String,
Label: labels.Optional,
Default: matched[1],
}, nil
}

return &specs.Property{
Name: name,
Path: path,
Expand Down
79 changes: 78 additions & 1 deletion pkg/specs/template/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"github.com/jexia/semaphore/pkg/broker"
"github.com/jexia/semaphore/pkg/broker/logger"
"github.com/jexia/semaphore/pkg/specs"
"github.com/jexia/semaphore/pkg/specs/labels"
"github.com/jexia/semaphore/pkg/specs/types"
)

func CompareProperties(t *testing.T, left specs.Property, right specs.Property) {
Expand Down Expand Up @@ -55,6 +57,81 @@ func TestGetTemplateContent(t *testing.T) {
}
}

func TestParseTemplateContent(t *testing.T) {
name := ""
path := "message"

tests := map[string]specs.Property{
"'prefix'": {
Name: name,
Path: path,
Type: types.String,
Label: labels.Optional,
Default: "prefix",
},
"'edge''": {
Name: name,
Path: path,
Type: types.String,
Label: labels.Optional,
Default: "edge'",
},
"input:message": {
Name: name,
Path: path,
Reference: &specs.PropertyReference{
Resource: "input",
Path: "message",
},
},
"input:user-id": {
Name: name,
Path: path,
Reference: &specs.PropertyReference{
Resource: "input",
Path: "user-id",
},
},
"input.header:Authorization": {
Name: name,
Path: path,
Reference: &specs.PropertyReference{
Resource: "input.header",
Path: "authorization",
},
},
"input.header:User-Id": {
Name: name,
Path: path,
Reference: &specs.PropertyReference{
Resource: "input.header",
Path: "user-id",
},
},
"input.header:": {
Path: path,
Reference: &specs.PropertyReference{
Resource: "input.header",
},
},
}

for input, expected := range tests {
t.Run(input, func(t *testing.T) {
property, err := ParseContent(path, name, input)
if err != nil {
t.Fatal(err)
}

if property.Path != expected.Path {
t.Errorf("unexpected path '%s', expected '%s'", property.Path, expected.Path)
}

CompareProperties(t, *property, expected)
})
}
}

func TestParseReference(t *testing.T) {
name := ""
path := "message"
Expand Down Expand Up @@ -166,7 +243,7 @@ func TestUnknownReferencePattern(t *testing.T) {
}
}

func TestParseTemplate(t *testing.T) {
func TestParseReferenceTemplates(t *testing.T) {
name := ""

tests := map[string]specs.Property{
Expand Down