Skip to content

Commit 7a4efe1

Browse files
committed
allow empty auth definitions in parser
fixes #110 fixes #126 Signed-off-by: spolti <[email protected]>
1 parent 8f9bf0c commit 7a4efe1

File tree

4 files changed

+179
-154
lines changed

4 files changed

+179
-154
lines changed

model/auth.go

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -17,36 +17,8 @@ package model
1717
import (
1818
"encoding/json"
1919
"fmt"
20-
"reflect"
21-
22-
"github.com/go-playground/validator/v10"
23-
24-
val "github.com/serverlessworkflow/sdk-go/v2/validator"
2520
)
2621

27-
func init() {
28-
val.GetValidator().RegisterStructValidation(AuthDefinitionsStructLevelValidation, AuthDefinitions{})
29-
}
30-
31-
// AuthDefinitionsStructLevelValidation custom validator for unique name of the auth methods
32-
func AuthDefinitionsStructLevelValidation(structLevel validator.StructLevel) {
33-
authDefs := structLevel.Current().Interface().(AuthDefinitions)
34-
dict := map[string]bool{}
35-
36-
for _, a := range authDefs.Defs {
37-
if !dict[a.Name] {
38-
dict[a.Name] = true
39-
} else {
40-
structLevel.ReportError(reflect.ValueOf(a.Name), "Name", "name", "reqnameunique", "")
41-
}
42-
}
43-
}
44-
45-
// AuthDefinitions used to define authentication information applied to resources defined in the operation property of function definitions
46-
type AuthDefinitions struct {
47-
Defs []Auth `json:"defs,omitempty"`
48-
}
49-
5022
// AuthType ...
5123
type AuthType string
5224

@@ -93,47 +65,6 @@ type Auth struct {
9365
Properties AuthProperties `json:"properties" validate:"required"`
9466
}
9567

