wip: api client for bpn-connect
This commit is contained in:
1 parent
a8c0801c32
commit
b3210a7a27
12 files changed
+262
-5
No files matched your search
@@ -48,6 +48,9 @@ MAILJET_DEFAULT_LIST_ID=
|
|||||||
MAILJET_WEBHOOK_BASIC_PASSWORD=secret
|
MAILJET_WEBHOOK_BASIC_PASSWORD=secret
|
||||||
APP_NEWSLETTER_CONFIRMATION_TTL_HOURS=1
|
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_USER=
|
||||||
APP_BPN_PASSWORD=
|
APP_BPN_PASSWORD=
|
||||||
APP_BPN_IP=
|
APP_BPN_IP=
|
||||||
@@ -72,9 +75,6 @@ APP_TRAVEL_PREFER_REMOTE=false
|
|||||||
APP_TRAVEL_ENABLE_FALLBACK=true
|
APP_TRAVEL_ENABLE_FALLBACK=true
|
||||||
APP_TRAVEL_SNAPSHOT_RETENTION_BUFFER_DAYS=14
|
APP_TRAVEL_SNAPSHOT_RETENTION_BUFFER_DAYS=14
|
||||||
|
|
||||||
# Feature Flags
|
|
||||||
FEATURE_TRAVEL_SNAPSHOT=true
|
|
||||||
|
|
||||||
# Emails
|
# Emails
|
||||||
APP_DEFAULT_EMAIL_FROM=[email protected]
|
APP_DEFAULT_EMAIL_FROM=[email protected]
|
||||||
APP_DEFAULT_EMAIL_TO=[email protected]
|
APP_DEFAULT_EMAIL_TO=[email protected]
|
||||||
@@ -88,8 +88,6 @@ APP_SEASON_SUMMER_FROM=2026-05-01
|
|||||||
# Use 'O' during beta phase, switch to 'F' for production
|
# Use 'O' during beta phase, switch to 'F' for production
|
||||||
DEFAULT_BOOKING_STATUS=F
|
DEFAULT_BOOKING_STATUS=F
|
||||||
|
|
||||||
API_KEYS=
|
|
||||||
|
|
||||||
XML_EXPORT_PATH="%kernel.project_dir%/var/xmlexport"
|
XML_EXPORT_PATH="%kernel.project_dir%/var/xmlexport"
|
||||||
XML_EXPORT_CONTINGENTS_PATH="%kernel.project_dir%/var/xmlexportzimmer"
|
XML_EXPORT_CONTINGENTS_PATH="%kernel.project_dir%/var/xmlexportzimmer"
|
||||||
JSON_EXPORT_PATH="%kernel.project_dir%/var/jsonexport"
|
JSON_EXPORT_PATH="%kernel.project_dir%/var/jsonexport"
|
||||||
|
|||||||
@@ -258,6 +258,15 @@ services:
|
|||||||
arguments:
|
arguments:
|
||||||
$bodyDimensionRanges: '%body_dimension_ranges%'
|
$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:
|
App\Service\MailjetApiClient:
|
||||||
arguments:
|
arguments:
|
||||||
$apiKey: '%env(default::MAILJET_API_KEY)%'
|
$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