wip: api client for bpn-connect
This commit is contained in:
1 parent
34c2209316
commit
0b4172bb6f
12 files changed
+262
-5
No files matched your search
@@ -48,6 +48,9 @@ MAILJET_DEFAULT_LIST_ID=
|
||||
MAILJET_WEBHOOK_BASIC_PASSWORD=secret
|
||||
APP_NEWSLETTER_CONFIRMATION_TTL_HOURS=1
|
||||
|
||||
BPN_CONNECT_BASE_URL=https://bpn-connect.ep-reisen.app
|
||||
BPN_CONNECT_API_KEY=
|
||||
|
||||
APP_BPN_USER=
|
||||
APP_BPN_PASSWORD=
|
||||
APP_BPN_IP=
|
||||
@@ -71,9 +74,6 @@ APP_TRAVEL_PREFER_REMOTE=false
|
||||
APP_TRAVEL_ENABLE_FALLBACK=true
|
||||
APP_TRAVEL_SNAPSHOT_RETENTION_BUFFER_DAYS=14
|
||||
|
||||
# Feature Flags
|
||||
FEATURE_TRAVEL_SNAPSHOT=true
|
||||
|
||||
# Emails
|
||||
APP_DEFAULT_EMAIL_FROM=[email protected]
|
||||
APP_DEFAULT_EMAIL_TO=[email protected]
|
||||
@@ -87,8 +87,6 @@ APP_SEASON_SUMMER_FROM=2026-05-01
|
||||
# Use 'O' during beta phase, switch to 'F' for production
|
||||
DEFAULT_BOOKING_STATUS=F
|
||||
|
||||
API_KEYS=
|
||||
|
||||
XML_EXPORT_PATH="%kernel.project_dir%/var/xmlexport"
|
||||
XML_EXPORT_CONTINGENTS_PATH="%kernel.project_dir%/var/xmlexportzimmer"
|
||||
JSON_EXPORT_PATH="%kernel.project_dir%/var/jsonexport"
|
||||
|
||||
@@ -258,6 +258,15 @@ services:
|
||||
arguments:
|
||||
$bodyDimensionRanges: '%body_dimension_ranges%'
|
||||
|
||||
App\BpnConnect\AbstractApiClient:
|
||||
abstract: true
|
||||
arguments:
|
||||
$baseUrl: '%env(default::BPN_CONNECT_BASE_URL)%'
|
||||
$apiKey: '%env(default::BPN_CONNECT_API_KEY)%'
|
||||
|
||||
App\BpnConnect\ContingentsClient:
|
||||
parent: App\BpnConnect\AbstractApiClient
|
||||
|
||||
App\Service\MailjetApiClient:
|
||||
arguments:
|
||||
$apiKey: '%env(default::MAILJET_API_KEY)%'
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect;
|
||||
|
||||
use App\BpnConnect\Exception\BpnConnectException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
abstract class AbstractApiClient
|
||||
{
|
||||
public function __construct(
|
||||
protected readonly HttpClientInterface $httpClient,
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly ?string $baseUrl = null,
|
||||
protected readonly ?string $apiKey = null,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $query
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function request(string $path, array $query = []): array
|
||||
{
|
||||
$this->assertConfigured();
|
||||
|
||||
try {
|
||||
$response = $this->httpClient->request('GET', $this->baseUrl.$path, [
|
||||
'headers' => [
|
||||
'X-API-KEY' => $this->apiKey,
|
||||
],
|
||||
'query' => $query,
|
||||
]);
|
||||
|
||||
return $response->toArray();
|
||||
} catch (ExceptionInterface $e) {
|
||||
$this->logger->error('BpnConnect request failed', [
|
||||
'path' => $path,
|
||||
'query' => $query,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
throw new BpnConnectException('BpnConnect request failed: '.$e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
private function assertConfigured(): void
|
||||
{
|
||||
if (null === $this->baseUrl || null === $this->apiKey) {
|
||||
throw new BpnConnectException('BpnConnect client is not configured (missing BPN_CONNECT_BASE_URL or BPN_CONNECT_API_KEY).');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect;
|
||||
|
||||
use App\BpnConnect\Model\ContingentCalendarEntry;
|
||||
use App\BpnConnect\Model\ContingentCalendarMeta;
|
||||
use App\BpnConnect\Model\ContingentCalendarResponse;
|
||||
use App\BpnConnect\Model\ContingentMode;
|
||||
use App\BpnConnect\Model\ContingentRangeEntry;
|
||||
use App\BpnConnect\Model\ContingentRangeResponse;
|
||||
use App\BpnConnect\Model\ContingentStatus;
|
||||
|
||||
class ContingentsClient extends AbstractApiClient
|
||||
{
|
||||
private const string CALENDAR_PATH = '/api/v1/contingents/calendar';
|
||||
|
||||
public function getContingentCalendar(
|
||||
string $hotelCode,
|
||||
string $dateFrom,
|
||||
string $dateTo,
|
||||
): ContingentCalendarResponse {
|
||||
$payload = $this->request(self::CALENDAR_PATH, [
|
||||
'hotelCode' => $hotelCode,
|
||||
'dateFrom' => $dateFrom,
|
||||
'dateTo' => $dateTo,
|
||||
'mode' => ContingentMode::Days->value,
|
||||
]);
|
||||
|
||||
$entries = array_map(
|
||||
static fn (array $item) => new ContingentCalendarEntry(
|
||||
date: $item['date'],
|
||||
status: ContingentStatus::from($item['status']),
|
||||
),
|
||||
$payload['data'] ?? [],
|
||||
);
|
||||
|
||||
return new ContingentCalendarResponse($this->buildMeta($payload), $entries);
|
||||
}
|
||||
|
||||
public function getContingentRanges(
|
||||
string $hotelCode,
|
||||
string $dateFrom,
|
||||
string $dateTo,
|
||||
): ContingentRangeResponse {
|
||||
$payload = $this->request(self::CALENDAR_PATH, [
|
||||
'hotelCode' => $hotelCode,
|
||||
'dateFrom' => $dateFrom,
|
||||
'dateTo' => $dateTo,
|
||||
'mode' => ContingentMode::Ranges->value,
|
||||
]);
|
||||
|
||||
$entries = array_map(
|
||||
static fn (array $item) => new ContingentRangeEntry(
|
||||
dateFrom: $item['date_from'],
|
||||
dateTo: $item['date_to'],
|
||||
status: ContingentStatus::from($item['status']),
|
||||
),
|
||||
$payload['data'] ?? [],
|
||||
);
|
||||
|
||||
return new ContingentRangeResponse($this->buildMeta($payload), $entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $payload
|
||||
*/
|
||||
private function buildMeta(array $payload): ContingentCalendarMeta
|
||||
{
|
||||
$raw = $payload['meta'] ?? [];
|
||||
|
||||
return new ContingentCalendarMeta(
|
||||
dateFrom: $raw['date_from'] ?? '',
|
||||
dateTo: $raw['date_to'] ?? '',
|
||||
mode: $raw['mode'] ?? '',
|
||||
hotelCode: $raw['hotel_code'] ?? '',
|
||||
hotelId: (int) ($raw['hotel_id'] ?? 0),
|
||||
rowCount: (int) ($raw['row_count'] ?? 0),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Exception;
|
||||
|
||||
class BpnConnectException extends \RuntimeException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
readonly class ContingentCalendarEntry
|
||||
{
|
||||
public function __construct(
|
||||
public string $date,
|
||||
public ContingentStatus $status,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
readonly class ContingentCalendarMeta
|
||||
{
|
||||
public function __construct(
|
||||
public string $dateFrom,
|
||||
public string $dateTo,
|
||||
public string $mode,
|
||||
public string $hotelCode,
|
||||
public int $hotelId,
|
||||
public int $rowCount,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
readonly class ContingentCalendarResponse
|
||||
{
|
||||
/**
|
||||
* @param ContingentCalendarEntry[] $data
|
||||
*/
|
||||
public function __construct(
|
||||
public ContingentCalendarMeta $meta,
|
||||
public array $data,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
enum ContingentMode: string
|
||||
{
|
||||
case Days = 'days';
|
||||
case Ranges = 'ranges';
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
readonly class ContingentRangeEntry
|
||||
{
|
||||
public function __construct(
|
||||
public string $dateFrom,
|
||||
public string $dateTo,
|
||||
public ContingentStatus $status,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
readonly class ContingentRangeResponse
|
||||
{
|
||||
/**
|
||||
* @param ContingentRangeEntry[] $data
|
||||
*/
|
||||
public function __construct(
|
||||
public ContingentCalendarMeta $meta,
|
||||
public array $data,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
enum ContingentStatus: string
|
||||
{
|
||||
case Ok = 'OK';
|
||||
case Blocked = 'BLOCKED';
|
||||
}
|
||||
Reference in new issue
Block a user