fn icon indicating copy to clipboard operation
fn copied to clipboard

Unable to define CallOpts outside the package agent

Open reclaro opened this issue 5 years ago • 0 comments

The agent.CallOpt allows configuring a call before execution, its definition is the follow: type CallOpt func(c *call) error

It is possible then define new CallOpt that get executed during the GetCall call to customize or run custom code before the Call interface is returned. That is a very handy way to extend Fn and add additional operations just before a specific call is executed. Unfortunately the exported CallOpt type takes a private *call as parameter, that means that the all the CallOption must be defined in the agent package; that reduces drammatically the capacity to extend the code with custom specific options.

I have a couple of concrete use cases:

  1. before making a call I want to execute an authentication against an external service to get an authentication token that needs to be passed as extension to the runners. At the moment the only option I have is to use the almost-deprecated CallOverrider.
  2. I want to access the req.Context from inside my CallOpt but there is no way to access it as that is part of the agent.call which is not exported.

The first case will be a blocker for removing the CallOverrider and for the second case there is not a solution yet. Some ideas I explored to find a solution for the case 2:

Solution 1 Use Annotations. That could be a valid solutoin for a sub-set of cases but not a general one. Annotations have a limited lenght constrain and we can't guarantee that everything will be a good fit to be stored as an Annotation, for example can the Requiest.Context fits in an Annotation? We can't guarantee it for all cases

Solution 2 Add an field to the Call struct to store the original request. Again that will be a solution for case 2 but it doesn't cover all the cases where a CallOpt needs to access other fields of the agent.call

Solution 3 Export the agent.call. That will be a solution for all cases

Solution 4 Hacks. I was thinking to different ~~beautiful~~ ugly hack to workaround the problem, one of them is something like:

// GenericFunction is a hack to pass a generic function to be added as callOpt
type GenericFunction func(p ...interface{}) error

// WithGenericCallFn allows to pass a generic funcion which will be converted to a callOpts
func WithGenericCallFn(fn GenericFunction, params ...interface{}) CallOpt {
	co := func(c *call) error {
		return fn(params)
	}
	return co
}

That hack is a solution for some cases where the CallOpt function doesn't need to access the agent.call struct, in my specific case I need to use a CallOpt to acess the agent.call and using that hack won't work because the cast from the generic interface{} to the agent.call is not possible as the agent.call is not exported.

That is the results of few hours thinking and looking at the code to find a solution for my relative simple case, I might probably miss some other option/solution to this problem, if so please comment here, thanks.

reclaro avatar Mar 06 '19 14:03 reclaro