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  }