github.com/graphql-editor/azure-functions-golang-worker@v0.1.0/worker/channel.go (about) 1 package worker 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 8 "github.com/graphql-editor/azure-functions-golang-worker/api" 9 "github.com/graphql-editor/azure-functions-golang-worker/function" 10 "github.com/graphql-editor/azure-functions-golang-worker/rpc" 11 "github.com/pkg/errors" 12 ) 13 14 type channel struct { 15 stream Sender 16 loader Loader 17 logger api.Logger 18 } 19 20 func (c *channel) StartStream(requestID string, msg *rpc.StartStream) { 21 // not yet implemented 22 } 23 24 func (c *channel) InitRequest(requestID string, msg *rpc.WorkerInitRequest) { 25 c.stream.Send(&rpc.StreamingMessage{ 26 RequestId: requestID, 27 Content: &rpc.StreamingMessage_WorkerInitResponse{ 28 WorkerInitResponse: &rpc.WorkerInitResponse{ 29 Result: c.getStatus(nil), 30 Capabilities: Capabilities{ 31 RPCHttpTriggerMetadataRemoved: "true", 32 RPCHttpBodyOnly: "true", 33 RawHTTPBodyBytes: "true", 34 }.ToRPC(), 35 }, 36 }, 37 }) 38 } 39 40 func (c *channel) Heartbeat(requestID string, msg *rpc.WorkerHeartbeat) { 41 // not yet implemented 42 } 43 44 func (c *channel) Terminate(requestID string, msg *rpc.WorkerTerminate) { 45 // not yet implemented 46 } 47 48 func (c *channel) StatusRequest(requestID string, msg *rpc.WorkerStatusRequest) { 49 // not yet implemented 50 } 51 52 func (c *channel) FileChangeEventRequest(requestID string, msg *rpc.FileChangeEventRequest) { 53 // not yet implemented 54 } 55 56 func (c *channel) FunctionLoadRequest(requestID string, msg *rpc.FunctionLoadRequest) { 57 functionID := msg.GetFunctionId() 58 metadata := msg.GetMetadata() 59 if functionID != "" && metadata != nil { 60 err := c.loader.Load(functionID, metadata, c.logger) 61 if err != nil { 62 c.logger.Error( 63 fmt.Sprintf( 64 "Worker was unable to load function %s: %v", 65 metadata.GetName(), 66 err, 67 ), 68 ) 69 } 70 c.stream.Send(&rpc.StreamingMessage{ 71 RequestId: requestID, 72 Content: &rpc.StreamingMessage_FunctionLoadResponse{ 73 FunctionLoadResponse: &rpc.FunctionLoadResponse{ 74 FunctionId: functionID, 75 Result: c.getStatus(err), 76 }, 77 }, 78 }) 79 } 80 } 81 82 func (c *channel) InvocationRequest(requestID string, msg *rpc.InvocationRequest) { 83 functionID := msg.GetFunctionId() 84 info, err := c.loader.Info(functionID) 85 outputData := make([]*rpc.ParameterBinding, 0, len(info.OutputBindings)) 86 var returnValue *rpc.TypedData 87 inputData := make([]function.BindingData, 0, len(msg.InputData)) 88 var triggerData *rpc.TypedData 89 var obj function.Object 90 if err == nil { 91 for _, binding := range msg.InputData { 92 if info.TriggerBindingName == binding.GetName() { 93 triggerData = binding.Data 94 } else { 95 inputData = append(inputData, function.BindingData{ 96 Name: binding.Name, 97 Data: binding.Data, 98 }) 99 } 100 } 101 } 102 if err == nil { 103 var objType function.ObjectType 104 objType, err = c.loader.Func(functionID) 105 if err == nil { 106 obj = objType.New() 107 err = obj.Call( 108 context.Background(), 109 Logger{ 110 InvocationID: msg.GetInvocationId(), 111 EventID: requestID, 112 Stream: c.stream, 113 Cat: rpc.RpcLog_User, 114 }, 115 triggerData, 116 msg.TriggerMetadata, 117 inputData..., 118 ) 119 } 120 } 121 if err == nil { 122 var ok bool 123 returnValue, ok, err = obj.ReturnValue() 124 if !ok && err == nil { 125 returnValue = nil 126 } 127 for name := range info.OutputBindings { 128 var outputValue *rpc.TypedData 129 outputValue, ok, err = obj.GetOutput(name) 130 if err != nil { 131 break 132 } 133 if ok { 134 outputData = append(outputData, &rpc.ParameterBinding{ 135 Name: name, 136 Data: outputValue, 137 }) 138 } 139 } 140 } 141 c.stream.Send(&rpc.StreamingMessage{ 142 RequestId: requestID, 143 Content: &rpc.StreamingMessage_InvocationResponse{ 144 InvocationResponse: &rpc.InvocationResponse{ 145 InvocationId: msg.GetInvocationId(), 146 OutputData: outputData, 147 ReturnValue: returnValue, 148 Result: c.getStatus(err), 149 }, 150 }, 151 }) 152 } 153 154 func (c *channel) InvocationCancel(requestID string, msg *rpc.InvocationCancel) { 155 // not yet implemented 156 } 157 158 func (c *channel) FunctionEnvironmentReloadRequest(requestID string, msg *rpc.FunctionEnvironmentReloadRequest) { 159 c.logger.Info(fmt.Sprintf("Reloading environment variables. Found %d variables to reload", len(msg.EnvironmentVariables))) 160 var err error 161 for k, v := range msg.EnvironmentVariables { 162 os.Setenv(k, v) 163 } 164 if msg.FunctionAppDirectory != "" { 165 c.logger.Info(fmt.Sprintf("Changing current working directory to %s", msg.FunctionAppDirectory)) 166 err = os.Chdir(msg.FunctionAppDirectory) 167 } 168 c.stream.Send(&rpc.StreamingMessage{ 169 RequestId: requestID, 170 Content: &rpc.StreamingMessage_FunctionEnvironmentReloadResponse{ 171 FunctionEnvironmentReloadResponse: &rpc.FunctionEnvironmentReloadResponse{ 172 Result: c.getStatus(err), 173 }, 174 }, 175 }) 176 } 177 178 type stackTracer interface { 179 StackTrace() errors.StackTrace 180 } 181 182 func (c *channel) getStatus(err error) *rpc.StatusResult { 183 status := &rpc.StatusResult{ 184 Status: rpc.StatusResult_Success, 185 } 186 if err != nil { 187 status.Status = rpc.StatusResult_Failure 188 status.Exception = &rpc.RpcException{ 189 Message: err.Error(), 190 } 191 if st, ok := err.(stackTracer); ok { 192 status.Exception.StackTrace = fmt.Sprintf("%+v", st.StackTrace()) 193 } 194 } 195 return status 196 } 197 198 func (c *channel) SetEventStream(stream Sender) { 199 c.stream = stream 200 c.logger = Logger{ 201 Stream: stream, 202 Cat: rpc.RpcLog_System, 203 } 204 } 205 206 func (c *channel) SetLoader(loader Loader) { 207 c.loader = loader 208 } 209 210 // NewChannel create new default channel 211 func NewChannel() Channel { 212 return &channel{} 213 }