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 }