refactor: delete the BusProNet base-data read path
This commit is contained in:
1 parent
0f76c5a7b6
commit
aa66c50979
4 files changed
+17
-168
No files matched your search
+12
-21
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\BusProNet;
|
||||
|
||||
use App\BusProNet\Model\BaseDataResponse;
|
||||
use App\BusProNet\Model\CrmAttributesResponse;
|
||||
use App\BusProNet\Model\NotificationResponse;
|
||||
use App\BusProNet\Model\ProfileResponse;
|
||||
@@ -17,15 +16,24 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
/**
|
||||
* The socket protocol against BusProNet: length prefixed German XML over fsockopen.
|
||||
*
|
||||
* This is the **write** path and the user facing reads that go with it - profile lookup and
|
||||
* update, registration, password reset, the CRM selections behind the leads roles and hotel
|
||||
* codes. All of it is per user and authenticated as that user, which is why it stays here.
|
||||
*
|
||||
* It is not legacy. The base data reads that used to sit alongside it - STAMMLAENDER,
|
||||
* STAMMZUSTIEGE, STAMMHOTELS - are gone: hotels, pickups and countries now come from MyE&P's
|
||||
* REST api (App\BusProNet\DataProvider\*, App\MyEp\Api\ApiClient), which is the one place
|
||||
* that understands BusProNet's XML.
|
||||
*/
|
||||
class ApiClient
|
||||
{
|
||||
use ApiClientTrait;
|
||||
|
||||
public const TYPE_NOTIFICATION = 'HINWEIS';
|
||||
public const TYPE_CUSTOMER_DATA = 'KUNDENKONTO';
|
||||
public const TYPE_BASE_DATA_COUNTRIES = 'STAMMLAENDER';
|
||||
public const TYPE_BASE_DATA_PICKUPS = 'STAMMZUSTIEGE';
|
||||
public const TYPE_BASE_DATA_HOTELS = 'STAMMHOTELS';
|
||||
|
||||
private array $config;
|
||||
|
||||
@@ -204,23 +212,6 @@ class ApiClient
|
||||
return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ApiClientException
|
||||
* @throws ResponseParserException
|
||||
*/
|
||||
public function getBaseData(string $type): NotificationResponse|BaseDataResponse
|
||||
{
|
||||
$data = [
|
||||
'anfrage' => [
|
||||
'user' => $this->config['bpn_username'],
|
||||
'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], $type),
|
||||
'satz' => ['@typ' => $type],
|
||||
],
|
||||
];
|
||||
|
||||
return $this->sendRequest($type, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ApiClientException
|
||||
* @throws ResponseParserException
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\BusProNet\Model;
|
||||
|
||||
class BaseDataResponse
|
||||
{
|
||||
public function __construct(private readonly array $items)
|
||||
{
|
||||
}
|
||||
|
||||
public function getItems(): array
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,11 @@
|
||||
namespace App\BusProNet;
|
||||
|
||||
use App\BusProNet\Model\Address;
|
||||
use App\BusProNet\Model\BaseDataResponse;
|
||||
use App\BusProNet\Model\Communication;
|
||||
use App\BusProNet\Model\Country;
|
||||
use App\BusProNet\Model\CrmAttribute;
|
||||
use App\BusProNet\Model\CrmAttributeGroup;
|
||||
use App\BusProNet\Model\CrmAttributesResponse;
|
||||
use App\BusProNet\Model\Hotel;
|
||||
use App\BusProNet\Model\NotificationResponse;
|
||||
use App\BusProNet\Model\Pickup;
|
||||
use App\BusProNet\Model\ProfileResponse;
|
||||
use App\BusProNet\Model\ProfileUpdateResponse;
|
||||
use App\BusProNet\Model\RegistrationResponse;
|
||||
@@ -64,12 +60,6 @@ class ResponseParser
|
||||
return $this->createRegistrationResponse($xml);
|
||||
}
|
||||
break;
|
||||
case ApiClient::TYPE_BASE_DATA_COUNTRIES:
|
||||
return $this->createCountriesResponse($xml);
|
||||
case ApiClient::TYPE_BASE_DATA_PICKUPS:
|
||||
return $this->createPickupsResponse($xml);
|
||||
case ApiClient::TYPE_BASE_DATA_HOTELS:
|
||||
return $this->createHotelsResponse($xml);
|
||||
}
|
||||
|
||||
throw new ResponseParserException(sprintf('Unrecognised BusProNet response type "%s"', $responseType));
|
||||
@@ -268,75 +258,6 @@ class ResponseParser
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function createCountriesResponse(\SimpleXMLElement $xml): BaseDataResponse
|
||||
{
|
||||
$countries = [];
|
||||
|
||||
foreach ($xml->laender->land as $item) {
|
||||
$itemAttributes = $item->attributes();
|
||||
$token = (string) $itemAttributes['kuerzel'];
|
||||
$country = new Country();
|
||||
$country
|
||||
->setId((int) $itemAttributes['id'])
|
||||
->setName((string) $itemAttributes['bezeichnung'])
|
||||
->setToken((string) $itemAttributes['kuerzel'])
|
||||
->setNationality((string) $itemAttributes['nationalitaet'])
|
||||
;
|
||||
$countries[$token] = $country;
|
||||
}
|
||||
|
||||
return new BaseDataResponse($countries);
|
||||
}
|
||||
|
||||
public function createPickupsResponse(\SimpleXMLElement $xml): BaseDataResponse
|
||||
{
|
||||
$pickups = [];
|
||||
|
||||
foreach ($xml->zustieg as $item) {
|
||||
$itemAttributes = $item->attributes();
|
||||
$id = (int) $itemAttributes['id'];
|
||||
$busProId = (int) $itemAttributes['idbuspro'];
|
||||
$pickup = new Pickup();
|
||||
$pickup
|
||||
->setId($id)
|
||||
->setBusProId($busProId)
|
||||
->setCode((string) $itemAttributes['code'])
|
||||
->setCity((string) $item->ort)
|
||||
->setStreet((string) $item->strasse)
|
||||
;
|
||||
$pickups[$busProId] = $pickup;
|
||||
}
|
||||
|
||||
return new BaseDataResponse($pickups);
|
||||
}
|
||||
|
||||
public function createHotelsResponse(\SimpleXMLElement $xml): BaseDataResponse
|
||||
{
|
||||
$hotels = [];
|
||||
|
||||
foreach ($xml->hotel as $item) {
|
||||
$itemAttributes = $item->attributes();
|
||||
$id = (int) $itemAttributes['id'];
|
||||
$busProId = (int) $itemAttributes['idbuspro'];
|
||||
$hotel = new Hotel();
|
||||
$hotel
|
||||
->setId($id)
|
||||
->setBusProId($busProId)
|
||||
->setCode((string) $itemAttributes['code'])
|
||||
->setName((string) $item->name)
|
||||
->setCountry(strtolower((string) $item->land))
|
||||
->setCity((string) $item->ort)
|
||||
->setStreet((string) $item->strasse)
|
||||
->setPhone((string) $item->telefon)
|
||||
->setType((string) $item->art)
|
||||
->setCostUnit($item->kostenstelle)
|
||||
;
|
||||
$hotels[$busProId] = $hotel;
|
||||
}
|
||||
|
||||
return new BaseDataResponse($hotels);
|
||||
}
|
||||
|
||||
/**
|
||||
* The BusProNet endpoint can answer with an empty or truncated body. Keep the libxml
|
||||
* reason instead of collapsing every shape of broken response into one message.
|
||||
|
||||
@@ -3,10 +3,7 @@
|
||||
namespace App\Tests\BusProNet;
|
||||
|
||||
use App\BusProNet\ApiClient;
|
||||
use App\BusProNet\Model\BaseDataResponse;
|
||||
use App\BusProNet\Model\CrmAttributesResponse;
|
||||
use App\BusProNet\Model\Hotel;
|
||||
use App\BusProNet\Model\Pickup;
|
||||
use App\BusProNet\Model\ProfileResponse;
|
||||
use App\BusProNet\Model\ProfileUpdateResponse;
|
||||
use App\BusProNet\Model\RegistrationResponse;
|
||||
@@ -135,51 +132,6 @@ class ResponseParserTest extends TestCase
|
||||
$this->assertTrue($response->isUpdated());
|
||||
}
|
||||
|
||||
public function testParseSuccessfulPickupsResponse(): void
|
||||
{
|
||||
$content = '<?xml version="1.0" encoding="utf-8" ?><zustiege><zustieg id="1" idbuspro="1" code="298"><ort>Karlsruhe</ort><strasse>A5 - Autohof Bruchsal</strasse><art>BUS</art><hausabholung>False</hausabholung><crsbuchbar>True</crsbuchbar><internetbuchbar>True</internetbuchbar></zustieg><zustieg id="2" idbuspro="2" code="2"><ort>Bonn</ort><strasse>Bahnhof</strasse><art>BUS</art><hausabholung>False</hausabholung><crsbuchbar>True</crsbuchbar><internetbuchbar>True</internetbuchbar></zustieg></zustiege>';
|
||||
|
||||
$parser = $this->getParserInstance();
|
||||
$response = $parser->parseXmlString(ApiClient::TYPE_BASE_DATA_PICKUPS, $content);
|
||||
|
||||
$this->assertInstanceOf(BaseDataResponse::class, $response);
|
||||
|
||||
$pickups = $response->getItems();
|
||||
$this->assertCount(2, $pickups);
|
||||
|
||||
$pickup = reset($pickups);
|
||||
$this->assertInstanceOf(Pickup::class, $pickup);
|
||||
$this->assertEquals(1, $pickup->getId());
|
||||
$this->assertEquals(1, $pickup->getBusProId());
|
||||
$this->assertEquals('298', $pickup->getCode());
|
||||
$this->assertEquals('Karlsruhe', $pickup->getCity());
|
||||
$this->assertEquals('A5 - Autohof Bruchsal', $pickup->getStreet());
|
||||
}
|
||||
|
||||
public function testParseSuccessfulHotelsResponse(): void
|
||||
{
|
||||
$content = '<?xml version="1.0" encoding="utf-8" ?><hotels><hotel id="1" idbuspro="202003" code="HÖCHST"><name>Berggasthof Höchsten</name><ort>Musterort</ort><strasse>Musterstr. 123</strasse><telefon>03444/19100</telefon><art>Gaststätte</art><internetbuchbar>True</internetbuchbar></hotel><hotel id="2" idbuspro="202004" code="Z. LÖW"><name>Brauereigasthof Zum Löwen</name><ort>Musterort</ort><strasse>Musterstr. 123</strasse><telefon>03437/11133</telefon><art>Gaststätte</art><internetbuchbar>True</internetbuchbar><selektiongruppe id="1" idbuspro="1" bezeichnung="Reisearten 1"><selektion id="1" idbuspro="2" bezeichnung="Kurz-und Clubreisen" /><selektion id="2" idbuspro="7" bezeichnung="Musik und Theater" /><selektion id="3" idbuspro="12" bezeichnung="Flugreisen" /><selektion id="4" idbuspro="13" bezeichnung="Tagesfahrten" /><selektion id="5" idbuspro="1" bezeichnung="Städtereisen" /></selektiongruppe><selektiongruppe id="2" idbuspro="2" bezeichnung="Reisearten 2"><selektion id="1" idbuspro="18" bezeichnung="Adventreisen" /></selektiongruppe><selektiongruppe id="3" idbuspro="3" bezeichnung="Statistik 1"><selektion id="1" idbuspro="31" bezeichnung="Städtreisen" /><selektion id="2" idbuspro="32" bezeichnung="Kurz-und Clubreisen" /><selektion id="3" idbuspro="37" bezeichnung="Musik und Theater" /><selektion id="4" idbuspro="42" bezeichnung="Flugreisen" /><selektion id="5" idbuspro="43" bezeichnung="Tagesfahrten" /></selektiongruppe></hotel></hotels>';
|
||||
|
||||
$parser = $this->getParserInstance();
|
||||
$response = $parser->parseXmlString(ApiClient::TYPE_BASE_DATA_HOTELS, $content);
|
||||
|
||||
$this->assertInstanceOf(BaseDataResponse::class, $response);
|
||||
|
||||
$hotels = $response->getItems();
|
||||
$this->assertCount(2, $hotels);
|
||||
|
||||
$hotel = reset($hotels);
|
||||
$this->assertInstanceOf(Hotel::class, $hotel);
|
||||
$this->assertEquals(1, $hotel->getId());
|
||||
$this->assertEquals(202003, $hotel->getBusProId());
|
||||
$this->assertEquals('HÖCHST', $hotel->getCode());
|
||||
$this->assertEquals('Berggasthof Höchsten', $hotel->getName());
|
||||
$this->assertEquals('Musterort', $hotel->getCity());
|
||||
$this->assertEquals('Musterstr. 123', $hotel->getStreet());
|
||||
$this->assertEquals('03444/19100', $hotel->getPhone());
|
||||
$this->assertEquals('Gaststätte', $hotel->getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* A broken response used to collapse into one opaque "Unable to parse XML response",
|
||||
* which is what made the production import failure undiagnosable. Each shape must now
|
||||
@@ -192,17 +144,17 @@ class ResponseParserTest extends TestCase
|
||||
$this->expectException(ResponseParserException::class);
|
||||
$this->expectExceptionMessage('empty response');
|
||||
|
||||
$parser->parseXmlString(ApiClient::TYPE_BASE_DATA_PICKUPS, '');
|
||||
$parser->parseXmlString(ApiClient::TYPE_CUSTOMER_DATA, '');
|
||||
}
|
||||
|
||||
public function testParseTruncatedResponse(): void
|
||||
{
|
||||
$content = '<?xml version="1.0" encoding="utf-8"?><ergebnis><satz typ="STAMMZUSTIEGE"></satz><zustieg id="1"';
|
||||
$content = '<?xml version="1.0" encoding="utf-8"?><ergebnis><satz typ="KUNDENKONTO"></satz><art>Adressdaten</art';
|
||||
|
||||
$parser = $this->getParserInstance();
|
||||
|
||||
try {
|
||||
$parser->parseXmlString(ApiClient::TYPE_BASE_DATA_PICKUPS, $content);
|
||||
$parser->parseXmlString(ApiClient::TYPE_CUSTOMER_DATA, $content);
|
||||
$this->fail('Expected a ResponseParserException');
|
||||
} catch (ResponseParserException $e) {
|
||||
$this->assertStringContainsString('('.strlen($content).' bytes)', $e->getMessage());
|
||||
@@ -219,7 +171,7 @@ class ResponseParserTest extends TestCase
|
||||
$this->expectException(ResponseParserException::class);
|
||||
$this->expectExceptionMessage('Unrecognised BusProNet response type "STAMMIRGENDWAS"');
|
||||
|
||||
$parser->parseXmlString(ApiClient::TYPE_BASE_DATA_PICKUPS, $content);
|
||||
$parser->parseXmlString(ApiClient::TYPE_CUSTOMER_DATA, $content);
|
||||
}
|
||||
|
||||
public function testParsingDoesNotLeakLibxmlErrorState(): void
|
||||
@@ -228,7 +180,7 @@ class ResponseParserTest extends TestCase
|
||||
|
||||
try {
|
||||
try {
|
||||
$this->getParserInstance()->parseXmlString(ApiClient::TYPE_BASE_DATA_PICKUPS, '<ergebnis');
|
||||
$this->getParserInstance()->parseXmlString(ApiClient::TYPE_CUSTOMER_DATA, '<ergebnis');
|
||||
} catch (ResponseParserException) {
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user