diff --git a/.env b/.env index a3b4e40..ecf9f58 100644 --- a/.env +++ b/.env @@ -44,7 +44,8 @@ APP_BASE_URI=https://myep-team.ddev.site APP_BPN_USER= APP_BPN_PASSWORD= -APP_BPN_ENDPOINT= +APP_BPN_IP= +APP_BPN_PORT= APP_BPN_DEBUG=false # This hotel code will be assigned to admin users together with ROLE_HOTEL_MANAGER diff --git a/composer.json b/composer.json index 5e5a0bb..c28d26b 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "ext-ctype": "*", "ext-iconv": "*", "ext-simplexml": "*", + "ext-sockets": "*", "beberlei/doctrineextensions": "^1.5", "doctrine/doctrine-bundle": "^2.10", "doctrine/doctrine-migrations-bundle": "^3.2", diff --git a/config/services.yaml b/config/services.yaml index 08c2a47..457e467 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -65,7 +65,8 @@ services: $options: bpn_username: '%env(APP_BPN_USER)%' bpn_password: '%env(APP_BPN_PASSWORD)%' - bpn_url: '%env(APP_BPN_ENDPOINT)%' + bpn_api_ip: '%env(APP_BPN_IP)%' + bpn_api_port: '%env(APP_BPN_PORT)%' debug: '%env(bool:APP_BPN_DEBUG)%' App\BusProNet\ResponseParser: diff --git a/src/BusProNet/ApiClient.php b/src/BusProNet/ApiClient.php index 532a167..aa48654 100644 --- a/src/BusProNet/ApiClient.php +++ b/src/BusProNet/ApiClient.php @@ -11,10 +11,11 @@ use Psr\Log\LoggerInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Uid\Uuid; -use Symfony\Contracts\HttpClient\HttpClientInterface; class ApiClient { + use ApiClientTrait; + public const TYPE_NOTIFICATION = 'HINWEIS'; public const TYPE_CUSTOMER_DATA = 'KUNDENKONTO'; public const TYPE_BASE_DATA_COUNTRIES = 'STAMMLAENDER'; @@ -24,7 +25,6 @@ class ApiClient private array $config; public function __construct( - private readonly HttpClientInterface $httpClient, private readonly SerializerInterface $serializer, private readonly ResponseParser $responseParser, private readonly LoggerInterface $logger, @@ -35,6 +35,7 @@ class ApiClient /** * @throws ApiClientException + * @throws ResponseParserException */ public function getProfile(string $email, string $password): NotificationResponse|ProfileResponse { @@ -54,6 +55,7 @@ class ApiClient /** * @throws ApiClientException + * @throws ResponseParserException */ public function updateProfile(User $user, string $password): NotificationResponse|ProfileResponse { @@ -79,6 +81,7 @@ class ApiClient /** * @throws ApiClientException + * @throws ResponseParserException */ public function resetPassword(string $email): NotificationResponse { @@ -97,6 +100,7 @@ class ApiClient /** * @throws ApiClientException + * @throws ResponseParserException */ public function getCrmAttributes(string $email, string $password): NotificationResponse|CrmAttributesResponse { @@ -116,6 +120,7 @@ class ApiClient /** * @throws ApiClientException + * @throws ResponseParserException */ public function getBaseData(string $type): NotificationResponse|BaseDataResponse { @@ -132,6 +137,7 @@ class ApiClient /** * @throws ApiClientException + * @throws ResponseParserException */ private function sendRequest(string $type, array $data): mixed { @@ -149,30 +155,26 @@ class ApiClient ]); } - try { - $response = $this->httpClient->request('GET', $this->config['bpn_url'], [ - 'query' => [ - 'operation' => $body, - ], - 'verify_peer' => false, - 'verify_host' => false, + $socket = $this->connect( + $this->config['bpn_api_ip'], + $this->config['bpn_api_port'], + $this->config['max_retries'] + ); + $this->send($socket, $body); + $response = $this->receive($socket); + $this->disconnect($socket); + + // message length (10 bytes) is prepended to actual message + $xml = substr($response, 10); + + if (true === $this->config['debug']) { + $this->logger->info('Response received', [ + 'id' => $requestId, + 'response' => $xml, ]); - - $xml = $response->getContent(); - - if (true === $this->config['debug']) { - $this->logger->info('Response received', [ - 'id' => $requestId, - 'response' => $xml, - ]); - } - - return $this->responseParser->parseXmlString($type, $xml); - } catch (\Throwable $e) { } - $this->logger->error('API error', ['error' => $e->getMessage()]); - throw new ApiClientException($e->getMessage()); + return $this->responseParser->parseXmlString($type, $xml); } private function createKey(string $username, string $password, string $type): string @@ -185,8 +187,14 @@ class ApiClient private function resolveOptions(array $options): array { $optionsResolver = new OptionsResolver(); - $optionsResolver->setRequired(['bpn_url', 'bpn_username', 'bpn_password']); + $optionsResolver->setRequired([ + 'bpn_username', + 'bpn_password', + 'bpn_api_ip', + 'bpn_api_port', + ]); $optionsResolver->setDefaults([ + 'max_retries' => 25, 'debug' => false, ]); diff --git a/src/BusProNet/ApiClientTrait.php b/src/BusProNet/ApiClientTrait.php new file mode 100644 index 0000000..2ae6921 --- /dev/null +++ b/src/BusProNet/ApiClientTrait.php @@ -0,0 +1,76 @@ + $tries) { + $this->logger->warning('Could not connect to socket, retrying', [ + 'error_message' => $errStr, + 'error_number' => $errNo, + ]); + ++$tries; + sleep(1); + $socket = $openSocket($errNo, $errStr); + } + + if (false !== $socket) { + stream_set_timeout($socket, 60); + } else { + $this->logger->error('Unable to open socket', [ + 'error_message' => $errStr, + 'error_number' => $errNo, + ]); + throw new ApiClientException('Unable to open socket'); + } + + return $socket; + } + + private function send($socket, string $data): void + { + // message length is prepended to actual message + $send = sprintf('%010s', strlen($data)) . $data; + fwrite($socket, $send); + } + + private function receive($socket): string + { + $response = ''; + + while (false === feof($socket)) { + $response .= fread($socket, 4096); + } + + return $response; + } + + private function disconnect($socket): void + { + @fclose($socket); + } +} \ No newline at end of file