96-
// UnmarshalJSON implements json.Unmarshaler
97-
func (a *AuthDefinitions) UnmarshalJSON(b []byte) error {
98-
if len(b) == 0 {
99-
// TODO: Normalize error messages
100-
return fmt.Errorf("no bytes to unmarshal")
101-
}
102-
103-
// See if we can guess based on the first character
104-
switch b[0] {
105-
case '"':
106-
return a.unmarshalFile(b)
107-
case '[':
108-
return a.unmarshalMany(b)
109-
case '{': // single values are not supported, should we support it?
110-
return fmt.Errorf("authDefinitions value '%s' is not supported, it must be an object or string", string(b))
111-
default:
112-
// nil can be returned as both cases are returning errors in case of unmarshal problem.
113-
return nil
114-
}
115-
}
116-
117-
func (a *AuthDefinitions) unmarshalFile(data []byte) error {
118-
b, err := unmarshalFile(data)
119-
if err != nil {
120-
return fmt.Errorf("authDefinitions value '%s' is not supported, it must be an object or string", string(data))
121-
}
122-
123-
return a.unmarshalMany(b)
124-
}
125-
126-
func (a *AuthDefinitions) unmarshalMany(data []byte) error {
127-
var auths []Auth
128-
err := json.Unmarshal(data, &auths)
129-
if err != nil {
130-
return fmt.Errorf("authDefinitions value '%s' is not supported, it must be an object or string", string(data))
131-
}
132-
133-
a.Defs = auths
134-
return nil
135-
}
136-
13768
// UnmarshalJSON Auth definition
13869
func (a *Auth) UnmarshalJSON(data []byte) error {
13970
auth := make(map[string]json.RawMessage)

model/auth_test.go

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -19,79 +19,8 @@ import (
1919
"testing"
2020

2121
"github.com/stretchr/testify/assert"
22-
23-
val "github.com/serverlessworkflow/sdk-go/v2/validator"
2422
)
2523

26-
func TestAuthDefinitionsStructLevelValidation(t *testing.T) {
27-
type testCase struct {
28-
desp string
29-
authDefs AuthDefinitions
30-
err string
31-
}
32-
testCases := []testCase{
33-
{
34-
desp: "nil defs",
35-
authDefs: AuthDefinitions{},
36-
err: ``,
37-
},
38-
{
39-
desp: "zero length defs",
40-
authDefs: AuthDefinitions{
41-
Defs: []Auth{},
42-
},
43-
err: ``,
44-
},
45-
{
46-
desp: "multi unique defs",
47-
authDefs: AuthDefinitions{
48-
Defs: []Auth{
49-
{
50-
Name: "1",
51-
},
52-
{
53-
Name: "2",
54-
},
55-
{
56-
Name: "3",
57-
},
58-
},
59-
},
60-
err: ``,
61-
},
62-
{
63-
desp: "multi non-unique defs",
64-
authDefs: AuthDefinitions{
65-
Defs: []Auth{
66-
{
67-
Name: "1",
68-
},
69-
{
70-
Name: "2",
71-
},
72-
{
73-
Name: "1",
74-
},
75-
},
76-
},
77-
err: `Key: 'AuthDefinitions.Name' Error:Field validation for 'Name' failed on the 'reqnameunique' tag`,
78-
},
79-
}
80-
for _, tc := range testCases {
81-
t.Run(tc.desp, func(t *testing.T) {
82-
err := val.GetValidator().Struct(tc.authDefs)
83-
84-
if tc.err != "" {
85-
assert.Error(t, err)
86-
assert.Regexp(t, tc.err, err)
87-
return
88-
}
89-
90-
assert.NoError(t, err)
91-
})
92-
}
93-
}
94-
9524
func TestUnmarshalJSONMultipleAuthProperties(t *testing.T) {
9625
t.Run("BearerAuthProperties", func(t *testing.T) {
9726
a1JSON := `{

model/workflow.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ type BaseWorkflow struct {
108108
// Auth definitions can be used to define authentication information that should be applied to resources defined in the operation
109109
// property of function definitions. It is not used as authentication information for the function invocation,
110110
// but just to access the resource containing the function invocation information.
111-
Auth *AuthDefinitions `json:"auth,omitempty"`
111+
Auth interface{} `json:"auth,omitempty"`
112112
}
113113

114114
// Workflow base definition
@@ -130,6 +130,34 @@ func (w *Workflow) UnmarshalJSON(data []byte) error {
130130
if err := json.Unmarshal(data, &workflowMap); err != nil {
131131
return err
132132
}
133+
134+
if len(workflowMap["auth"]) > 0 {
135+
var auth []*Auth
136+
if err := json.Unmarshal(workflowMap["auth"], &auth); err != nil {
137+
138+
b, err := unmarshalFile(workflowMap["auth"])
139+
if err != nil {
140+
return fmt.Errorf("auth value '%s' is not supported, it must be an object or string", string(workflowMap["auth"]))
141+
}
142+
err = json.Unmarshal(b, &auth)
143+
if err != nil {
144+
return fmt.Errorf("auth value '%s' is not supported, it must be an object or string", string(b))
145+
}
146+
}
147+
148+
// detect duplicated auth.Name
149+
dict := map[string]bool{}
150+
for _, a := range auth {
151+
if !dict[a.Name] {
152+
dict[a.Name] = true
153+
} else {
154+
// TODO is there a way to report error with validator here?
155+
return fmt.Errorf("auth field '%s.Name' must be unique", reflect.TypeOf(a))
156+
}
157+
}
158+
w.BaseWorkflow.Auth = auth
159+
}
160+
133161
var rawStates []json.RawMessage
134162
if err := json.Unmarshal(workflowMap["states"], &rawStates); err != nil {
135163
return err

parser/parser_test.go

Lines changed: 150 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
package parser
1616

1717
import (
18+
"encoding/json"
1819
"os"
1920
"path/filepath"
21+
"strings"
2022
"testing"
2123

2224
"github.com/stretchr/testify/assert"
@@ -171,11 +173,11 @@ func TestFromFile(t *testing.T) {
171173
assert.NotEmpty(t, operationState.Actions)
172174
assert.Equal(t, "startApplicationWorkflowId", operationState.Actions[0].SubFlowRef.WorkflowID)
173175
assert.NotNil(t, w.Auth)
174-
assert.NotNil(t, w.Auth.Defs)
175-
assert.Equal(t, len(w.Auth.Defs), 1)
176-
assert.Equal(t, "testAuth", w.Auth.Defs[0].Name)
177-
assert.Equal(t, model.AuthTypeBearer, w.Auth.Defs[0].Scheme)
178-
bearerProperties := w.Auth.Defs[0].Properties.(*model.BearerAuthProperties).Token
176+
auth := w.Auth.([]*model.Auth)
177+
assert.Equal(t, len(auth), 1)
178+
assert.Equal(t, "testAuth", auth[0].Name)
179+
assert.Equal(t, model.AuthTypeBearer, auth[0].Scheme)
180+
bearerProperties := auth[0].Properties.(*model.BearerAuthProperties).Token
179181
assert.Equal(t, "test_token", bearerProperties)
180182
},
181183
}, {
@@ -194,15 +196,15 @@ func TestFromFile(t *testing.T) {
194196
assert.NotEmpty(t, operationState.Actions)
195197
assert.Equal(t, "startApplicationWorkflowId", operationState.Actions[0].SubFlowRef.WorkflowID)
196198
assert.NotNil(t, w.Auth)
197-
assert.NotNil(t, w.Auth.Defs)
198-
assert.Equal(t, len(w.Auth.Defs), 2)
199-
assert.Equal(t, "testAuth", w.Auth.Defs[0].Name)
200-
assert.Equal(t, model.AuthTypeBearer, w.Auth.Defs[0].Scheme)
201-
bearerProperties := w.Auth.Defs[0].Properties.(*model.BearerAuthProperties).Token
199+
auth := w.Auth.([]*model.Auth)
200+
assert.Equal(t, len(auth), 2)
201+
assert.Equal(t, "testAuth", auth[0].Name)
202+
assert.Equal(t, model.AuthTypeBearer, auth[0].Scheme)
203+
bearerProperties := auth[0].Properties.(*model.BearerAuthProperties).Token
202204
assert.Equal(t, "test_token", bearerProperties)
203-
assert.Equal(t, "testAuth2", w.Auth.Defs[1].Name)
204-
assert.Equal(t, model.AuthTypeBasic, w.Auth.Defs[1].Scheme)
205-
basicProperties := w.Auth.Defs[1].Properties.(*model.BasicAuthProperties)
205+
assert.Equal(t, "testAuth2", auth[1].Name)
206+
assert.Equal(t, model.AuthTypeBasic, auth[1].Scheme)
207+
basicProperties := auth[1].Properties.(*model.BasicAuthProperties)
206208
assert.Equal(t, "test_user", basicProperties.Username)
207209
assert.Equal(t, "test_pwd", basicProperties.Password)
208210
},
@@ -497,3 +499,138 @@ func TestFromFile(t *testing.T) {
497499
)
498500
}
499501
}
502+
503+
func TestUnmarshalWorkflowBasicTests(t *testing.T) {
504+
t.Run("BasicWorkflowYamlNoAuthDefs", func(t *testing.T) {
505+
workflow, err := FromYAMLSource([]byte(`
506+
id: helloworld
507+
version: '1.0.0'
508+
specVersion: '0.8'
509+
name: Hello World Workflow
510+
description: Inject Hello World
511+
start: Hello State
512+
states:
513+
- name: Hello State
514+
type: inject
515+
data:
516+
result: Hello World!
517+
end: true
518+
`))
519+
assert.Nil(t, err)
520+
assert.NotNil(t, workflow)
521+
522+
b, err := json.Marshal(workflow)
523+
assert.Nil(t, err)
524+
assert.True(t, !strings.Contains(string(b), "auth"))
525+
526+
workflow = nil
527+
err = json.Unmarshal(b, &workflow)
528+
assert.Nil(t, err)
529+
})
530+
531+
t.Run("BasicWorkflowBasicAuthJSONSource", func(t *testing.T) {
532+
workflow, err := FromJSONSource([]byte(`
533+
{
534+
"id": "applicantrequest",
535+
"version": "1.0",
536+
"name": "Applicant Request Decision Workflow",
537+
"description": "Determine if applicant request is valid",
538+
"start": "CheckApplication",
539+
"specVersion": "0.8",
540+
"auth": [
541+
{
542+
"name": "testAuth",
543+
"scheme": "bearer",
544+
"properties": {
545+
"token": "test_token"
546+
}
547+
},
548+
{
549+
"name": "testAuth2",
550+
"scheme": "basic",
551+
"properties": {
552+
"username": "test_user",
553+
"password": "test_pwd"
554+
}
555+
}
556+
],
557+
"states": [
558+
{
559+
"name": "Hello State",
560+
"type": "inject",
561+
"data": {
562+
"result": "Hello World!"
563+
},
564+
"end": true
565+
}
566+
]
567+
}
568+
`))
569+
assert.Nil(t, err)
570+
assert.NotNil(t, workflow.Auth)
571+
572+
b, _ := json.Marshal(workflow)
573+
assert.Equal(t, "{\"id\":\"applicantrequest\",\"name\":\"Applicant Request Decision Workflow\",\"description\":\"Determine if applicant request is valid\",\"version\":\"1.0\",\"start\":{\"stateName\":\"CheckApplication\"},\"specVersion\":\"0.8\",\"expressionLang\":\"jq\",\"auth\":[{\"name\":\"testAuth\",\"scheme\":\"bearer\",\"properties\":{\"token\":\"test_token\"}},{\"name\":\"testAuth2\",\"scheme\":\"basic\",\"properties\":{\"username\":\"test_user\",\"password\":\"test_pwd\"}}],\"states\":[{\"name\":\"Hello State\",\"type\":\"inject\",\"end\":{},\"data\":{\"result\":\"Hello World!\"}}]}",
574+
string(b))
575+
576+
})
577+
578+
t.Run("BasicWorkflowBasicAuthStringJSONSource", func(t *testing.T) {
579+
workflow, err := FromJSONSource([]byte(`
580+
{
581+
"id": "applicantrequest",
582+
"version": "1.0",
583+
"name": "Applicant Request Decision Workflow",
584+
"description": "Determine if applicant request is valid",
585+
"start": "CheckApplication",
586+
"specVersion": "0.8",
587+
"auth": "./testdata/workflows/urifiles/auth.json",
588+
"states": [
589+
{
590+
"name": "Hello State",
591+
"type": "inject",
592+
"data": {
593+
"result": "Hello World!"
594+
},
595+
"end": true
596+
}
597+
]
598+
}
599+
`))
600+
assert.Nil(t, err)
601+
assert.NotNil(t, workflow.Auth)
602+
603+
b, _ := json.Marshal(workflow)
604+
assert.Equal(t, "{\"id\":\"applicantrequest\",\"name\":\"Applicant Request Decision Workflow\",\"description\":\"Determine if applicant request is valid\",\"version\":\"1.0\",\"start\":{\"stateName\":\"CheckApplication\"},\"specVersion\":\"0.8\",\"expressionLang\":\"jq\",\"auth\":[{\"name\":\"testAuth\",\"scheme\":\"bearer\",\"properties\":{\"token\":\"test_token\"}},{\"name\":\"testAuth2\",\"scheme\":\"basic\",\"properties\":{\"username\":\"test_user\",\"password\":\"test_pwd\"}}],\"states\":[{\"name\":\"Hello State\",\"type\":\"inject\",\"end\":{},\"data\":{\"result\":\"Hello World!\"}}]}",
605+
string(b))
606+
607+
})
608+
609+
t.Run("BasicWorkflowInteger", func(t *testing.T) {
610+
workflow, err := FromJSONSource([]byte(`
611+
{
612+
"id": "applicantrequest",
613+
"version": "1.0",
614+
"name": "Applicant Request Decision Workflow",
615+
"description": "Determine if applicant request is valid",
616+
"start": "CheckApplication",
617+
"specVersion": "0.7",
618+
"auth": 123,
619+
"states": [
620+
{
621+
"name": "Hello State",
622+
"type": "inject",
623+
"data": {
624+
"result": "Hello World!"
625+
},
626+
"end": true
627+
}
628+
]
629+
}
630+
`))
631+
632+
assert.NotNil(t, err)
633+
assert.Equal(t, "auth value '123' is not supported, it must be an object or string", err.Error())
634+
assert.Nil(t, workflow)
635+
})
636+
}

0 commit comments

Comments
 (0)