This commit is contained in:
Björn Fromme
2023-07-08 16:21:05 +02:00
parent 305699b1ac
commit dc5b9a9238
17 changed files with 297 additions and 176 deletions
+5 -23
View File
@@ -2,16 +2,9 @@
namespace App\BusProNet;
use App\BusProNet\Model\CrmAttributeSelection;
use App\BusProNet\Model\Profile;
use App\BusProNet\Model\ErrorResponse;
use App\BusProNet\Model\Result;
use App\BusProNet\Model\BaseResponse;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class ApiClient
@@ -29,7 +22,7 @@ class ApiClient
/**
* @throws ApiClientException
*/
public function getProfile(string $email, string $password): mixed
public function getProfile(string $email, string $password): BaseResponse
{
$data = [
'anfrage' => [
@@ -56,11 +49,7 @@ class ApiClient
$xml = $response->getContent();
if (str_contains($xml, 'HINWEIS')) {
return $this->serializer->deserialize($xml, ErrorResponse::class, 'xml');
}
return $this->serializer->deserialize($xml, Profile::class, 'xml');
return (new ResponseParser())->parseXmlString($xml);
} catch (\Throwable $e) {
}
@@ -73,7 +62,7 @@ class ApiClient
/**
* @throws ApiClientException
*/
public function getCrmSelection(string $email, string $password): mixed
public function getCrmAttributes(string $email, string $password): BaseResponse
{
$data = [
'anfrage' => [
@@ -100,20 +89,13 @@ class ApiClient
$xml = $response->getContent();
if (str_contains($xml, 'HINWEIS')) {
return $this->serializer->deserialize($xml, ErrorResponse::class, 'xml');
}
return $this->serializer->deserialize($xml, CrmAttributeSelection::class, 'xml');
return (new ResponseParser())->parseXmlString($xml);
} catch (\Throwable $e) {
}
throw new ApiClientException($e->getMessage());
}
/**
* @Creates key for BusPro API access according to documentation
*/
private function createKey(string $username, string $password, string $type): string
{
$date = (new \DateTimeImmutable())->format('Ymd');
-9
View File
@@ -2,20 +2,11 @@
namespace App\BusProNet\Model;
use Symfony\Component\Serializer\Annotation\SerializedName;
class Address
{
#[SerializedName('strasse')]
private ?string $street = null;
#[SerializedName('plz')]
private ?string $postCode = null;
#[SerializedName('ort')]
private ?string $city = null;
#[SerializedName('land')]
private ?string $country = null;
public function getStreet(): ?string
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace App\BusProNet\Model;
class BaseResponse
{
private ?int $code;
private ?string $message;
public function __construct(int $code = null, string $message = null)
{
$this->code = $code;
$this->message = $message;
}
public function getCode(): ?int
{
return $this->code;
}
public function getMessage(): ?string
{
return $this->message;
}
public function isSuccessful(): bool
{
// Successful responses don't carry codes and messages
if (null === $this->getCode() && null === $this->getMessage()) {
return true;
}
// These weird and contradictory looking assertions are required because
// of the stupid API implementation that doesn't distinguish between error
// and success responses.
$responseIsError = 100 <= $this->getCode() || $this->getMessage() === 'Daten konnten nicht gesendet werden.';
$responseIsSuccess = 650 === $this->getCode() && false === stripos($this->getMessage(), 'fehler');
return false === $responseIsError && true === $responseIsSuccess;
}
}
-5
View File
@@ -6,13 +6,8 @@ use Symfony\Component\Serializer\Annotation\SerializedName;
class Communication
{
#[SerializedName('telefonprivat')]
private ?string $phone = null;
#[SerializedName('telefonmobil')]
private ?string $mobile = null;
#[SerializedName('email')]
private ?string $email = null;
public function getPhone(): ?string
+3 -23
View File
@@ -2,19 +2,11 @@
namespace App\BusProNet\Model;
use Symfony\Component\Serializer\Annotation\Ignore;
use Symfony\Component\Serializer\Annotation\SerializedName;
class CrmAttribute
{
#[SerializedName('@id')]
private ?int $id = null;
#[SerializedName('@bezeichnung')]
private ?string $label = null;
#[SerializedName('@auswahl')]
private ?string $selectedAsString = null;
private bool $selected = false;
public function getId(): ?int
{
@@ -40,26 +32,14 @@ class CrmAttribute
return $this;
}
public function getSelectedAsString(): ?string
{
return $this->selectedAsString;
}
public function setSelectedAsString(?string $selectedAsString): static
{
$this->selectedAsString = $selectedAsString;
return $this;
}
public function isSelected(): bool
{
return 'true' === strtolower($this->selectedAsString);
return $this->selected;
}
public function setSelected(bool $selected): static
{
$this->selectedAsString = $selected ? 'True' : 'False';
$this->selected = $selected;
return $this;
}
@@ -2,14 +2,9 @@
namespace App\BusProNet\Model;
use Symfony\Component\Serializer\Annotation\SerializedName;
class CrmAttributeSelectionGroup
class CrmAttributeGroup
{
#[SerializedName('@bezeichnung')]
private ?string $label = null;
#[SerializedName('selektion')]
private ?array $attributes = null;
public function getLabel(): ?string
@@ -24,18 +19,11 @@ class CrmAttributeSelectionGroup
return $this;
}
/**
* @return CrmAttribute[]|null
*/
public function getAttributes(): ?array
{
return $this->attributes;
}
/**
* @param CrmAttribute[]|null $attributes
* @return $this
*/
public function setAttributes(?array $attributes): static
{
$this->attributes = $attributes;
@@ -1,30 +0,0 @@
<?php
namespace App\BusProNet\Model;
use Symfony\Component\Serializer\Annotation\SerializedPath;
class CrmAttributeSelection
{
#[SerializedPath('[selektionsmerkmale][selektionsgruppe]')]
private ?array $selectionGroups = null;
/**
* @return CrmAttributeSelectionGroup[]|null
*/
public function getSelectionGroups(): ?array
{
return $this->selectionGroups;
}
/**
* @param CrmAttributeSelectionGroup[]|null $selectionGroups
* @return $this
*/
public function setSelectionGroups(?array $selectionGroups): static
{
$this->selectionGroups = $selectionGroups;
return $this;
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\BusProNet\Model;
class CrmAttributesResponse extends BaseResponse
{
private ?array $attributeGroups = null;
private bool $teamer = false;
public function getAttributeGroups(): ?array
{
return $this->attributeGroups;
}
public function setAttributeGroups(?array $attributeGroups): static{
$this->attributeGroups = $attributeGroups;
return $this;
}
public function isTeamer(): bool
{
return $this->teamer;
}
public function setTeamer(bool $teamer): static
{
$this->teamer = $teamer;
return $this;
}
}
-38
View File
@@ -1,38 +0,0 @@
<?php
namespace App\BusProNet\Model;
use Symfony\Component\Serializer\Annotation\SerializedPath;
class ErrorResponse
{
#[SerializedPath('[satz][nr]')]
private ?string $code;
#[SerializedPath('[satz][text]')]
private ?string $type;
public function getCode(): ?string
{
return $this->code;
}
public function setCode(?string $code): static
{
$this->code = $code;
return $this;
}
public function getType(): ?string
{
return $this->type;
}
public function setType(?string $type): static
{
$this->type = $type;
return $this;
}
}
@@ -2,41 +2,17 @@
namespace App\BusProNet\Model;
use Symfony\Component\Serializer\Annotation\Context;
use Symfony\Component\Serializer\Annotation\SerializedPath;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
class Profile
class ProfileResponse extends BaseResponse
{
#[SerializedPath('[idadresse]')]
private ?int $addressId = null;
#[SerializedPath('[idperson]')]
private ?int $personId = null;
#[SerializedPath('[adressdaten][name]')]
private ?string $name = null;
#[SerializedPath('[adressdaten][vorname]')]
private ?string $firstName = null;
#[SerializedPath('[adressdaten][anrede]')]
private ?string $salutation = null;
#[SerializedPath('[adressdaten][titel]')]
private ?string $title = null;
#[SerializedPath('[adressdaten][geschlecht]')]
private ?string $gender = null;
#[SerializedPath('[adressdaten][geburtsdatum]')]
#[Context([DateTimeNormalizer::FORMAT_KEY => 'd.m.Y'])]
private ?\DateTimeImmutable $dateOfBirth = null;
#[SerializedPath('[adressdaten][anschrift]')]
private ?Address $address = null;
#[SerializedPath('[adressdaten][kommunikation]')]
private ?Communication $communication = null;
public function getAddressId(): ?int
+143
View File
@@ -0,0 +1,143 @@
<?php
namespace App\BusProNet;
use App\BusProNet\Model\Address;
use App\BusProNet\Model\Communication;
use App\BusProNet\Model\CrmAttribute;
use App\BusProNet\Model\CrmAttributesResponse;
use App\BusProNet\Model\CrmAttributeGroup;
use App\BusProNet\Model\ProfileResponse;
use App\BusProNet\Model\BaseResponse;
class ResponseParser
{
public const ATTR_ID_TEAMER = 1070;
/**
* @throws ResponseParserException
*/
public function parseXmlString(string $content): BaseResponse
{
$xml = simplexml_load_string($content);
$type = (string) $xml->xpath('satz/@typ')[0];
switch ($type) {
case 'HINWEIS':
return $this->createBaseResponse($xml);
case 'KUNDENKONTO':
$subType = (string) $xml->xpath('art')[0];
switch ($subType) {
case 'Adressdaten':
return $this->createProfileResponse($xml);
case 'SelektionCRM':
return $this->createCrmAttributesResponse($xml);
}
}
throw new ResponseParserException('Unable to parse XML response');
}
public function createBaseResponse(\SimpleXMLElement $xml): BaseResponse
{
$code = (int) $xml->xpath('satz/nr')[0];
$message = (string) $xml->xpath('satz/text')[0];
return new BaseResponse($code, $message);
}
public function createProfileResponse(\SimpleXMLElement $xml): ProfileResponse
{
$addressId = (int) $xml->xpath('idadresse')[0];
$personId = (int) $xml->xpath('idperson')[0];
$lastName = (string) $xml->xpath('adressdaten/name')[0];
$firstName = (string) $xml->xpath('adressdaten/vorname')[0];
$salutation = (string) $xml->xpath('adressdaten/anrede')[0];
$title = (string) $xml->xpath('adressdaten/titel')[0];
$gender = (string) $xml->xpath('adressdaten/geschlecht')[0];
$gender = $gender === 'W' ? 'f' : strtolower($gender);
$date = $xml->xpath('adressdaten/geburtsdatum');
$dateOfBirth = $date ?\DateTimeImmutable::createFromFormat('d.m.Y', (string) $date[0]) : null;
$response = new ProfileResponse();
$response
->setAddressId($addressId)
->setPersonId($personId)
->setFirstName($firstName)
->setName($lastName)
->setTitle($title)
->setSalutation($salutation)
->setGender($gender)
->setDateOfBirth($dateOfBirth)
;
$postalAddressStreet = $xml->xpath('adressdaten/anschrift/strasse');
$postalAddressPostcode = $xml->xpath('adressdaten/anschrift/plz');
$postalAddressCity = $xml->xpath('adressdaten/anschrift/ort');
$postalAddressCountry = $xml->xpath('adressdaten/anschrift/land');
$address = new Address();
$address
->setStreet($postalAddressStreet ? (string) $postalAddressStreet[0] : null)
->setPostCode($postalAddressPostcode ? (string) $postalAddressPostcode[0] : null)
->setCity($postalAddressCity ? (string) $postalAddressCity[0] : null)
->setCountry($postalAddressCountry ? (string) $postalAddressCountry[0] : null)
;
$response->setAddress($address);
$phone = $xml->xpath('adressdaten/kommunikation/telefonprivat');
$mobile = $xml->xpath('adressdaten/kommunikation/telefonmobil');
$email = $xml->xpath('adressdaten/kommunikation/email');
$communication = new Communication();
$communication
->setPhone($phone ? (string) $phone[0] : null)
->setMobile($mobile ? (string) $mobile[0] : null)
->setEmail($email ? (string) $email[0] : null)
;
$response->setCommunication($communication);
return $response;
}
public function createCrmAttributesResponse(\SimpleXMLElement $xml): CrmAttributesResponse
{
$groups = [];
$isTeam = false;
foreach ($xml->xpath('selektionsmerkmale/selektionsgruppe') as $item) {
$group = new CrmAttributeGroup();
$group->setLabel($item->attributes()['bezeichnung']);
$attributes = [];
foreach ($item->xpath('selektion') as $subItem) {
$attribute = new CrmAttribute();
$attribute
->setId((int) $subItem->attributes()['id'])
->setLabel((string) $subItem->attributes()['bezeichnung'])
->setSelected('True' === (string) $subItem->attributes()['auswahl'])
;
$attributes[] = $attribute;
if (static::ATTR_ID_TEAMER === $attribute->getId() && true === $attribute->isSelected()) {
$isTeam = true;
}
}
$group->setAttributes($attributes);
$groups[] = $group;
}
$response = new CrmAttributesResponse();
$response
->setAttributeGroups($groups)
->setTeamer($isTeam)
;
return $response;
}
}
@@ -0,0 +1,7 @@
<?php
namespace App\BusProNet;
class ResponseParserException extends \Exception
{
}