From 1a093bbe80d30e557f12e7d939e0ff73e32edf5c Mon Sep 17 00:00:00 2001 From: gregor <gregor.schulz@holi.social> Date: Wed, 12 Mar 2025 12:01:14 +0100 Subject: [PATCH] wip: add event by id fetching logic --- app/fetch_event.ts | 73 ++++++++++++++++++++++++++ app/fetch_event_test.ts | 60 +++++++++++++++++++++ app/fetch_events.ts | 6 +-- app/providers/jasd/appapi.dto.types.ts | 2 +- app/server.ts | 29 ++++++++++ deno.lock | 7 +++ 6 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 app/fetch_event.ts create mode 100644 app/fetch_event_test.ts diff --git a/app/fetch_event.ts b/app/fetch_event.ts new file mode 100644 index 0000000..cd50265 --- /dev/null +++ b/app/fetch_event.ts @@ -0,0 +1,73 @@ +import { HoliEvent } from './fetch_events.ts' +import { logger } from './logging.ts' +import { Activity } from './providers/jasd/appapi.dto.types.ts' +import NotFound = Deno.errors.NotFound + +const APP_API_BASE_URL = 'https://gemeinschaftswerk-nachhaltigkeit.de/app/api/v1' +const APP_FILES_BASE_URL = 'https://gemeinschaftswerk-nachhaltigkeit.de/app/api/v1/files' + +export type HoliEventResponse = { + data: HoliEvent +} + +export type FetchEventInput = { + id: string +} + +export const toHoliEvent = (activity: Activity): HoliEvent | null => { + if (activity.period.permanent) return null + + return { + id: activity.id.toString(), + title: activity.name, + organisationName: activity.organisation.name, + startDate: activity.period.start, + endDate: activity.period.end, + location: { + city: activity.location.address.city, + street: activity.location.address.street, + streetNo: activity.location.address.streetNo, + zipCode: activity.location.address.zipCode, + }, + isRemote: activity.location.online, + // todo gregor: imageproxy-fi the url + imageUrl: APP_FILES_BASE_URL + '/' + activity.image, + eventDetails: { + description: activity.description, + externalLink: activity.location.url, + meetingLink: activity.registerUrl, + }, + } +} + +export const fetchEvent = async (input: FetchEventInput) => { + const url = new URL(`${APP_API_BASE_URL}/activities/${input.id}`) + const startDate = Date.now() + + logger.info(`fetching event from ${url}`) + + const response = await fetch(url) + + if (response.status === 404) { + throw new NotFound('Not found') + } + + try { + const json = (await response.json()).activity as Activity + + const holiEvent = toHoliEvent(json) + + return { + data: holiEvent, + } + // deno-lint-ignore no-explicit-any + } catch (e: any) { + logger.error( + `Error performing request to ${url}: ${e.message}`, + ) + throw e + } finally { + const duration = Date.now() - startDate + logger.debug(`fetching projects took ${duration} ms`) + } +} diff --git a/app/fetch_event_test.ts b/app/fetch_event_test.ts new file mode 100644 index 0000000..03c8d8f --- /dev/null +++ b/app/fetch_event_test.ts @@ -0,0 +1,60 @@ +import { RealTimedEvent } from './providers/jasd/tests/fixtures.ts' +import { fetchEvent } from './fetch_event.ts' +import { returnsNext, stub } from 'https://deno.land/std@0.165.0/testing/mock.ts' +import { describe, it } from 'https://deno.land/std@0.165.0/testing/bdd.ts' +import { assertEquals, assertRejects } from 'https://deno.land/std@0.165.0/testing/asserts.ts' +import NotFound = Deno.errors.NotFound + +describe('fetchEvent', () => { + it('throws when no event can be fetched', () => { + const fakeFetch = stub( + globalThis, + 'fetch', + returnsNext([Promise.resolve(new Response(null, { status: 404, statusText: 'Not Found' }))]), + ) + + assertRejects(() => fetchEvent({ id: 'iDoesNotExist' }), NotFound) + + fakeFetch.restore() + }) + + it('works for a jasd api response', async () => { + const fakeFetch = stub( + globalThis, + 'fetch', + returnsNext([Promise.resolve(new Response(JSON.stringify(RealTimedEvent)))]), + ) + + const expectedEvent = { + data: { + id: '4610', + endDate: '2025-03-26T00:00:00+01:00', + eventDetails: { + description: + '<p>Jeden Dienstag (bis auf eine Sommer- und Winterpause) zeigt der Verein Allerweltskino e.V. im Kölner Filmtheater Off Broadway einen Nachhaltigkeitsfilm. Das Programm ist im Internet abrufbar (siehe Kasten rechts).</p>', + externalLink: 'https://www.allerweltskino.de', + meetingLink: 'https://www.allerweltskino.de', + }, + imageUrl: 'https://gemeinschaftswerk-nachhaltigkeit.de/app/api/v1/files/activities/act5.jpg', + isRemote: false, + location: { + city: 'Köln', + street: 'Bahnhofstr.', + streetNo: '41', + zipCode: '50674', + }, + organisationName: 'Allerweltskino e.V.', + startDate: '2025-03-26T00:00:00+01:00', + title: 'RealTimedEvent', + }, + } + + const result = await fetchEvent({ id: '4160' }) + + assertEquals(result, expectedEvent) + + fakeFetch.restore() + }) + + //return 500 errors for internal problems +}) diff --git a/app/fetch_events.ts b/app/fetch_events.ts index 11aad7e..e5439e8 100644 --- a/app/fetch_events.ts +++ b/app/fetch_events.ts @@ -5,20 +5,20 @@ import { AppApiEvent, AppApiResponse } from './providers/jasd/appapi.dto.types.t const APP_API_BASE_URL = 'https://gemeinschaftswerk-nachhaltigkeit.de/app/api/v2' const APP_FILES_BASE_URL = 'https://gemeinschaftswerk-nachhaltigkeit.de/app/api/v1/files' -type HoliEventLocation = { +export type HoliEventLocation = { street?: string streetNo?: string zipCode?: string city?: string } -type HoliEventDetails = { +export type HoliEventDetails = { description: string externalLink: string meetingLink?: string } -type HoliEvent = { +export type HoliEvent = { id: string title: string organisationName: string diff --git a/app/providers/jasd/appapi.dto.types.ts b/app/providers/jasd/appapi.dto.types.ts index 2ea7a4f..c70601a 100644 --- a/app/providers/jasd/appapi.dto.types.ts +++ b/app/providers/jasd/appapi.dto.types.ts @@ -75,7 +75,7 @@ type ActivityType = 'EVENT' | string type ImpactArea = 'LOCAL' | 'REGIONAL' | 'NATIONAL' | 'INTERNATIONAL' | string -type Activity = { +export type Activity = { id: number name: string description: string diff --git a/app/server.ts b/app/server.ts index 1e390be..ca8cc88 100644 --- a/app/server.ts +++ b/app/server.ts @@ -45,6 +45,10 @@ export type EventsRequest = { location?: string } +export type EventRequest = { + id: string +} + const validateEventsRequestInput = (request: EventsRequest): FetchEventsInput => { console.log(request) return { @@ -54,6 +58,13 @@ const validateEventsRequestInput = (request: EventsRequest): FetchEventsInput => } } +const _validateEventRequestInput = (request: EventRequest): FetchEventsInput => { + console.log(request) + return { + id: request.id, + } +} + const createResolvers = (_config: ServerConfig) => ({ Query: { events: ( @@ -72,6 +83,24 @@ const createResolvers = (_config: ServerConfig) => ({ ) } }, + /* + event: ( + // deno-lint-ignore no-explicit-any + _parent: any, + parameters: EventRequest, + // next line is required for the resolver to work + // deno-lint-ignore no-unused-vars + context: GraphQLContext, + ): Promise<HoliEventsResponse> => { + if (_config.fake) { + return Promise.resolve({ totalResults: 0, data: [] }) + } else { + return fetchEvent( + validateEventRequestInput(parameters), + ) + } + }, + */ }, }) diff --git a/deno.lock b/deno.lock index 6c22ce1..82b4e23 100644 --- a/deno.lock +++ b/deno.lock @@ -527,6 +527,13 @@ "https://deno.land/std@0.156.0/async/pool.ts": "ef9eb97b388543acbf0ac32647121e4dbe629236899586c4d4311a8770fbb239", "https://deno.land/std@0.156.0/async/tee.ts": "d27680d911816fcb3d231e16d690e7588079e66a9b2e5ce8cc354db94fdce95f", "https://deno.land/std@0.156.0/http/server.ts": "c1bce1cbf4060055f622d5c3f0e406fd553e5dca111ca836d28c6268f170ebeb", + "https://deno.land/std@0.165.0/fmt/colors.ts": "9e36a716611dcd2e4865adea9c4bec916b5c60caad4cdcdc630d4974e6bb8bd4", + "https://deno.land/std@0.165.0/testing/_diff.ts": "a23e7fc2b4d8daa3e158fa06856bedf5334ce2a2831e8bf9e509717f455adb2c", + "https://deno.land/std@0.165.0/testing/_format.ts": "cd11136e1797791045e639e9f0f4640d5b4166148796cad37e6ef75f7d7f3832", + "https://deno.land/std@0.165.0/testing/_test_suite.ts": "2d07073d5460a4e3ec50c55ae822cd9bd136926d7363091379947fef9c73c3e4", + "https://deno.land/std@0.165.0/testing/asserts.ts": "1e340c589853e82e0807629ba31a43c84ebdcdeca910c4a9705715dfdb0f5ce8", + "https://deno.land/std@0.165.0/testing/bdd.ts": "35060cefd9cc21b414f4d89453b3551a3d52ec50aeff25db432503c5485b2f72", + "https://deno.land/std@0.165.0/testing/mock.ts": "d7ad40139cda87476c4dc1efeffe406bbfb4a5f82b688e0b85bffe9e911526a2", "https://deno.land/std@0.198.0/async/delay.ts": "a6142eb44cdd856b645086af2b811b1fcce08ec06bb7d50969e6a872ee9b8659", "https://deno.land/std@0.198.0/collections/_comparators.ts": "fa7f9a44cea1d270098a2a5a6f8bb30c61b595c1b1f983bd67c6297d766adffa", "https://deno.land/std@0.198.0/collections/binary_search_node.ts": "8d99dd95901d73a0edbe105826ef7ce0e1111ce184d2d0410dbfda172c9ebf35", -- GitLab