visit
async createPermamentCell(shapeId: number): Promise<Cell> {
try {
return await this.createCell(`shape-${shapeId}`, true);
} catch (error: any) {
// try auth again
if (error.status === 403) {
const userEmail = httpContext.get(REQUEST_USER);
const token = await this.authenticate(userEmail);
httpContext.set(CLIENT,
new Client({
token: token,
})
);
return await this.createCell(`shape-${shapeId}`, true);
}
}
}
// ...As well for other functions same try catch
async retryOn403(functionToCall: (...args: any[]) => any, ...args: any[]): Promise<Cell> {
try {
await functionToCall(...args);
} catch (error: any) {
// try auth again
if (error.status === 403) {
const userEmail = httpContext.get(REQUEST_USER);
const token = await this.authenticate(userEmail);
httpContext.set(CLIENT,
new Client({
token: token,
})
);
return await functionToCall(...args);
}
}
}
// The function will be wrapped
I require to create a new class and implement the interface InterceptorMethods
and use decorator @Interceptor()
.
@Interceptor()
export class ReAuthInterceptor implements InterceptorMethods {
constructor() {}
async intercept(context: InterceptorContext<any>, next: InterceptorNext) {
try {
// It's possible to do somthing before call
const result = await next();
// Or right after the call
return result;
} catch (error: any) {
if (error.status === 403) {
try {
const userEmail = httpContext.get(REQUEST_USER);
const token = await this.authenticate(userEmail);
httpContext.set(CLIENT,
new Client({
token: token,
})
);
return next();
} catch (error: any) {
_log.warn('Failed re auth', error);
}
// In case of exception on re auth return original exception
return next(error);
}
// All other cases immediately return error
return next(error);
}
}
}
And any service or particular function can be decorated with the new interceptor @Intercept(ReAuthInterceptor)
.
@Intercept(ReAuthInterceptor)
export class CellService implements CellService {
constructor(@Inject(ClientProvider) private clientProvider: ClientProvider) {}
// ... Many fucntions
}
In the context
parameter of interceptor you can find arguments or options
(the second parameter that you may use in the decorator @Intercept(ReAuthInterceptor, ‘My opts’)
. It could help to implement different logic for different functions.
There is one specific feature when you will try to use the interceptor in Ts.ED on the whole class with async and sync functions, e.g.:
@Intercept(ReAuthInterceptor)
export class CellService implements CellService {
constructor(@Inject(ClientProvider) private clientProvider: ClientProvider) {}
async createCell() {
// ...
}
getCurrentCell(): string {
// ...
return `some str`;
}
}
The actual result of the second function getCurrentCell()
will be not the string
, instead of this type, it will be Promise<string>
.
But otherwise, the code looks more understandable and robust via Intercept
approach, all things in one place, you can still process the functions differently, and test logic in one place.