Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

- [#235](https://github.com/os2display/display-api-service/pull/234)
- Fixed Eventdatabasen v2 subscription data order by using occurrences endpoint.
- [#236](https://github.com/os2display/display-api-service/pull/236)
- Fixed bug where no media url made Notified feed crash.
- [#231](https://github.com/os2display/display-api-service/pull/231)
Expand Down
194 changes: 153 additions & 41 deletions src/Feed/EventDatabaseApiV2FeedType.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Symfony\Component\HttpClient\Exception\ClientException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

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

private const string CACHE_OPTIONS_PREFIX = 'options_';
private const string CACHE_EXPIRE_SUFFIX = '_expire';
private const int CACHE_TTL = 60 * 60; // An hour.

public function __construct(
private readonly FeedService $feedService,
private readonly LoggerInterface $logger,
Expand Down Expand Up @@ -68,37 +73,21 @@ public function getData(Feed $feed): array
$locations = $configuration['subscriptionPlaceValue'] ?? null;
$organizers = $configuration['subscriptionOrganizerValue'] ?? null;
$tags = $configuration['subscriptionTagValue'] ?? null;
$numberOfItems = $configuration['subscriptionNumberValue'] ?? 5;
$numberOfItems = isset($configuration['subscriptionNumberValue']) ? (int) $configuration['subscriptionNumberValue'] : 5;

$queryParams = [
'itemsPerPage' => $numberOfItems,
];
$queryParams = [];

if (is_array($locations) && count($locations) > 0) {
$queryParams['location.entityId'] = implode(',', array_map(static fn ($location) => (int) $location['value'], $locations));
$queryParams['event.location.entityId'] = implode(',', array_map(static fn ($location) => (int) $location['value'], $locations));
}
if (is_array($organizers) && count($organizers) > 0) {
$queryParams['organizer.entityId'] = implode(',', array_map(static fn ($organizer) => (int) $organizer['value'], $organizers));
$queryParams['event.organizer.entityId'] = implode(',', array_map(static fn ($organizer) => (int) $organizer['value'], $organizers));
}
if (is_array($tags) && count($tags) > 0) {
$queryParams['tags'] = implode(',', array_map(static fn ($tag) => (string) $tag['value'], $tags));
$queryParams['event.tags'] = implode(',', array_map(static fn ($tag) => (string) $tag['value'], $tags));
}

$queryParams['occurrences.start'] = date('c');
// TODO: Should be based on (end >= now) instead. But not supported by the API.
// $queryParams['occurrences.end'] = date('c');
// @see https://github.com/itk-dev/event-database-api/blob/develop/src/Api/Dto/Event.php

$members = $this->helper->request($feedSource, 'events', $queryParams);

$result = [];

foreach ($members as $member) {
$poster = $this->helper->mapFirstOccurrenceToOutput((object) $member);
if (null !== $poster) {
$result[] = $poster;
}
}
$result = $this->getSubscriptionData($feedSource, $queryParams, $numberOfItems);

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

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

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

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

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

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

$result = [];

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

$query = [
'itemsPerPage' => 50,
'name' => $request->query->get('search') ?? '',
];

if (null === $entityType) {
throw new \Exception('entityType must not be null');
}

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

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

foreach ($members as $member) {
$result[] = $this->helper->toPosterOption($member, $entityType);
if ($expireCacheItem->isHit()) {
$result = $expireCacheItem->get();

if ($result > time()) {
if ($cacheItem->isHit()) {
return $cacheItem->get();
}
}
}

return $result;
try {
$page = 1;
$results = [];
$itemsPerPage = 50;

do {
$query = [
'itemsPerPage' => $itemsPerPage,
'page' => $page,
];

$responseData = $this->helper->request($feedSource, $entityType, $query);
$members = $responseData->{'hydra:member'};

foreach ($members as $member) {
$results[] = $this->helper->toPosterOption($member, $entityType);
}

if ($responseData->{'hydra:totalItems'} > $page * $itemsPerPage) {
$fetchMore = true;
$page = $page + 1;
} else {
$fetchMore = false;
}
} while ($fetchMore);

$cacheItem->set($results);
$this->feedWithoutExpireCache->save($cacheItem);

$expireCacheItem->set(time() + $this::CACHE_TTL);
$this->feedWithoutExpireCache->save($expireCacheItem);

return $results;
} catch (\Exception) {
if ($cacheItem->isHit()) {
return $cacheItem->get();
} else {
return [];
}
}
} elseif ('subscription' === $name) {
$query = $request->query->all();

$queryParams = [];

if (isset($query['tag'])) {
$tag = $query['tag'];
$queryParams['event.tags'] = implode(',', $tag);
}

if (isset($query['organization'])) {
$organizer = $query['organization'];
$queryParams['event.organizer.entityId'] = implode(',', $organizer);
}

if (isset($query['location'])) {
$location = $query['location'];
$queryParams['event.location.entityId'] = implode(',', $location);
}

$numberOfItems = isset($query['numberOfItems']) ? (int) $query['numberOfItems'] : 10;

return $this->getSubscriptionData($feedSource, $queryParams, $numberOfItems);
} elseif ('search' === $name) {
$query = $request->query->all();
$queryParams = [];
Expand All @@ -267,23 +326,23 @@ public function getConfigOptions(Request $request, FeedSource $feedSource, strin

if (isset($query['organization'])) {
$organizer = $query['organization'];
$queryParams['organizer.entityId'] = (int) $organizer;
$queryParams['organizer.entityId'] = $organizer;
}

if (isset($query['location'])) {
$location = $query['location'];
$queryParams['location.entityId'] = (int) $location;
$queryParams['location.entityId'] = $location;
}

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

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

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

$result = [];

Expand Down Expand Up @@ -351,4 +410,57 @@ public function getSchema(): array
'required' => ['host', 'apikey'],
];
}

private function getSubscriptionData(FeedSource $feedSource, array $queryParams = [], int $numberOfItems = 10): array
{
$itemsPerPage = 20;
$page = 1;

$result = [];
$addedEventIds = [];

$queryParams['itemsPerPage'] = $itemsPerPage;

$queryParams['end'] = [
'gt' => date('c'),
];

do {
$queryParams['page'] = $page;

$responseData = $this->helper->request($feedSource, 'occurrences', $queryParams);
$members = $responseData->{'hydra:member'};

foreach ($members as $member) {
// If occurrence.event has not been added already, add it to the result array.
$occurrence = $this->helper->mapOccurrenceToOutput((object) $member);

if (null == $occurrence) {
continue;
}

if (!in_array($occurrence->eventId, $addedEventIds)) {
$addedEventIds[] = $occurrence->eventId;
$result[] = $occurrence;
}

if (count($result) >= $numberOfItems) {
break;
}
}

if (count($result) < $numberOfItems) {
if ($responseData->{'hydra:totalItems'} > $page * $itemsPerPage) {
$fetchMore = true;
$page = $page + 1;
} else {
$fetchMore = false;
}
} else {
$fetchMore = false;
}
} while ($fetchMore);

return $result;
}
}
8 changes: 4 additions & 4 deletions src/Feed/EventDatabaseApiV2Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ public function __construct(
private readonly HttpClientInterface $client,
) {}

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

if (!isset($secrets['host']) || !isset($secrets['apikey'])) {
return [];
throw new \Exception('Missing host or apikey');
}

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

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

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

public function toEntityResult(string $entityType, object $entity): ?object
Expand Down Expand Up @@ -106,6 +105,7 @@ public function createPoster(?object $event = null, ?object $occurrence = null):
$event->url ?? null,
$baseUrl,
$imageUrls->large ?? null,
$imageUrls->small ?? null,
$occurrence->start ?? null,
$occurrence->end ?? null,
$occurrence->ticketPriceRange ?? null,
Expand Down
1 change: 1 addition & 0 deletions src/Feed/OutputModel/Poster/Poster.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public function __construct(
public ?string $url,
public ?string $baseUrl,
public ?string $image,
public ?string $imageThumbnail,
public ?string $startDate,
public ?string $endDate,
public ?string $ticketPriceRange,
Expand Down