blob: 57199b958425a7c35c8daa631d05ed2ccb33b477 [file] [log] [blame]
// Copyright 2022 Red Hat, Inc. and/or its affiliates
//
// 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.
package converters
import (
"context"
"errors"
"github.com/go-logr/logr"
apiv08 "github.com/kiegroup/kogito-serverless-operator/api/v1alpha08"
"github.com/kiegroup/kogito-serverless-operator/constants"
"github.com/serverlessworkflow/sdk-go/v2/model"
"path"
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"
"strings"
)
var log logr.Logger
type KogitoServerlessWorkflowConverter struct {
ctx context.Context
}
// NewKogitoServerlessWorkflowConverter ...
func NewKogitoServerlessWorkflowConverter(contex context.Context) KogitoServerlessWorkflowConverter {
return KogitoServerlessWorkflowConverter{ctx: contex}
}
// Function to convert a KogitoServerlessWorkflow object to a model.Workflow one in order to be able to convert it to a YAML/Json
func (k *KogitoServerlessWorkflowConverter) ToCNCFWorkflow(serverlessWorkflow *apiv08.KogitoServerlessWorkflow) (*model.Workflow, error) {
if serverlessWorkflow != nil {
log = ctrllog.FromContext(k.ctx)
newBaseWorkflow := &model.BaseWorkflow{ID: serverlessWorkflow.ObjectMeta.Name,
Key: serverlessWorkflow.ObjectMeta.Annotations[constants.WorkflowMetadataKeys()("key")],
Name: serverlessWorkflow.ObjectMeta.Name,
Description: serverlessWorkflow.ObjectMeta.Annotations[constants.WorkflowMetadataKeys()("description")],
Version: serverlessWorkflow.ObjectMeta.Annotations[constants.WorkflowMetadataKeys()("version")],
SpecVersion: extractSchemaVersion(serverlessWorkflow.APIVersion),
ExpressionLang: extractExpressionLang(serverlessWorkflow.ObjectMeta.Annotations),
KeepActive: serverlessWorkflow.Spec.KeepActive,
AutoRetries: serverlessWorkflow.Spec.AutoRetries,
Start: retrieveStartState(serverlessWorkflow.Spec.Start)}
log.Info("Created new Base Workflow with name", "name", newBaseWorkflow.Name)
newWorkflow := &model.Workflow{BaseWorkflow: *newBaseWorkflow, Functions: retrieveFunctions(serverlessWorkflow.Spec.Functions), States: retrieveStates(serverlessWorkflow.Spec.States)}
return newWorkflow, nil
}
return nil, errors.New(("KogitoServerlessWorkflow is nil"))
}
func extractExpressionLang(annotations map[string]string) string {
expressionLang := annotations[constants.WorkflowMetadataKeys()("expressionLang")]
if expressionLang != "" {
return expressionLang
}
return constants.DEFAULT_KOGITO_EXPLANG
}
// Function to extract from the apiVersion the ServerlessWorkflow schema version
// For example given sw.kogito.kie.org/apiv08 we would like to extract v0.8
func extractSchemaVersion(version string) string {
schemaVersion := path.Base(version)
strings.Replace(schemaVersion, "v0", "v0.", 1)
return schemaVersion
}
// Function to retrieve a Start object given the name of the start state
func retrieveStartState(name string) *model.Start {
start := &model.Start{StateName: name, Schedule: nil}
return start
}
// Function to retrieve a list of states coming from an array of v08.State objects
func retrieveStates(incomingStates []apiv08.State) []model.State {
states := make([]model.State, len(incomingStates))
log.Info("States: ", "states", incomingStates)
for i, s := range incomingStates {
stateT := model.StateType(s.Type.String())
newBaseState := &model.BaseState{Name: s.Name, Type: stateT}
if s.End {
newBaseState.End = &model.End{Terminate: true}
}
if s.Transition != nil {
newBaseState.Transition = &model.Transition{
NextState: *s.Transition,
}
}
switch sType := s.Type; sType {
case "switch":
newBaseSwitchState := &model.BaseSwitchState{
BaseState: *newBaseState,
DefaultCondition: model.DefaultCondition{},
}
if s.DataConditions != nil {
newSwitchState := &model.DataBasedSwitchState{BaseSwitchState: *newBaseSwitchState}
dataConditions := make([]model.DataCondition, len(*s.DataConditions))
for k, dc := range *s.DataConditions {
newBaseCondition := &model.BaseDataCondition{Condition: dc.Condition}
newTrasition := &model.Transition{NextState: dc.Transition}
dataConditions[k] = &model.TransitionDataCondition{
BaseDataCondition: *newBaseCondition,
Transition: *newTrasition,
}
}
if s.DefaultCondition != nil {
// Since at the moment we are not able yet to manage default condition that can be end or transition
// let's use Transition
newSwitchState.DefaultCondition = model.DefaultCondition{Transition: &model.Transition{
NextState: *s.DefaultCondition,
}}
}
newSwitchState.DataConditions = dataConditions
states[i] = newSwitchState
}
case "inject":
data := getData(*s.Data)
states[i] = &model.InjectState{BaseState: *newBaseState, Data: data}
case "operation":
var actions []model.Action
if s.Actions != nil {
actions = make([]model.Action, len(*s.Actions))
for k, ac := range *s.Actions {
action := &model.Action{
Name: ac.Name,
FunctionRef: &model.FunctionRef{RefName: ac.FunctionRef.RefName},
}
if &ac.FunctionRef != nil {
action.FunctionRef = &model.FunctionRef{RefName: ac.FunctionRef.RefName}
if ac.FunctionRef.Arguments != nil {
action.FunctionRef.Arguments = getArguments(ac.FunctionRef.Arguments)
}
}
actions[k] = *action
}
}
states[i] = &model.OperationState{BaseState: *newBaseState, Actions: actions}
default:
log.Info("Unable to create a CNCF State from incoming state type ", "type", sType)
}
}
return states
}
func getData(data map[string]string) map[string]interface{} {
out := make(map[string]interface{}, len(data))
for k, v := range data {
out[k] = v
}
return out
}
func getArguments(arguments map[string]string) map[string]interface{} {
out := make(map[string]interface{}, len(arguments))
for k, v := range arguments {
out[k] = v
}
return out
}
// Function to retrieve a list of model.Function coming from an array of v08.Function objects
func retrieveFunctions(incomingFunctions []apiv08.Function) []model.Function {
functions := make([]model.Function, len(incomingFunctions))
for i, f := range incomingFunctions {
switch ftype := f.Type; ftype {
default:
function := &model.Function{Name: f.Name, Type: model.FunctionType(f.Type), Operation: f.Operation}
functions[i] = *function
}
}
return functions
}