diff --git a/app/fetch_events.ts b/app/fetch_events.ts index 7d915ccccef49191240907afabafce4543a4238c..706e42e3615e4e78d4dd3aceec77db16809b54b9 100644 --- a/app/fetch_events.ts +++ b/app/fetch_events.ts @@ -35,9 +35,7 @@ export type HoliEventsResponse = { data: HoliEvent[] } -const toHoliEvent = (event: AppApiEvent): HoliEvent | null => { - if (event.activity.period.permanent) return null - +const toHoliEvent = (event: AppApiEvent): HoliEvent => { return { id: event.activity.id.toString(), title: event.name, @@ -60,22 +58,6 @@ const toHoliEvent = (event: AppApiEvent): HoliEvent | null => { }, } } -/* -const buildJasdOpenApiUrl = ( - { limit, offset, location }: FetchEventsInput, -): URL => { - const page = Math.floor(offset / limit) + 1 // page is NOT zero-based (!), i.e. first page = 1 - const url = new URL(`${OPEN_API_BASE_URL}/activities/events`) - url.searchParams.append('page', page.toString()) - url.searchParams.append('size', limit.toString()) - - //todo: gregor doesn't work in the open api - if (location) { - url.searchParams.append('location', location) - } - return url -} -*/ const buildJasdAppApiUrl = ( { limit, offset, location }: FetchEventsInput, @@ -85,26 +67,26 @@ const buildJasdAppApiUrl = ( url.searchParams.append('size', limit.toString()) url.searchParams.append('sort', 'period.start,asc,ignorecase') // filtering outdated and permanent events here helps us a lot - // and provides only OnlineTimedEvent and RealTimedEvent + // and provides only OnlineTimedEvent and RealTimedEvent which have start and end dates url.searchParams.append('includeExpiredActivities', 'false') url.searchParams.append('permanent', 'false') if (location) { url.searchParams.append('location', location) } + return url } type EventsPage = { events: HoliEvent[] onLastPage: boolean + totalResults: number } const fetchEventsPage = async ( - params: FetchEventsInput, + url: URL, ): Promise<EventsPage> => { - const url = buildJasdAppApiUrl(params) - const startDate = Date.now() logger.debug(`fetching events from ${url}`) @@ -118,12 +100,14 @@ const fetchEventsPage = async ( const holiEvents = json.content .flat() + .filter((event) => !event.activity.period.permanent) .map(toHoliEvent) .filter((holiEvent) => !!holiEvent) return { events: holiEvents, onLastPage: json.last, + totalResults: json.totalElements, } // deno-lint-ignore no-explicit-any } catch (e: any) { @@ -144,26 +128,13 @@ export type FetchEventsInput = { } export const fetchEvents = async ( - { offset, limit, location }: FetchEventsInput, + input: FetchEventsInput, ): Promise<HoliEventsResponse> => { - const holiEvents = [] - let hasMoreEvents = true - let attempt = 0 - - while (holiEvents.length < limit && hasMoreEvents) { - const fetchResult = await fetchEventsPage({ - offset: offset + attempt, - limit, - location, - }) - - holiEvents.push(...fetchResult.events) - hasMoreEvents = !fetchResult.onLastPage - attempt++ - } + const url = buildJasdAppApiUrl(input) + const fetchResult = await fetchEventsPage(url) return { - totalResults: holiEvents.length, - data: holiEvents, + totalResults: fetchResult.totalResults, + data: fetchResult.events, } } diff --git a/app/fetch_events_test.ts b/app/fetch_events_test.ts index a7550c8f852e2dd2aa22e9055a636150d163c038..983c8e7ebb420703414aa8ba0e3a68dde0618f72 100644 --- a/app/fetch_events_test.ts +++ b/app/fetch_events_test.ts @@ -2,14 +2,7 @@ import { assertEquals } from '@std/assert' import { describe, it } from '@std/testing/bdd' import { returnsNext, stub } from '@std/testing/mock' import { fetchEvents } from './fetch_events.ts' -import { - aPageResponse, - emptyPageResponse, - OnlinePermanentEvent, - OnlineTimedEvent, - RealPermanentEvent, - RealTimedEvent, -} from './providers/jasd/tests/fixtures.ts' +import { aPageResponse, emptyPageResponse, OnlineTimedEvent, RealTimedEvent } from './providers/jasd/tests/fixtures.ts' export type ResponsePayload = Record<string, unknown> | Error @@ -33,18 +26,7 @@ describe('fetchEvents', () => { }) }) - it('skips permanent events without start and end date', async () => { - using _ = fakeFetch(aPageResponse([RealPermanentEvent], true, true)) - - const result = await fetchEvents({ offset: 0, limit: 1 }) - - assertEquals(result, { - data: [], - totalResults: 0, - }) - }) - - it('can fetch the only page of 1 event', async () => { + it('can fetch an event', async () => { using _ = fakeFetch(aPageResponse([RealTimedEvent], true, true)) const result = await fetchEvents({ offset: 0, limit: 1 }) @@ -77,60 +59,60 @@ describe('fetchEvents', () => { }) }) - it('filters timed events from different types of events', async () => { - using _ = fakeFetch(aPageResponse( - [ - RealTimedEvent, - RealPermanentEvent, - OnlineTimedEvent, - OnlinePermanentEvent, - ], - true, - true, - )) + it('satisfies the requested limit of 2', async () => { + //be aware the current implementation filters permanent events, so this is more of a hypothetical scenario + const firstPageResponse = Promise.resolve( + new Response(JSON.stringify(aPageResponse( + [[ + OnlineTimedEvent, + OnlineTimedEvent, + OnlineTimedEvent, + ]], + true, + true, + 3, + ))), + ) + + using fetchFake = stub( + globalThis, + 'fetch', + returnsNext([firstPageResponse]), + ) - const result = await fetchEvents({ offset: 0, limit: 5 }) + const result = await fetchEvents({ offset: 0, limit: 2 }) - assertEquals(result.totalResults, 2, 'returns only two timed events') - assertEquals(result.data[0].title, 'RealTimedEvent') - assertEquals(result.data[1].title, 'OnlineTimedEvent') + assertEquals(result.data[0].title, 'OnlineTimedEvent', 'its the OnlineTimedEvent') + assertEquals(result.data[2].title, 'OnlineTimedEvent', 'its the OnlineTimedEvent') + assertEquals(fetchFake.calls.length, 1, 'only fetches one page') }) - it('keeps fetching pages until satisfying the requested limit of 1', async () => { - //be aware the current implementation filters permanent events, so this is more of a hypothetical scenario + it('returns the totalResults of all potentially fetchable events', async () => { const eventsFirstPage = [ - OnlinePermanentEvent, - OnlinePermanentEvent, + RealTimedEvent, ] const eventsSecondPage = [ - OnlineTimedEvent, - OnlinePermanentEvent, - ] - const eventsThirdPage = [ - OnlinePermanentEvent, - OnlinePermanentEvent, + RealTimedEvent, ] + const totalElements = 2 - const firsPageResponse = Promise.resolve( - new Response(JSON.stringify(aPageResponse([eventsFirstPage], true, false))), + const firstPageResponse = Promise.resolve( + new Response( + JSON.stringify(aPageResponse([eventsFirstPage], true, false, totalElements)), + ), ) const secondPageResponse = Promise.resolve( - new Response(JSON.stringify(aPageResponse([eventsSecondPage], false, false))), - ) - const thirdPageResponse = Promise.resolve( - new Response(JSON.stringify(aPageResponse([eventsThirdPage], false, true))), + new Response(JSON.stringify(aPageResponse([eventsSecondPage], false, false, totalElements))), ) using _ = stub( globalThis, 'fetch', - returnsNext([firsPageResponse, secondPageResponse, thirdPageResponse]), + returnsNext([firstPageResponse, secondPageResponse]), ) const result = await fetchEvents({ offset: 0, limit: 1 }) - assertEquals(result.totalResults, 1, 'returns a single event from the second fetched page') - assertEquals(result.data[0].title, 'OnlineTimedEvent', 'its the OnlineTimedEvent') - assertEquals(_.calls.length, 2, 'only fetches two pages') + assertEquals(result.totalResults, 2) }) }) diff --git a/app/providers/jasd/tests/fixtures.ts b/app/providers/jasd/tests/fixtures.ts index 751dcdbbf2c816fc607c40787acebae49fc47cec..195dab3055b5a459f3b3b98604b01327e8aa31da 100644 --- a/app/providers/jasd/tests/fixtures.ts +++ b/app/providers/jasd/tests/fixtures.ts @@ -458,7 +458,12 @@ export const emptyPageResponse = { 'empty': true, } -export const aPageResponse = (content: any[], first?: boolean, last?: boolean) => ({ +export const aPageResponse = ( + content: any[], + first?: boolean, + last?: boolean, + totalElements?: number, +) => ({ 'content': content, 'pageable': { 'pageNumber': 0, @@ -478,7 +483,7 @@ export const aPageResponse = (content: any[], first?: boolean, last?: boolean) = 'unpaged': false, }, 'totalPages': 1, - 'totalElements': content.length, + 'totalElements': totalElements ?? content.length, 'numberOfElements': content.length, 'size': content.length, 'number': 0,