Skip to content

Commit

Permalink
fix(api): sentry throwing error on no user (#652)
Browse files Browse the repository at this point in the history
  • Loading branch information
timonmasberg committed Feb 1, 2024
1 parent beb0d5b commit 8683454
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 20 deletions.
4 changes: 0 additions & 4 deletions libs/api/auth/src/lib/interceptors/auth.interceptor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ describe('AuthInterceptor', () => {
service = new AuthInterceptor(mockAuthUserExtractor);
});

it('should be defined', () => {
expect(service).toBeDefined();
});

it('should throw unauthorized http exception', async () => {
jest
.spyOn(mockAuthUserExtractor, 'getUserFromRequest')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ describe('SentryOTelUserContextInterceptor', () => {
interceptor = new SentryOTelUserContextInterceptor();
});

it('should be defined', () => {
expect(interceptor).toBeDefined();
afterEach(() => {
jest.clearAllMocks();
});

it('should set user context and attributes for Sentry and OpenTelemetry', async () => {
Expand Down Expand Up @@ -66,4 +66,27 @@ describe('SentryOTelUserContextInterceptor', () => {
username: `${user.firstName} ${user.lastName}`,
});
});

it('should ignore user context and attributes for Sentry and OpenTelemetry if no user is present', async () => {
const ctx = createGqlContextForRequest(
createMock<KordisRequest>({
user: undefined,
}),
);
const handler = createMock<CallHandler>({
handle(): Observable<boolean> {
return of(true);
},
});

const sentrySetUserSpy = jest.spyOn(Sentry, 'setUser');
const getActiveSpanSpy = jest.spyOn(trace, 'getActiveSpan');

await expect(
firstValueFrom(interceptor.intercept(ctx, handler)),
).resolves.toBeTruthy();

expect(sentrySetUserSpy).not.toHaveBeenCalled();
expect(getActiveSpanSpy).not.toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,38 @@ import {
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { GqlContextType, GqlExecutionContext } from '@nestjs/graphql';
import { trace } from '@opentelemetry/api';
import * as Sentry from '@sentry/node';
import { Observable } from 'rxjs';

import { KordisGqlContext } from '@kordis/api/shared';
import { KordisGqlContext, KordisRequest } from '@kordis/api/shared';
import { AuthUser } from '@kordis/shared/auth';

@Injectable()
export class SentryOTelUserContextInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
const ctx = GqlExecutionContext.create(context);
const { user } = ctx.getContext<KordisGqlContext>().req;
let user: AuthUser | undefined; // user can be undefined, since we have routes passed through the auth interceptor which don't require a user (/health-check)
if (context.getType<GqlContextType>() === 'graphql') {
const ctx = GqlExecutionContext.create(context);
user = ctx.getContext<KordisGqlContext>().req.user;
} else {
user = context.switchToHttp().getRequest<KordisRequest>().user;
}

trace.getActiveSpan()?.setAttributes({
'user.id': user.id,
'user.email': user.email,
'user.name': `${user.firstName} ${user.lastName}`,
});
if (user) {
trace.getActiveSpan()?.setAttributes({
'user.id': user.id,
'user.email': user.email,
'user.name': `${user.firstName} ${user.lastName}`,
});

Sentry.setUser({
id: user.id,
email: user.email,
username: `${user.firstName} ${user.lastName}`,
});
Sentry.setUser({
id: user.id,
email: user.email,
username: `${user.firstName} ${user.lastName}`,
});
}

return next.handle();
}
Expand Down

0 comments on commit 8683454

Please sign in to comment.