From ff1d532f5cc48f8f8f751bc48bbec6a916097f46 Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Thu, 10 Oct 2024 18:52:48 -0400 Subject: [PATCH] Fix #213 - Review DataInputSchema unmarshal function; review k8s annotations for Object type Signed-off-by: Ricardo Zanini --- ...erlessworkflow.io_serverlessworkflows.yaml | 63 ++++++------------- model/action.go | 2 + model/common.go | 2 + model/event.go | 4 ++ model/inject_state.go | 3 +- model/object.go | 17 ++--- model/workflow.go | 10 ++- parser/parser_test.go | 11 ---- .../workflows/dataInputSchemaNotExists.yaml | 53 ++++++++++++++++ 9 files changed, 100 insertions(+), 65 deletions(-) create mode 100644 parser/testdata/workflows/dataInputSchemaNotExists.yaml diff --git a/config/crd/bases/serverlessworkflow.io_serverlessworkflows.yaml b/config/crd/bases/serverlessworkflow.io_serverlessworkflows.yaml index 5584a65..1dfc265 100644 --- a/config/crd/bases/serverlessworkflow.io_serverlessworkflows.yaml +++ b/config/crd/bases/serverlessworkflow.io_serverlessworkflows.yaml @@ -90,7 +90,7 @@ spec: failOnValidationErrors: type: boolean schema: - type: object + x-kubernetes-preserve-unknown-fields: true required: - failOnValidationErrors - schema @@ -357,17 +357,15 @@ spec: event definitions. properties: contextAttributes: - additionalProperties: - type: object description: Add additional extension context attributes to the produced event. - type: object + x-kubernetes-preserve-unknown-fields: true data: description: |- If string type, an expression which selects parts of the states data output to become the data (payload) of the event referenced by triggerEventRef. If object type, a custom object to become the data (payload) of the event referenced by triggerEventRef. - type: object + x-kubernetes-preserve-unknown-fields: true invoke: default: sync description: Specifies if the function should be @@ -397,11 +395,9 @@ spec: description: References a reusable function definition. properties: arguments: - additionalProperties: - type: object description: Arguments (inputs) to be passed to the referenced function - type: object + x-kubernetes-preserve-unknown-fields: true invoke: default: sync description: Specifies if the function should be @@ -630,17 +626,15 @@ spec: reusable event definitions. properties: contextAttributes: - additionalProperties: - type: object description: Add additional extension context attributes to the produced event. - type: object + x-kubernetes-preserve-unknown-fields: true data: description: |- If string type, an expression which selects parts of the states data output to become the data (payload) of the event referenced by triggerEventRef. If object type, a custom object to become the data (payload) of the event referenced by triggerEventRef. - type: object + x-kubernetes-preserve-unknown-fields: true invoke: default: sync description: Specifies if the function should @@ -671,11 +665,9 @@ spec: definition. properties: arguments: - additionalProperties: - type: object description: Arguments (inputs) to be passed to the referenced function - type: object + x-kubernetes-preserve-unknown-fields: true invoke: default: sync description: Specifies if the function should @@ -875,17 +867,15 @@ spec: event definitions. properties: contextAttributes: - additionalProperties: - type: object description: Add additional extension context attributes to the produced event. - type: object + x-kubernetes-preserve-unknown-fields: true data: description: |- If string type, an expression which selects parts of the states data output to become the data (payload) of the event referenced by triggerEventRef. If object type, a custom object to become the data (payload) of the event referenced by triggerEventRef. - type: object + x-kubernetes-preserve-unknown-fields: true invoke: default: sync description: Specifies if the function should @@ -915,11 +905,9 @@ spec: description: References a reusable function definition. properties: arguments: - additionalProperties: - type: object description: Arguments (inputs) to be passed to the referenced function - type: object + x-kubernetes-preserve-unknown-fields: true invoke: default: sync description: Specifies if the function should @@ -1074,12 +1062,9 @@ spec: data input. properties: data: - additionalProperties: - type: object description: JSON object which can be set as state's data input and can be manipulated via filter - minProperties: 1 - type: object + x-kubernetes-preserve-unknown-fields: true timeouts: description: State specific timeouts properties: @@ -1189,17 +1174,15 @@ spec: event definitions. properties: contextAttributes: - additionalProperties: - type: object description: Add additional extension context attributes to the produced event. - type: object + x-kubernetes-preserve-unknown-fields: true data: description: |- If string type, an expression which selects parts of the states data output to become the data (payload) of the event referenced by triggerEventRef. If object type, a custom object to become the data (payload) of the event referenced by triggerEventRef. - type: object + x-kubernetes-preserve-unknown-fields: true invoke: default: sync description: Specifies if the function should @@ -1229,11 +1212,9 @@ spec: description: References a reusable function definition. properties: arguments: - additionalProperties: - type: object description: Arguments (inputs) to be passed to the referenced function - type: object + x-kubernetes-preserve-unknown-fields: true invoke: default: sync description: Specifies if the function should @@ -1401,17 +1382,15 @@ spec: reusable event definitions. properties: contextAttributes: - additionalProperties: - type: object description: Add additional extension context attributes to the produced event. - type: object + x-kubernetes-preserve-unknown-fields: true data: description: |- If string type, an expression which selects parts of the states data output to become the data (payload) of the event referenced by triggerEventRef. If object type, a custom object to become the data (payload) of the event referenced by triggerEventRef. - type: object + x-kubernetes-preserve-unknown-fields: true invoke: default: sync description: Specifies if the function should @@ -1442,11 +1421,9 @@ spec: definition. properties: arguments: - additionalProperties: - type: object description: Arguments (inputs) to be passed to the referenced function - type: object + x-kubernetes-preserve-unknown-fields: true invoke: default: sync description: Specifies if the function should @@ -1675,7 +1652,7 @@ spec: description: |- If string type, an expression which selects parts of the states data output to become the workflow data input of continued execution. If object type, a custom object to become the workflow data input of the continued execution - type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version of the workflow to continue execution as. @@ -1730,7 +1707,7 @@ spec: description: |- If String, expression which selects parts of the states data output to become the data of the produced event. If object a custom object to become the data of produced event. - type: object + x-kubernetes-preserve-unknown-fields: true eventRef: description: Reference to a defined unique event name in the events definition @@ -1784,7 +1761,7 @@ spec: description: |- If String, expression which selects parts of the states data output to become the data of the produced event. If object a custom object to become the data of produced event. - type: object + x-kubernetes-preserve-unknown-fields: true eventRef: description: Reference to a defined unique event name in the events definition diff --git a/model/action.go b/model/action.go index 7bc4fba..2635849 100644 --- a/model/action.go +++ b/model/action.go @@ -80,6 +80,8 @@ type FunctionRef struct { RefName string `json:"refName" validate:"required"` // Arguments (inputs) to be passed to the referenced function // +optional + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields // TODO: validate it as required if function type is graphql Arguments map[string]Object `json:"arguments,omitempty"` // Used if function type is graphql. String containing a valid GraphQL selection set. diff --git a/model/common.go b/model/common.go index 6993de7..3d4f000 100644 --- a/model/common.go +++ b/model/common.go @@ -24,4 +24,6 @@ type Common struct { } // Metadata information +// +kubebuilder:pruning:PreserveUnknownFields +// +kubebuilder:validation:Schemaless type Metadata map[string]Object diff --git a/model/event.go b/model/event.go index 96069bf..bad1ce4 100644 --- a/model/event.go +++ b/model/event.go @@ -106,9 +106,13 @@ type EventRef struct { // of the event referenced by triggerEventRef. If object type, a custom object to become the data (payload) // of the event referenced by triggerEventRef. // +optional + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields Data *Object `json:"data,omitempty"` // Add additional extension context attributes to the produced event. // +optional + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields ContextAttributes map[string]Object `json:"contextAttributes,omitempty"` // Specifies if the function should be invoked sync or async. Default is sync. // +kubebuilder:validation:Enum=async;sync diff --git a/model/inject_state.go b/model/inject_state.go index a195423..e3995c8 100644 --- a/model/inject_state.go +++ b/model/inject_state.go @@ -21,7 +21,8 @@ import ( // InjectState used to inject static data into state data input. type InjectState struct { // JSON object which can be set as state's data input and can be manipulated via filter - // +kubebuilder:validation:MinProperties=1 + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields Data map[string]Object `json:"data" validate:"required,min=1"` // State specific timeouts // +optional diff --git a/model/object.go b/model/object.go index b8360a7..e19d7b0 100644 --- a/model/object.go +++ b/model/object.go @@ -44,15 +44,16 @@ const ( // - Integer - holds int32 values, JSON marshal any number to float64 by default, during the marshaling process it is // parsed to int32 // -// +kubebuilder:validation:Type=object +// +kubebuilder:pruning:PreserveUnknownFields +// +kubebuilder:validation:Schemaless type Object struct { - Type Type `json:"type,inline"` - StringValue string `json:"strVal,inline"` - IntValue int32 `json:"intVal,inline"` - FloatValue float64 - MapValue map[string]Object - SliceValue []Object - BoolValue bool `json:"boolValue,inline"` + Type Type `json:"type,inline"` + StringValue string `json:"strVal,inline"` + IntValue int32 `json:"intVal,inline"` + FloatValue float64 `json:"floatVal,inline"` + MapValue map[string]Object `json:"mapVal,inline"` + SliceValue []Object `json:"sliceVal,inline"` + BoolValue bool `json:"boolValue,inline"` } // UnmarshalJSON implements json.Unmarshaler diff --git a/model/workflow.go b/model/workflow.go index aa72d1f..54723bb 100644 --- a/model/workflow.go +++ b/model/workflow.go @@ -154,9 +154,9 @@ type BaseWorkflow struct { // +optional KeepActive bool `json:"keepActive,omitempty"` // Metadata custom information shared with the runtime. + // +optional // +kubebuilder:validation:Schemaless // +kubebuilder:pruning:PreserveUnknownFields - // +optional Metadata Metadata `json:"metadata,omitempty"` // AutoRetries If set to true, actions should automatically be retried on unchecked errors. Default is false // +optional @@ -471,6 +471,8 @@ type ContinueAs struct { // If string type, an expression which selects parts of the states data output to become the workflow data input of // continued execution. If object type, a custom object to become the workflow data input of the continued execution // +optional + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields Data Object `json:"data,omitempty"` // WorkflowExecTimeout Workflow execution timeout to be used by the workflow continuing execution. // Overwrites any specific settings set by that workflow @@ -495,6 +497,8 @@ type ProduceEvent struct { // If String, expression which selects parts of the states data output to become the data of the produced event. // If object a custom object to become the data of produced event. // +optional + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields Data Object `json:"data,omitempty"` // Add additional event extension context attributes. // +optional @@ -513,6 +517,8 @@ type StateDataFilter struct { // +builder-gen:new-call=ApplyDefault type DataInputSchema struct { // +kubebuilder:validation:Required + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields Schema *Object `json:"schema" validate:"required"` // +kubebuilder:validation:Required FailOnValidationErrors bool `json:"failOnValidationErrors"` @@ -557,7 +563,7 @@ func (d *DataInputSchema) UnmarshalJSON(data []byte) error { } d.Schema = new(Object) - return util.UnmarshalObjectOrFile("schema", data, &d.Schema) + return util.UnmarshalObject("schema", data, &d.Schema) } // ApplyDefault set the default values for Data Input Schema diff --git a/parser/parser_test.go b/parser/parser_test.go index 760f181..daf6608 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -581,17 +581,6 @@ func TestFromFile(t *testing.T) { assert.Equal(t, "SendTextForHighPriority", w.States[10].SwitchState.DefaultCondition.Transition.NextState) assert.Equal(t, true, w.States[10].End.Terminate) }, - }, { - "./testdata/workflows/dataInputSchemaValidation.yaml", func(t *testing.T, w *model.Workflow) { - assert.NotNil(t, w.DataInputSchema) - expected := model.DataInputSchema{} - data, err := util.LoadExternalResource("file://testdata/datainputschema.json") - err1 := util.UnmarshalObject("schema", data, &expected.Schema) - assert.Nil(t, err) - assert.Nil(t, err1) - assert.Equal(t, expected.Schema, w.DataInputSchema.Schema) - assert.Equal(t, false, w.DataInputSchema.FailOnValidationErrors) - }, }, { "./testdata/workflows/dataInputSchemaObject.json", func(t *testing.T, w *model.Workflow) { assert.NotNil(t, w.DataInputSchema) diff --git a/parser/testdata/workflows/dataInputSchemaNotExists.yaml b/parser/testdata/workflows/dataInputSchemaNotExists.yaml new file mode 100644 index 0000000..7aa3712 --- /dev/null +++ b/parser/testdata/workflows/dataInputSchemaNotExists.yaml @@ -0,0 +1,53 @@ +# Copyright 2024 The Serverless Workflow Specification Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +id: orderprocessing +version: '1.0' +specVersion: '0.8' +start: ChooseOnLanguage +dataInputSchema: + schema: doesnexist.json + failOnValidationErrors: true +functions: + - name: greetFunction + type: custom + operation: sysout +states: + - name: ChooseOnLanguage + type: switch + dataConditions: + - condition: "${ .language == \"English\" }" + transition: GreetInEnglish + - condition: "${ .language == \"Spanish\" }" + transition: GreetInSpanish + defaultCondition: GreetInEnglish + - name: GreetInEnglish + type: inject + data: + greeting: "Hello from JSON Workflow, " + transition: GreetPerson + - name: GreetInSpanish + type: inject + data: + greeting: "Saludos desde JSON Workflow, " + transition: GreetPerson + - name: GreetPerson + type: operation + actions: + - name: greetAction + functionRef: + refName: greetFunction + arguments: + message: ".greeting+.name" + end: true \ No newline at end of file