fn
fn copied to clipboard
Unable to define CallOpts outside the package agent
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:
- 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
. - I want to access the
req.Context
from inside myCallOpt
but there is no way to access it as that is part of theagent.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.