diff --git a/src/lib/features/events/event-service.ts b/src/lib/features/events/event-service.ts index 72b5e09c9147..885c2888e7aa 100644 --- a/src/lib/features/events/event-service.ts +++ b/src/lib/features/events/event-service.ts @@ -13,7 +13,7 @@ import { ApiTokenType } from '../../types/models/api-token'; import { EVENTS_CREATED_BY_PROCESSED } from '../../metric-events'; import type { IQueryParam } from '../feature-toggle/types/feature-toggle-strategies-store-type'; import { parseSearchOperatorValue } from '../feature-search/search-utils'; -import { endOfDay, formatISO } from 'date-fns'; +import { addDays, formatISO } from 'date-fns'; import type { IPrivateProjectChecker } from '../private-project/privateProjectCheckerType'; import type { ProjectAccess } from '../private-project/privateProjectStore'; import type { IAccessReadModel } from '../access/access-read-model-type'; @@ -184,18 +184,20 @@ export default class EventService { } if (params.to) { - const parsed = parseSearchOperatorValue( - 'created_at', - formatISO(endOfDay(new Date(params.to)), { - representation: 'date', - }), - ); - + const parsed = parseSearchOperatorValue('created_at', params.to); if (parsed) { + const values = parsed.values + .filter((v): v is string => v !== null) + .map((date) => + formatISO(addDays(new Date(date), 1), { + representation: 'date', + }), + ); + queryParams.push({ field: parsed.field, operator: 'IS_BEFORE', - values: parsed.values, + values, }); } } diff --git a/src/test/e2e/api/admin/event-search.e2e.test.ts b/src/test/e2e/api/admin/event-search.e2e.test.ts index 5281290acdfb..c1bfc471bb01 100644 --- a/src/test/e2e/api/admin/event-search.e2e.test.ts +++ b/src/test/e2e/api/admin/event-search.e2e.test.ts @@ -339,6 +339,66 @@ test('should include dates created on the `to` date', async () => { }); }); +test('should not include events before `from` or after `to`', async () => { + await eventService.storeEvent({ + type: FEATURE_CREATED, + project: 'default', + data: { featureName: 'early-event' }, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + }); + + await eventService.storeEvent({ + type: FEATURE_CREATED, + project: 'default', + data: { featureName: 'late-event' }, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + }); + + await eventService.storeEvent({ + type: FEATURE_CREATED, + project: 'default', + data: { featureName: 'goldilocks' }, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + }); + + const { events } = await eventService.getEvents(); + const earlyEvent = events.find((e) => e.data.featureName === 'early-event'); + await db.rawDatabase.raw( + `UPDATE events SET created_at = created_at - interval '1 day' where id = ?`, + [earlyEvent?.id], + ); + + const lateEvent = events.find((e) => e.data.featureName === 'late-event'); + await db.rawDatabase.raw( + `UPDATE events SET created_at = created_at + interval '1 day' where id = ?`, + [lateEvent?.id], + ); + + const today = new Date(); + const todayString = today.toISOString().split('T')[0]; + + const { body } = await searchEvents({ + from: `IS:${todayString}`, + to: `IS:${todayString}`, + }); + + expect(body).toMatchObject({ + events: [ + { + type: FEATURE_CREATED, + data: { featureName: 'goldilocks' }, + }, + ], + total: 1, + }); +}); + test('should paginate with offset and limit', async () => { for (let i = 0; i < 5; i++) { await eventService.storeEvent({