From aa66c50979289eaed15653475bb8f80a0b72bf4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Tue, 29 Sep 2026 13:01:01 +0200 Subject: [PATCH] refactor: delete the BusProNet base-data read path --- src/BusProNet/ApiClient.php | 33 ++++------ src/BusProNet/Model/BaseDataResponse.php | 15 ----- src/BusProNet/ResponseParser.php | 79 ------------------------ tests/BusProNet/ResponseParserTest.php | 58 ++--------------- 4 files changed, 17 insertions(+), 168 deletions(-) delete mode 100644 src/BusProNet/Model/BaseDataResponse.php diff --git a/src/BusProNet/ApiClient.php b/src/BusProNet/ApiClient.php index d9811ac..753520f 100644 --- a/src/BusProNet/ApiClient.php +++ b/src/BusProNet/ApiClient.php @@ -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 diff --git a/src/BusProNet/Model/BaseDataResponse.php b/src/BusProNet/Model/BaseDataResponse.php deleted file mode 100644 index 0c1ab7e..0000000 --- a/src/BusProNet/Model/BaseDataResponse.php +++ /dev/null @@ -1,15 +0,0 @@ -items; - } -} diff --git a/src/BusProNet/ResponseParser.php b/src/BusProNet/ResponseParser.php index c3ced18..ddbd576 100644 --- a/src/BusProNet/ResponseParser.php +++ b/src/BusProNet/ResponseParser.php @@ -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. diff --git a/tests/BusProNet/ResponseParserTest.php b/tests/BusProNet/ResponseParserTest.php index 8764f59..38b56aa 100644 --- a/tests/BusProNet/ResponseParserTest.php +++ b/tests/BusProNet/ResponseParserTest.php @@ -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 = 'KarlsruheA5 - Autohof BruchsalBUSFalseTrueTrueBonnBahnhofBUSFalseTrueTrue'; - - $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 = 'Berggasthof HöchstenMusterortMusterstr. 12303444/19100GaststätteTrueBrauereigasthof Zum LöwenMusterortMusterstr. 12303437/11133GaststätteTrue'; - - $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 = '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, 'getParserInstance()->parseXmlString(ApiClient::TYPE_CUSTOMER_DATA, '