Skip to content

Commit

Permalink
Merge branch 'v2-updated-bis-api'
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Kurowski committed Feb 4, 2021
2 parents 2551a78 + eb3a965 commit 7485f4c
Show file tree
Hide file tree
Showing 29 changed files with 2,178 additions and 902 deletions.
7 changes: 6 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"minimum-stability": "stable",
"require": {
"php": ">=5.6",
"ext-dom": "*",
"guzzlehttp/guzzle": "^6.3"
},
"require-dev": {
Expand All @@ -20,7 +21,11 @@
"psr-4": {
"HnutiBrontosaurus\\BisApiClient\\": "src/"
},
"classmap": ["src/exceptions.php"]
"classmap": [
"src/exceptions.php",
"src/Response/exceptions.php",
"src/Response/OrganizationalUnit/exceptions.php"
]
},
"config": {
"sort-packages": true
Expand Down
190 changes: 148 additions & 42 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,24 @@
use GuzzleHttp\Client as HttpClient;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\TransferException;
use GuzzleHttp\Psr7\Request;
use HnutiBrontosaurus\BisApiClient\Request\Adoption;
use HnutiBrontosaurus\BisApiClient\Request\EventAttendee;
use HnutiBrontosaurus\BisApiClient\Request\EventParameters;
use HnutiBrontosaurus\BisApiClient\Request\OrganizationalUnitParameters;
use HnutiBrontosaurus\BisApiClient\Request\Parameters;
use HnutiBrontosaurus\BisApiClient\Response\Event;
use HnutiBrontosaurus\BisApiClient\Response\OrganizationalUnit;
use HnutiBrontosaurus\BisApiClient\Response\Event\Event;
use HnutiBrontosaurus\BisApiClient\Response\InvalidContentTypeException;
use HnutiBrontosaurus\BisApiClient\Response\InvalidParametersException;
use HnutiBrontosaurus\BisApiClient\Response\InvalidUserInputException;
use HnutiBrontosaurus\BisApiClient\Response\InvalidXMLStructureException;
use HnutiBrontosaurus\BisApiClient\Response\OrganizationalUnit\OrganizationalUnit;
use HnutiBrontosaurus\BisApiClient\Response\OrganizationalUnit\UnknownOrganizationUnitTypeException;
use HnutiBrontosaurus\BisApiClient\Response\Response;
use HnutiBrontosaurus\BisApiClient\Response\ResponseErrorException;
use HnutiBrontosaurus\BisApiClient\Response\UnauthorizedAccessException;
use HnutiBrontosaurus\BisApiClient\Response\UnknownErrorException;
use Psr\Http\Message\ResponseInterface;


final class Client
Expand All @@ -36,6 +46,7 @@ final class Client
* @param string $username
* @param string $password
* @param HttpClient $httpClient
* @throws InvalidArgumentException
*/
public function __construct($url, $username, $password, HttpClient $httpClient)
{
Expand All @@ -56,24 +67,26 @@ public function __construct($url, $username, $password, HttpClient $httpClient)
}


// events

/**
* @param int $id
* @param EventParameters $params
* @return Event
* @throws BisClientException
* @throws GuzzleException
* @throws ResourceNotFoundException
* @throws NotFoundException
* @throws TransferErrorException
* @throws ResponseErrorException
*/
public function getEvent($id, EventParameters $params = NULL)
public function getEvent($id, EventParameters $params = null)
{
$params = ($params !== NULL ? $params : new EventParameters());
$params = ($params !== null ? $params : new EventParameters());
$params->setId($id);
$response = $this->processRequest($params);

$data = $response->getData();

if (\count($data) === 0) {
throw new BisClientException('No result for event with id `' . $id . '`.');
throw new NotFoundException('No result for event with id `' . $id . '`.');
}

return Event::fromResponseData(\reset($data));
Expand All @@ -82,99 +95,192 @@ public function getEvent($id, EventParameters $params = NULL)
/**
* @param EventParameters $params
* @return Event[]
* @throws BisClientException
* @throws GuzzleException
* @throws ResourceNotFoundException
* @throws NotFoundException
* @throws TransferErrorException
* @throws ResponseErrorException
*/
public function getEvents(EventParameters $params = NULL)
public function getEvents(EventParameters $params = null)
{
$response = $this->processRequest($params !== NULL ? $params : new EventParameters());
$response = $this->processRequest($params !== null ? $params : new EventParameters());
$data = $response->getData();

if ($data === NULL) {
if ($data === null) {
return [];
}

return \array_map(Event::class . '::fromResponseData', $data);
}

/**
* @param EventAttendee $eventAttendee
* @throws ResponseErrorException
*/
public function addAttendeeToEvent(EventAttendee $eventAttendee)
{
$eventAttendee->setCredentials($this->username, $this->password);
$response = $this->httpClient->send($this->createRequest($eventAttendee));

$this->checkForResponseContentType($response);

$domDocument = $this->generateDOM($response);

$this->checkForResponseErrors($domDocument);
}


// organizational units

/**
* @param OrganizationalUnitParameters $params
* @return OrganizationalUnit[]
* @throws BisClientException
* @throws GuzzleException
* @throws ResourceNotFoundException
* @throws NotFoundException
* @throws TransferErrorException
* @throws ResponseErrorException
*/
public function getOrganizationalUnits(OrganizationalUnitParameters $params = NULL)
public function getOrganizationalUnits(OrganizationalUnitParameters $params = null)
{
$response = $this->processRequest($params !== NULL ? $params : new OrganizationalUnitParameters());
return \array_map(OrganizationalUnit::class . '::fromResponseData', $response->getData());
$response = $this->processRequest($params !== null ? $params : new OrganizationalUnitParameters());

$organizationalUnits = [];
foreach ($response->getData() as $organizationalUnit) {
try {
$organizationalUnits[] = OrganizationalUnit::fromResponseData($organizationalUnit);

} catch (UnknownOrganizationUnitTypeException $e) {
continue; // In case of unknown type - just ignore it.

}
}

return $organizationalUnits;
}


// adoption

/**
* @param Adoption $adoption
* @throws ResponseErrorException
*/
public function saveRequestForAdoption(Adoption $adoption)
{
$adoption->setCredentials($this->username, $this->password);

$response = $this->httpClient->send($this->createRequest($adoption));

$this->checkForResponseContentType($response);

$domDocument = $this->generateDOM($response);

$this->checkForResponseErrors($domDocument);
}


/**
* @param Parameters $requestParameters
* @return Response
* @throws BisClientException
* @throws GuzzleException
* @throws ResourceNotFoundException
* @throws NotFoundException
* @throws TransferErrorException
* @throws ResponseErrorException
*/
private function processRequest(Parameters $requestParameters)
{
$requestParameters->setCredentials($this->username, $this->password);

$httpRequest = new Request('POST', $this->buildUrl($requestParameters));

try {
$response = $this->httpClient->send($httpRequest);
$response = $this->httpClient->send($this->createRequest($requestParameters));

} catch (ClientException $e) {
throw new ResourceNotFoundException('Bis client could not find the queried resource.', 0, $e);
throw new NotFoundException('Bis client could not find the queried resource.', 0, $e);

} catch (TransferException $e) {
throw new BisClientException('Unable to process request: transfer error.', 0, $e);
} catch (GuzzleException $e) {
throw new TransferErrorException('Unable to process request: transfer error.', 0, $e);
}

$this->checkForResponseContentType($response);

$domDocument = $this->generateDOM($response);
$this->checkForResponseErrors($domDocument);

return new Response($response, $domDocument);
}


/**
* @param ResponseInterface $response
* @throws InvalidContentTypeException
*/
private function checkForResponseContentType(ResponseInterface $response)
{
if (\strncmp($response->getHeaderLine('Content-Type'), 'text/xml', \strlen('text/xml')) !== 0) {
throw new BisClientException('Unable to process response: the response Content-Type is invalid or missing.');
throw new InvalidContentTypeException('Unable to process response: the response Content-Type is invalid or missing.');
}
}

/**
* @param ResponseInterface $response
* @return \DOMDocument
* @throws InvalidXMLStructureException
*/
private function generateDOM(ResponseInterface $response)
{
try {
$domDocument = new \DOMDocument();
$domDocument->loadXML($response->getBody());

} catch (\Exception $e) {
throw new BisClientException('Unable to process response: response body contains invalid XML.', 0, $e);
throw new InvalidXMLStructureException('Unable to process response: response body contains invalid XML.', 0, $e);
}

return $domDocument;
}

/**
* @param \DOMDocument $domDocument
* @throws ResponseErrorException
*/
private function checkForResponseErrors(\DOMDocument $domDocument)
{
$resultNode = $domDocument->getElementsByTagName(Response::TAG_RESULT)->item(0);
\assert($resultNode instanceof \DOMElement);

if ($resultNode->hasAttribute(Response::TAG_RESULT_ATTRIBUTE_ERROR)) {
switch ($resultNode->getAttribute(Response::TAG_RESULT_ATTRIBUTE_ERROR)) {
case 'success': // In case of POST request with form data, BIS returns `<result error="success" />` for some reason... Let's pretend that there is no error in such case because... you know... there is no error!
break;

case 'user':
throw new InvalidUserInputException($resultNode);
break;

case 'forbidden':
throw new BisClientException('You are not authorized to make such request with given credentials. Or you have simply wrong credentials. :-)');
throw new UnauthorizedAccessException();
break;

case 'params':
throw new BisClientException('Parameters are invalid.');
throw new InvalidParametersException();
break;

default:
throw new BisClientException('Unknown error. Error type returned from BIS: `' . $resultNode->getAttribute(Response::TAG_RESULT_ATTRIBUTE_ERROR) . '`');
throw new UnknownErrorException($resultNode->getAttribute(Response::TAG_RESULT_ATTRIBUTE_ERROR));
break;
}
}

return new Response($response, $domDocument);
}

/**
* @param Parameters $params
* @return string
* @return Request
*/
private function buildUrl(Parameters $params)
private function createRequest(Parameters $parameters)
{
$queryString = $params->getQueryString();
return $this->url . ($queryString !== '' ? '?' . $queryString : '');
return new Request(
'POST',
$this->url,
[
'Content-Type' => 'application/x-www-form-urlencoded',
],
\http_build_query($parameters->getAll())
);
}

}
51 changes: 51 additions & 0 deletions src/Request/Adoption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace HnutiBrontosaurus\BisApiClient\Request;


final class Adoption extends Parameters
{

/**
* @param int $amount
* @param string $firstName
* @param string $lastName
* @param string $streetAddress
* @param string $streetNumber
* @param string $postalCode
* @param string $city
* @param string $emailAddress
* @param int|null $preferredUnitOfTypeRegional
* @param int|null $preferredUnitOfTypeBase
* @param bool $excludeFromPublic
*/
public function __construct(
$amount,
$firstName,
$lastName,
$streetAddress,
$streetNumber,
$postalCode,
$city,
$emailAddress,
$preferredUnitOfTypeRegional = null,
$preferredUnitOfTypeBase = null,
$excludeFromPublic
) {
parent::__construct([
self::PARAM_QUERY => 'adopce',
'f_jmeno' => $firstName,
'f_prijmeni' => $lastName,
'f_ulice' => $streetAddress . ' ' . $streetNumber,
'f_mesto' => $city,
'f_psc' => $postalCode,
'f_email' => $emailAddress,
'f_pohlavi' => null, // not required, but accepted by BIS (values muz/zena)
'f_uvest_v_seznamu' => $excludeFromPublic ? 'off' : 'on',
'f_clanek' => $preferredUnitOfTypeBase,
'f_rc' => $preferredUnitOfTypeRegional,
'f_castka' => $amount,
]);
}

}
Loading

0 comments on commit 7485f4c

Please sign in to comment.