All files / libs/kernel/logger/server/src/lib/middleware inject-request-metadata.interceptor.ts

85.71% Statements 54/63
93.33% Branches 14/15
100% Functions 2/2
85.71% Lines 54/63

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 641x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 5x 5x 5x 5x 5x 5x 5x 4x 4x 4x 4x 4x 3x 3x 3x 3x 3x 4x           4x 2x 2x 2x 2x 2x 2x 4x 2x 2x 2x 2x 2x 4x 4x 5x 5x 5x         5x 5x 1x  
import { Injectable, type CallHandler, type ExecutionContext, type NestInterceptor } from '@nestjs/common';
import { type Request } from 'express';
import { PinoLogger } from 'nestjs-pino';
import { catchError, throwError, type Observable } from 'rxjs';
 
import { toError } from '@amalia/ext/typescript';
import { type AuthenticatedContext } from '@amalia/kernel/auth/types';
 
export type WithAuthenticatedContext<TRequest> = TRequest & { user: AuthenticatedContext | null };
 
@Injectable()
export class InjectRequestMetadataInterceptor implements NestInterceptor {
  public constructor(private readonly logger: PinoLogger) {}
 
  public intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
    const req = context.switchToHttp().getRequest<WithAuthenticatedContext<Request>>();
 
    const authenticatedContext = req.user;
 
    const { id: userId, company } = authenticatedContext?.user ?? {};
 
    if (authenticatedContext && (userId || company)) {
      const { id: companyId, name } = company ?? {};
 
      this.logger.assign({
        url: req.url,
        ...(userId && {
          user: {
            id: userId,
            email: authenticatedContext.user.email,
          },
        }),
        ...(authenticatedContext.impersonation && {
          impersonator: {
            id: authenticatedContext.impersonation.impersonator.id,
            email: authenticatedContext.impersonation.impersonator.email,
          },
        }),
        ...(authenticatedContext.meta && {
          meta: {
            ...(authenticatedContext.meta.amaliaImpersonatorEmail && {
              amaliaImpersonatorEmail: authenticatedContext.meta.amaliaImpersonatorEmail,
            }),
          },
        }),
        ...(companyId && {
          company: {
            id: companyId,
            name,
          },
        }),
      });
    }
 
    return next.handle().pipe(
      catchError((err) => {
        const error = toError(err);
        this.logger.error(error);
        return throwError(() => error);
      }),
    );
  }
}