diff --git a/packages/twenty-server/src/engine/core-modules/audit/audit-exception-filter.ts b/packages/twenty-server/src/engine/core-modules/audit/audit-exception-filter.ts new file mode 100644 index 000000000..af8d32731 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/audit/audit-exception-filter.ts @@ -0,0 +1,23 @@ +import { Catch, ExceptionFilter } from '@nestjs/common'; + +import { + AuditException, + AuditExceptionCode, +} from 'src/engine/core-modules/audit/audit.exception'; +import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; + +@Catch(AuditException) +export class AuditExceptionFilter implements ExceptionFilter { + catch(exception: AuditException) { + switch (exception.code) { + case AuditExceptionCode.INVALID_TYPE: + case AuditExceptionCode.INVALID_INPUT: + throw new UserInputError(exception.message); + default: { + const _exhaustiveCheck: never = exception.code; + + throw exception; + } + } + } +} diff --git a/packages/twenty-server/src/engine/core-modules/audit/audit.exception.ts b/packages/twenty-server/src/engine/core-modules/audit/audit.exception.ts index 0a4a0e351..fdb4ea65b 100644 --- a/packages/twenty-server/src/engine/core-modules/audit/audit.exception.ts +++ b/packages/twenty-server/src/engine/core-modules/audit/audit.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class AuditException extends CustomException { + declare code: AuditExceptionCode; constructor(message: string, code: AuditExceptionCode) { super(message, code); } diff --git a/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.ts b/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.ts index 8868416ee..cf40e45f2 100644 --- a/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.ts @@ -1,5 +1,7 @@ +import { UseFilters } from '@nestjs/common'; import { Args, Mutation, Resolver } from '@nestjs/graphql'; +import { AuditExceptionFilter } from 'src/engine/core-modules/audit/audit-exception-filter'; import { AuditException, AuditExceptionCode, @@ -19,6 +21,7 @@ import { Analytics } from './entities/analytics.entity'; import { AuditService } from './services/audit.service'; @Resolver(() => Analytics) +@UseFilters(AuditExceptionFilter) export class AuditResolver { constructor(private readonly auditService: AuditService) {} diff --git a/packages/twenty-server/src/engine/core-modules/audit/utils/analytics.utils.spec.ts b/packages/twenty-server/src/engine/core-modules/audit/utils/analytics.utils.spec.ts new file mode 100644 index 000000000..dab80fc16 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/audit/utils/analytics.utils.spec.ts @@ -0,0 +1,53 @@ +import { makePageview } from './analytics.utils'; + +describe('makePageview', () => { + it('should create a pageview with default properties when none are provided', () => { + const result = makePageview('test-page'); + + expect(result.name).toBe('test-page'); + expect(result.type).toBe('page'); + expect(result.properties).toEqual({ + href: '', + locale: '', + pathname: '', + referrer: '', + sessionId: '', + timeZone: '', + userAgent: '', + }); + expect(result.timestamp).toBeDefined(); + expect(result.version).toBe('1'); + }); + + it('should create a pageview with provided properties and fill in defaults for missing ones', () => { + const providedProperties = { + href: 'https://example.com', + sessionId: 'test-session-id', + }; + + const result = makePageview('test-page', providedProperties); + + expect(result.name).toBe('test-page'); + expect(result.type).toBe('page'); + expect(result.properties).toEqual({ + href: 'https://example.com', + locale: '', + pathname: '', + referrer: '', + sessionId: 'test-session-id', + timeZone: '', + userAgent: '', + }); + expect(result.timestamp).toBeDefined(); + expect(result.version).toBe('1'); + }); + + it('should handle empty properties object', () => { + const result = makePageview('test-page', {}); + + expect(result.name).toBe('test-page'); + expect(result.type).toBe('page'); + expect(result.properties.sessionId).toBe(''); + expect(result.properties.href).toBe(''); + }); +}); diff --git a/packages/twenty-server/src/engine/core-modules/audit/utils/events/pageview/pageview.ts b/packages/twenty-server/src/engine/core-modules/audit/utils/events/pageview/pageview.ts index 6ae385138..156143eaf 100644 --- a/packages/twenty-server/src/engine/core-modules/audit/utils/events/pageview/pageview.ts +++ b/packages/twenty-server/src/engine/core-modules/audit/utils/events/pageview/pageview.ts @@ -6,13 +6,13 @@ export const pageviewSchema = baseEventSchema.extend({ type: z.literal('page'), name: z.string(), properties: z.object({ - href: z.string(), - locale: z.string(), - pathname: z.string(), - referrer: z.string(), - sessionId: z.string(), - timeZone: z.string(), - userAgent: z.string(), + href: z.string().optional().default(''), + locale: z.string().optional().default(''), + pathname: z.string().optional().default(''), + referrer: z.string().optional().default(''), + sessionId: z.string().optional().default(''), + timeZone: z.string().optional().default(''), + userAgent: z.string().optional().default(''), }), });