Closed memoalv closed 8 months ago
The first problem is that you shouldn't reassign the def[Whatever]
variables since we're already returning references to their initial values (which is why you're seeing it only ever returning the first response). The second problem is that you're not throwing an error if the second attempt fails for the same isAuthError(response)
logic. Here's an attempt to fix your interceptor:
export default (): RpcInterceptor => ({
interceptUnary(
next: NextUnaryFn,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
method: MethodInfo<any, any>,
input: object,
options: RpcOptions,
): UnaryCall {
let defHeader = new Deferred<RpcMetadata>();
let defMessage = new Deferred<object>();
let defStatus = new Deferred<RpcStatus>();
let defTrailer = new Deferred<RpcMetadata>();
void (async () => {
try {
const first_try = await next(method, input, options);
// If the response doesn't have an auth error then resolve and exit.
if (!isAuthError(first_try.response)) {
defHeader.resolve(first_try.headers);
defMessage.resolve(first_try.response);
defStatus.resolve(first_try.status);
defTrailer.resolve(first_try.trailers);
return;
}
// Otherwise try again with a reset auth status
console.warn(`[${method.name}] auth error, unauthenticating & retrying...`);
const second_try = await next(
method,
{
...input,
authStatus: {},
},
options,
);
// If the second attempt's response also has an auth error then throw a custom RpcError.
if (isAuthError(second_try.response)) {
const error = new RpcError(
'The request does not have valid authentication credentials for the operation',
'UNAUTHENTICATED',
{...second_try.headers, ...second_try.trailers}
);
error.methodName = method.name;
error.serviceName = method.service.typeName;
throw error;
}
defHeader.resolve(second_try.headers);
defMessage.resolve(second_try.response);
defStatus.resolve(second_try.status);
defTrailer.resolve(second_try.trailers);
} catch (err) {
defHeader.rejectPending(err);
defMessage.rejectPending(err);
defStatus.rejectPending(err);
defTrailer.rejectPending(err);
}
})();
return new UnaryCall(
method,
options.meta ?? {},
input,
defHeader.promise,
defMessage.promise,
defStatus.promise,
defTrailer.promise,
);
},
});
Worked like a charm. Thank you very much for the pointers. I couldn't wrap my head around the def[Whatever]
variables.
I'm trying to retry a request if I detect an error in the response. The server returns this specific error as a successful response with a specific error code inside the message. I know its not ideal but its what we have now.
This is the code I have now:
I can definitely detect the error in the response and trigger the second RPC call but its response is ignored. It always returns whatever the first RPC call returns.
I've been going through other issues where interceptor examples are posted (that's how I've gotten this far) but still can't do what I need. Any help is appreciated.