github.com/erda-project/erda-infra@v1.0.9/providers/component-protocol/protocol/framework.go (about)

     1  // Copyright (c) 2021 Terminus, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package protocol
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"reflect"
    21  	"runtime/debug"
    22  
    23  	"github.com/sirupsen/logrus"
    24  
    25  	"github.com/erda-project/erda-infra/providers/component-protocol/cptype"
    26  	"github.com/erda-project/erda-infra/providers/component-protocol/utils/cputil"
    27  )
    28  
    29  const (
    30  	fieldStdStructuredPtr = "StdStructuredPtr"
    31  )
    32  
    33  // FRAMEWORK .
    34  type FRAMEWORK struct {
    35  	IC cptype.IComponent
    36  }
    37  
    38  // Render .
    39  func (F FRAMEWORK) Render(ctx context.Context, c *cptype.Component, scenario cptype.Scenario, event cptype.ComponentEvent, gs *cptype.GlobalStateData) (err error) {
    40  	defer func() {
    41  		if r := recover(); r != nil {
    42  			msg := fmt.Sprintf("component %s render panic: %v", c.Name, r)
    43  			logrus.Error(msg)
    44  			debug.PrintStack()
    45  			err = fmt.Errorf(msg)
    46  		}
    47  	}()
    48  	originSDK := cputil.SDK(ctx)
    49  	originSDK.GlobalState = gs
    50  	sdk := originSDK.Clone()
    51  	sdk.Comp = ensureCompFields(sdk, c)
    52  	sdk.Event = event
    53  	// structured comp ptr
    54  	stdStructuredCompPtr := F.IC.StdStructuredPtrCreator()()
    55  	F.injectStdStructurePtr(stdStructuredCompPtr)
    56  	sdk.StdStructuredPtr = stdStructuredCompPtr
    57  	// register operations
    58  	F.registerOperations(sdk)
    59  	// init
    60  	F.IC.Initialize(sdk)
    61  	// decoder
    62  	F.IC.DecodeInParams(sdk.InParams, stdStructuredCompPtr.InParamsPtr())
    63  	F.IC.DecodeState(c.State, stdStructuredCompPtr.StatePtr())
    64  	F.IC.DecodeData(c.Data, stdStructuredCompPtr.DataPtr())
    65  	// handle op
    66  	if !F.IC.SkipOp(sdk) {
    67  		F.IC.BeforeHandleOp(sdk)
    68  		F.handleOp(sdk, stdStructuredCompPtr)
    69  		F.IC.AfterHandleOp(sdk)
    70  		// encoder
    71  		ensureCompFieldsBeforeEncode(sdk)
    72  		F.IC.EncodeData(stdStructuredCompPtr.DataPtr(), &sdk.Comp.Data)
    73  		F.IC.EncodeState(stdStructuredCompPtr.StatePtr(), &sdk.Comp.State)
    74  		F.IC.EncodeInParams(stdStructuredCompPtr.InParamsPtr(), &sdk.InParams)
    75  		// flat extra
    76  		F.flatExtra(sdk.Comp)
    77  		// finalize
    78  		F.IC.Finalize(sdk)
    79  		// global state
    80  		sdk.MergeClonedGlobalState()
    81  	}
    82  	// visible
    83  	visible := F.IC.Visible(sdk)
    84  	F.setVisible(sdk, visible)
    85  	return nil
    86  }
    87  
    88  func (F FRAMEWORK) setVisible(sdk *cptype.SDK, visible bool) {
    89  	sdk.Comp.Options.Visible = visible
    90  }
    91  
    92  func ensureCompFields(sdk *cptype.SDK, comp *cptype.Component) *cptype.Component {
    93  	if sdk.InParams == nil {
    94  		sdk.InParams = make(cptype.InParams)
    95  	}
    96  	if sdk.GlobalState == nil {
    97  		sdk.GlobalState = &cptype.GlobalStateData{}
    98  	}
    99  	if sdk.CompOpFuncs == nil {
   100  		sdk.CompOpFuncs = make(map[cptype.OperationKey]cptype.OperationFunc)
   101  	}
   102  	if comp.Data == nil {
   103  		comp.Data = make(cptype.ComponentData)
   104  	}
   105  	if comp.State == nil {
   106  		comp.State = make(cptype.ComponentState)
   107  	}
   108  	if comp.Props == nil {
   109  		comp.Props = make(cptype.ComponentProps)
   110  	}
   111  	if comp.Operations == nil {
   112  		comp.Operations = make(cptype.ComponentOperations)
   113  	}
   114  	if comp.Options == nil {
   115  		comp.Options = &cptype.ComponentOptions{}
   116  	}
   117  	return comp
   118  }
   119  
   120  // ensureCompFieldsBeforeEncode ensure data/state/inParams to empty to avoid `omitempty` issue.
   121  func ensureCompFieldsBeforeEncode(sdk *cptype.SDK) {
   122  	sdk.Comp.Data = cptype.ComponentData{}
   123  	sdk.Comp.State = cptype.ComponentState{}
   124  	sdk.InParams = cptype.InParams{}
   125  }
   126  
   127  func (F FRAMEWORK) flatExtra(comp *cptype.Component) {
   128  	if !comp.Options.FlatExtra {
   129  		return
   130  	}
   131  	m := make(map[string]interface{})
   132  	cputil.MustObjJSONTransfer(comp, &m)
   133  	cputil.MustFlatMapMeta(m, comp.Options.RemoveExtraAfterFlat)
   134  	cputil.MustObjJSONTransfer(&m, comp)
   135  }
   136  
   137  func (F FRAMEWORK) registerOperations(sdk *cptype.SDK) {
   138  	sdk.RegisterOperation(cptype.InitializeOperation, F.IC.RegisterInitializeOp())
   139  	sdk.RegisterOperation(cptype.RenderingOperation, F.IC.RegisterRenderingOp())
   140  	// comp standard ops
   141  	for opKey, opFunc := range F.IC.RegisterCompStdOps() {
   142  		sdk.RegisterOperation(opKey, opFunc)
   143  	}
   144  	// comp non-standard ops
   145  	for opKey, opFunc := range F.IC.RegisterCompNonStdOps() {
   146  		sdk.RegisterOperation(opKey, opFunc)
   147  	}
   148  }
   149  
   150  func (F FRAMEWORK) handleOp(sdk *cptype.SDK, stdPtr cptype.IStdStructuredPtr) {
   151  	op := sdk.Event.Operation
   152  	opFunc, ok := sdk.CompOpFuncs[op]
   153  	if !ok {
   154  		panic(fmt.Errorf("component [%s] not supported operation [%s]", sdk.Comp.Name, op))
   155  	}
   156  	// nil opFunc equals to empty op func
   157  	if opFunc == nil {
   158  		return
   159  	}
   160  	// do op
   161  	stdResp := opFunc(sdk)
   162  	// if stdResp is not nil, set stdPtr to stdResp
   163  	if stdResp != nil {
   164  		reflect.ValueOf(stdPtr).Elem().Set(reflect.ValueOf(stdResp).Elem())
   165  	}
   166  }
   167  
   168  func (F FRAMEWORK) injectStdStructurePtr(stdPtr cptype.IStdStructuredPtr) {
   169  	// inject std ptr
   170  	reflect.ValueOf(F.IC).Elem().FieldByName(fieldStdStructuredPtr).Set(reflect.ValueOf(stdPtr))
   171  }