Skip to content
Open
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

- [#313](https://github.com/os2display/display-api-service/pull/313)
- Add BRND booking feed type

## [2.5.2] - 2025-09-25

- [#260](https://github.com/os2display/display-api-service/pull/260)
Expand Down
183 changes: 183 additions & 0 deletions src/Feed/BrndFeedType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<?php

declare(strict_types=1);

namespace App\Feed;

use App\Entity\Tenant\Feed;
use App\Entity\Tenant\FeedSource;
use App\Feed\SourceType\Brnd\ApiClient;
use App\Feed\SourceType\Brnd\SecretsDTO;
use App\Service\FeedService;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\HttpFoundation\Request;

/**
* Brnd Bookingsystem Feed.
*
* @see https://brndapi.brnd.com/swagger/index.html
*/
class BrndFeedType implements FeedTypeInterface
{
public const int CACHE_TTL = 3600;

final public const string SUPPORTED_FEED_TYPE = FeedOutputModels::BRND_BOOKING_OUTPUT;

public function __construct(
private readonly FeedService $feedService,
private readonly ApiClient $apiClient,
private readonly CacheItemPoolInterface $feedsCache,
) {}

public function getAdminFormOptions(FeedSource $feedSource): array
{
$feedEntryRecipients = $this->feedService->getFeedSourceConfigUrl($feedSource, 'sport-center');

return [
[
'key' => 'brnd-sport-center-id',
'input' => 'input',
'type' => 'text',
'name' => 'sport_center_id',
'label' => 'Sportcenter ID',
'formGroupClasses' => 'mb-3',
],
];
}

public function getData(Feed $feed): array
{
$result = [
'title' => 'BRND Booking',
'bookings' => [],
];

$configuration = $feed->getConfiguration();
$feedSource = $feed->getFeedSource();

if (null == $feedSource) {
return $result;
}

$secrets = new SecretsDTO($feedSource);

$baseUri = $secrets->apiBaseUri;
$sportCenterId = $configuration['sport_center_id'] ?? null;

if ('' === $baseUri || null === $sportCenterId || '' === $sportCenterId) {
return $result;
}

$feedSource = $feed->getFeedSource();

if (null === $feedSource) {
return $result;
}

$bookings = $this->apiClient->getInfomonitorBookingsDetails($feedSource, $sportCenterId);

$result['bookings'] = array_map([$this, 'parseBrndBooking'], $bookings);

return $result;
}

private function parseBrndBooking(array $booking): array
{
// Parse start time
$startDateTime = null;
if (!empty($booking['dato']) && isset($booking['starttid']) && is_string($booking['starttid'])) {
// Trim starttid to 6 digits after dot for microseconds
$starttid = preg_replace('/\.(\d{6})\d+$/', '.$1', $booking['starttid']);
$dateOnly = substr($booking['dato'], 0, 10);
$dateTimeString = $dateOnly.' '.$starttid;
$startDateTime = \DateTimeImmutable::createFromFormat('m/d/Y H:i:s.u', $dateTimeString);
}

// Parse end time
$endDateTime = null;
if (!empty($booking['dato']) && isset($booking['sluttid']) && is_string($booking['sluttid'])) {
$sluttid = preg_replace('/\.(\d{6})\d+$/', '.$1', $booking['sluttid']);
$dateOnly = substr($booking['dato'], 0, 10);
$dateTimeString = $dateOnly.' '.$sluttid;
$endDateTime = \DateTimeImmutable::createFromFormat('m/d/Y H:i:s.u', $dateTimeString);
}

return [
'bookingcode' => $booking['ansøgning'] ?? '',
'remarks' => $booking['bemærkninger'] ?? '',
'startTime' => $startDateTime ? $startDateTime->getTimestamp() : null,
'endTime' => $endDateTime ? $endDateTime->getTimestamp() : null,
'complex' => $booking['anlæg'] ?? '',
'area' => $booking['område'] ?? '',
'facility' => $booking['facilitet'] ?? '',
'activity' => $booking['aktivitet'] ?? '',
'team' => $booking['hold'] ?? '',
'status' => $booking['status'] ?? '',
'checkIn' => $booking['checK_IN'] ?? '',
'bookingBy' => $booking['ansøgt_af'] ?? '',
'changingRooms' => $booking['omklædningsrum'] ?? '',
];
}

public function getConfigOptions(Request $request, FeedSource $feedSource, string $name): ?array
{
return null;
}

public function getRequiredSecrets(): array
{
return [
'api_base_uri' => [
'type' => 'string',
'exposeValue' => true,
],
'company_id' => [
'type' => 'string',
'exposeValue' => true,
],
'api_auth_key' => [
'type' => 'string',
'exposeValue' => true,
],
];
}

public function getRequiredConfiguration(): array
{
return ['sport_center_id'];
}

public function getSupportedFeedOutputType(): string
{
return self::SUPPORTED_FEED_TYPE;
}

public function getSchema(): array
{
return [
'$schema' => 'http://json-schema.org/draft-04/schema#',
'type' => 'object',
'properties' => [
'api_base_uri' => [
'type' => 'string',
'format' => 'uri',
],
'company_id' => [
'type' => 'string',
],
'api_auth_key' => [
'type' => 'string',
],
],
'required' => ['api_base_uri', 'company_id', 'api_auth_key'],
];
}

public static function getIdKey(FeedSource $feedSource): string
{
$ulid = $feedSource->getId();
assert(null !== $ulid);

return $ulid->toBase32();
}
}
39 changes: 39 additions & 0 deletions src/Feed/FeedOutputModels.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,43 @@ class FeedOutputModels
* ]
*/
final public const string RSS_OUTPUT = 'rss';

/**
* Data example:
* [
* {
* activity: "Svømning",
* area: "Svømmehal",
* bookingBy: "Offentlig svømning",
* bookingcode: "BKN-363973",
* changingRooms: "",
* checkIn: "0",
* complex: "Humlehøj Hallen",
* endTime: 1751615100,
* facility: "Svømmehal",
* remarks: "",
* startTime: 1751608800,
* status: "Tildelt tid",
* team: ""
* },
* {
* activity: "Undervisning",
* area: "Mødelokaler",
* bookingBy: "Svømmeklubben Sønderborg",
* bookingcode: "BKN-388946",
* changingRooms: "",
* checkIn: "0",
* complex: "Humlehøj Hallen",
* endTime: 1751641200,
* facility: "Mødelokale 1+2",
* remarks: "",
* startTime: 1751630400,
* status: "Tildelt tid",
* team: ""
* }
* ]
*
* Start/end time are unix timestamps.
*/
final public const string BRND_BOOKING_OUTPUT = 'brnd-booking';
}
Loading