Skip to content

Commit fa12e92

Browse files
authored
Merge pull request #235 from os2display/feature/2927-fixes
Eventdatabase v2 feed source fixes
2 parents 09cabdb + 1f12deb commit fa12e92

File tree

4 files changed

+160
-45
lines changed

4 files changed

+160
-45
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
- [#235](https://github.com/os2display/display-api-service/pull/234)
8+
- Fixed Eventdatabasen v2 subscription data order by using occurrences endpoint.
79
- [#236](https://github.com/os2display/display-api-service/pull/236)
810
- Fixed bug where no media url made Notified feed crash.
911
- [#231](https://github.com/os2display/display-api-service/pull/231)

src/Feed/EventDatabaseApiV2FeedType.php

+153-41
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\HttpClient\Exception\ClientException;
1515
use Symfony\Component\HttpFoundation\Request;
1616
use Symfony\Component\HttpFoundation\Response;
17+
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
1718

1819
/**
1920
* @see https://github.com/itk-dev/event-database-api
@@ -23,6 +24,10 @@ class EventDatabaseApiV2FeedType implements FeedTypeInterface
2324
{
2425
final public const string SUPPORTED_FEED_TYPE = SupportedFeedOutputs::POSTER_OUTPUT;
2526

27+
private const string CACHE_OPTIONS_PREFIX = 'options_';
28+
private const string CACHE_EXPIRE_SUFFIX = '_expire';
29+
private const int CACHE_TTL = 60 * 60; // An hour.
30+
2631
public function __construct(
2732
private readonly FeedService $feedService,
2833
private readonly LoggerInterface $logger,
@@ -68,37 +73,21 @@ public function getData(Feed $feed): array
6873
$locations = $configuration['subscriptionPlaceValue'] ?? null;
6974
$organizers = $configuration['subscriptionOrganizerValue'] ?? null;
7075
$tags = $configuration['subscriptionTagValue'] ?? null;
71-
$numberOfItems = $configuration['subscriptionNumberValue'] ?? 5;
76+
$numberOfItems = isset($configuration['subscriptionNumberValue']) ? (int) $configuration['subscriptionNumberValue'] : 5;
7277

73-
$queryParams = [
74-
'itemsPerPage' => $numberOfItems,
75-
];
78+
$queryParams = [];
7679

7780
if (is_array($locations) && count($locations) > 0) {
78-
$queryParams['location.entityId'] = implode(',', array_map(static fn ($location) => (int) $location['value'], $locations));
81+
$queryParams['event.location.entityId'] = implode(',', array_map(static fn ($location) => (int) $location['value'], $locations));
7982
}
8083
if (is_array($organizers) && count($organizers) > 0) {
81-
$queryParams['organizer.entityId'] = implode(',', array_map(static fn ($organizer) => (int) $organizer['value'], $organizers));
84+
$queryParams['event.organizer.entityId'] = implode(',', array_map(static fn ($organizer) => (int) $organizer['value'], $organizers));
8285
}
8386
if (is_array($tags) && count($tags) > 0) {
84-
$queryParams['tags'] = implode(',', array_map(static fn ($tag) => (string) $tag['value'], $tags));
87+
$queryParams['event.tags'] = implode(',', array_map(static fn ($tag) => (string) $tag['value'], $tags));
8588
}
8689

87-
$queryParams['occurrences.start'] = date('c');
88-
// TODO: Should be based on (end >= now) instead. But not supported by the API.
89-
// $queryParams['occurrences.end'] = date('c');
90-
// @see https://github.com/itk-dev/event-database-api/blob/develop/src/Api/Dto/Event.php
91-
92-
$members = $this->helper->request($feedSource, 'events', $queryParams);
93-
94-
$result = [];
95-
96-
foreach ($members as $member) {
97-
$poster = $this->helper->mapFirstOccurrenceToOutput((object) $member);
98-
if (null !== $poster) {
99-
$result[] = $poster;
100-
}
101-
}
90+
$result = $this->getSubscriptionData($feedSource, $queryParams, $numberOfItems);
10291

10392
$posterOutput = (new PosterOutput($result))->toArray();
10493

@@ -112,7 +101,8 @@ public function getData(Feed $feed): array
112101
if (isset($configuration['singleSelectedOccurrence'])) {
113102
$occurrenceId = $configuration['singleSelectedOccurrence'];
114103

115-
$members = $this->helper->request($feedSource, 'occurrences', null, $occurrenceId);
104+
$responseData = $this->helper->request($feedSource, 'occurrences', null, $occurrenceId);
105+
$members = $responseData->{'hydra:member'};
116106

117107
if (empty($members)) {
118108
return [];
@@ -188,6 +178,7 @@ public function getAdminFormOptions(FeedSource $feedSource): array
188178
$searchEndpoint = $this->feedService->getFeedSourceConfigUrl($feedSource, 'search');
189179
$entityEndpoint = $this->feedService->getFeedSourceConfigUrl($feedSource, 'entity');
190180
$optionsEndpoint = $this->feedService->getFeedSourceConfigUrl($feedSource, 'options');
181+
$subscriptionEndpoint = $this->feedService->getFeedSourceConfigUrl($feedSource, 'subscription');
191182

192183
return [
193184
[
@@ -196,6 +187,7 @@ public function getAdminFormOptions(FeedSource $feedSource): array
196187
'endpointSearch' => $searchEndpoint,
197188
'endpointEntity' => $entityEndpoint,
198189
'endpointOption' => $optionsEndpoint,
190+
'endpointSubscription' => $subscriptionEndpoint,
199191
'name' => 'resources',
200192
'label' => 'Vælg resurser',
201193
'helpText' => 'Her vælger du hvilke resurser der skal hentes indgange fra.',
@@ -218,7 +210,8 @@ public function getConfigOptions(Request $request, FeedSource $feedSource, strin
218210
throw new \Exception('entityType and entityId must not be null');
219211
}
220212

221-
$members = $this->helper->request($feedSource, $entityType, null, (int) $entityId);
213+
$responseData = $this->helper->request($feedSource, $entityType, null, (int) $entityId);
214+
$members = $responseData->{'hydra:member'};
222215

223216
$result = [];
224217

@@ -231,24 +224,90 @@ public function getConfigOptions(Request $request, FeedSource $feedSource, strin
231224
} elseif ('options' === $name) {
232225
$entityType = $request->query->get('entityType');
233226

234-
$query = [
235-
'itemsPerPage' => 50,
236-
'name' => $request->query->get('search') ?? '',
237-
];
238-
239227
if (null === $entityType) {
240228
throw new \Exception('entityType must not be null');
241229
}
242230

243-
$members = $this->helper->request($feedSource, $entityType, $query);
231+
if (!in_array($entityType, ['tags', 'organizations', 'locations'])) {
232+
throw new BadRequestHttpException('Unsupported entityType: '.$entityType);
233+
}
244234

245-
$result = [];
235+
$expireCacheItem = $this->feedWithoutExpireCache->getItem($this::CACHE_OPTIONS_PREFIX.$entityType.$this::CACHE_EXPIRE_SUFFIX);
236+
$cacheItem = $this->feedWithoutExpireCache->getItem($this::CACHE_OPTIONS_PREFIX.$entityType);
246237

247-
foreach ($members as $member) {
248-
$result[] = $this->helper->toPosterOption($member, $entityType);
238+
if ($expireCacheItem->isHit()) {
239+
$result = $expireCacheItem->get();
240+
241+
if ($result > time()) {
242+
if ($cacheItem->isHit()) {
243+
return $cacheItem->get();
244+
}
245+
}
249246
}
250247

251-
return $result;
248+
try {
249+
$page = 1;
250+
$results = [];
251+
$itemsPerPage = 50;
252+
253+
do {
254+
$query = [
255+
'itemsPerPage' => $itemsPerPage,
256+
'page' => $page,
257+
];
258+
259+
$responseData = $this->helper->request($feedSource, $entityType, $query);
260+
$members = $responseData->{'hydra:member'};
261+
262+
foreach ($members as $member) {
263+
$results[] = $this->helper->toPosterOption($member, $entityType);
264+
}
265+
266+
if ($responseData->{'hydra:totalItems'} > $page * $itemsPerPage) {
267+
$fetchMore = true;
268+
$page = $page + 1;
269+
} else {
270+
$fetchMore = false;
271+
}
272+
} while ($fetchMore);
273+
274+
$cacheItem->set($results);
275+
$this->feedWithoutExpireCache->save($cacheItem);
276+
277+
$expireCacheItem->set(time() + $this::CACHE_TTL);
278+
$this->feedWithoutExpireCache->save($expireCacheItem);
279+
280+
return $results;
281+
} catch (\Exception) {
282+
if ($cacheItem->isHit()) {
283+
return $cacheItem->get();
284+
} else {
285+
return [];
286+
}
287+
}
288+
} elseif ('subscription' === $name) {
289+
$query = $request->query->all();
290+
291+
$queryParams = [];
292+
293+
if (isset($query['tag'])) {
294+
$tag = $query['tag'];
295+
$queryParams['event.tags'] = implode(',', $tag);
296+
}
297+
298+
if (isset($query['organization'])) {
299+
$organizer = $query['organization'];
300+
$queryParams['event.organizer.entityId'] = implode(',', $organizer);
301+
}
302+
303+
if (isset($query['location'])) {
304+
$location = $query['location'];
305+
$queryParams['event.location.entityId'] = implode(',', $location);
306+
}
307+
308+
$numberOfItems = isset($query['numberOfItems']) ? (int) $query['numberOfItems'] : 10;
309+
310+
return $this->getSubscriptionData($feedSource, $queryParams, $numberOfItems);
252311
} elseif ('search' === $name) {
253312
$query = $request->query->all();
254313
$queryParams = [];
@@ -267,23 +326,23 @@ public function getConfigOptions(Request $request, FeedSource $feedSource, strin
267326

268327
if (isset($query['organization'])) {
269328
$organizer = $query['organization'];
270-
$queryParams['organizer.entityId'] = (int) $organizer;
329+
$queryParams['organizer.entityId'] = $organizer;
271330
}
272331

273332
if (isset($query['location'])) {
274333
$location = $query['location'];
275-
$queryParams['location.entityId'] = (int) $location;
334+
$queryParams['location.entityId'] = $location;
276335
}
277336

278-
$queryParams['occurrences.start'] = date('c');
279-
// TODO: Should be based on (end >= now) instead. But not supported by the API.
280-
// $queryParams['occurrences.end'] = date('c');
281-
// @see https://github.com/itk-dev/event-database-api/blob/develop/src/Api/Dto/Event.php
337+
$queryParams['occurrences.end'] = [
338+
'gt' => date('c'),
339+
];
282340
}
283341

284342
$queryParams['itemsPerPage'] = $query['itemsPerPage'] ?? 10;
285343

286-
$members = $this->helper->request($feedSource, $type, $queryParams);
344+
$responseData = $this->helper->request($feedSource, $type, $queryParams);
345+
$members = $responseData->{'hydra:member'};
287346

288347
$result = [];
289348

@@ -351,4 +410,57 @@ public function getSchema(): array
351410
'required' => ['host', 'apikey'],
352411
];
353412
}
413+
414+
private function getSubscriptionData(FeedSource $feedSource, array $queryParams = [], int $numberOfItems = 10): array
415+
{
416+
$itemsPerPage = 20;
417+
$page = 1;
418+
419+
$result = [];
420+
$addedEventIds = [];
421+
422+
$queryParams['itemsPerPage'] = $itemsPerPage;
423+
424+
$queryParams['end'] = [
425+
'gt' => date('c'),
426+
];
427+
428+
do {
429+
$queryParams['page'] = $page;
430+
431+
$responseData = $this->helper->request($feedSource, 'occurrences', $queryParams);
432+
$members = $responseData->{'hydra:member'};
433+
434+
foreach ($members as $member) {
435+
// If occurrence.event has not been added already, add it to the result array.
436+
$occurrence = $this->helper->mapOccurrenceToOutput((object) $member);
437+
438+
if (null == $occurrence) {
439+
continue;
440+
}
441+
442+
if (!in_array($occurrence->eventId, $addedEventIds)) {
443+
$addedEventIds[] = $occurrence->eventId;
444+
$result[] = $occurrence;
445+
}
446+
447+
if (count($result) >= $numberOfItems) {
448+
break;
449+
}
450+
}
451+
452+
if (count($result) < $numberOfItems) {
453+
if ($responseData->{'hydra:totalItems'} > $page * $itemsPerPage) {
454+
$fetchMore = true;
455+
$page = $page + 1;
456+
} else {
457+
$fetchMore = false;
458+
}
459+
} else {
460+
$fetchMore = false;
461+
}
462+
} while ($fetchMore);
463+
464+
return $result;
465+
}
354466
}

src/Feed/EventDatabaseApiV2Helper.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ public function __construct(
2222
private readonly HttpClientInterface $client,
2323
) {}
2424

25-
public function request(FeedSource $feedSource, string $entityType, ?array $queryParams = null, ?int $entityId = null): array
25+
public function request(FeedSource $feedSource, string $entityType, ?array $queryParams = null, ?int $entityId = null): object
2626
{
2727
$secrets = $feedSource->getSecrets();
2828

2929
if (!isset($secrets['host']) || !isset($secrets['apikey'])) {
30-
return [];
30+
throw new \Exception('Missing host or apikey');
3131
}
3232

3333
$host = $secrets['host'];
@@ -57,9 +57,8 @@ public function request(FeedSource $feedSource, string $entityType, ?array $quer
5757
);
5858

5959
$content = $response->getContent();
60-
$decoded = json_decode($content, null, 512, JSON_THROW_ON_ERROR);
6160

62-
return $decoded->{'hydra:member'};
61+
return json_decode($content, null, 512, JSON_THROW_ON_ERROR);
6362
}
6463

6564
public function toEntityResult(string $entityType, object $entity): ?object
@@ -106,6 +105,7 @@ public function createPoster(?object $event = null, ?object $occurrence = null):
106105
$event->url ?? null,
107106
$baseUrl,
108107
$imageUrls->large ?? null,
108+
$imageUrls->small ?? null,
109109
$occurrence->start ?? null,
110110
$occurrence->end ?? null,
111111
$occurrence->ticketPriceRange ?? null,

src/Feed/OutputModel/Poster/Poster.php

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public function __construct(
1616
public ?string $url,
1717
public ?string $baseUrl,
1818
public ?string $image,
19+
public ?string $imageThumbnail,
1920
public ?string $startDate,
2021
public ?string $endDate,
2122
public ?string $ticketPriceRange,

0 commit comments

Comments
 (0)