Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change endpoint #5

Merged
merged 3 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .php-cs-fixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
'strict_param' => false,
'array_syntax' => ['syntax' => 'short'],
'concat_space' => ['spacing' => 'one'],
'phpdoc_align' => [],
'phpdoc_align' => ['align' => 'left'],
'phpdoc_summary' => false,
'void_return' => false,
'phpdoc_var_without_name' => false,
Expand Down
31 changes: 28 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# FanCourier bundle

FanCourier integration for Symfony.
Documentation of the API can be found here: https://github.com/FAN-Courier/API-Docs
Documentation of the API can be found here: https://www.fancourier.ro/wp-content/uploads/2023/07/EN_FANCourier_API-2.0-160523.pdf

## Installation

Expand All @@ -19,17 +19,42 @@ should be added automatically to your `config/bundles.php` file by Symfony Flex.
```yaml
# config/packages/answear_fancourier.yaml
answear_fan_courier:
clientId: yourClientId
username: yourUsername
password: yourPassword
apiUrl: apiUrl
logger: customLogger #default: null
```

Logger service must implement Psr\Log\LoggerInterface interface.

## Usage

### TODO
### Get pickup points

```php
namespace App\Service\PickupPointsImporter;

use Answear\FanCourierBundle\Service\PickupPointService;

class FanCourierImport
{
public function __construct(
private PickupPointService $pickupPointService,
) {
}

/**
* @return PickupPointDTO[]
*/
public function getPickupPoints(): array
{
return $this->pickupPointService->getAll();
}
}

```

Above `getPickupPoints` method will return an array of `Answear\FanCourierBundle\DTO\PickupPointDTO` objects.

Final notes
------------
Expand Down
14 changes: 7 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
"webmozart/assert": "^1.3"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.4",
"matthiasnoback/symfony-config-test": "^5.0.0",
"phpro/grumphp": "^1.5.0",
"phpstan/phpstan": "^1.4",
"phpstan/phpstan-webmozart-assert": "^1.2",
"phpunit/phpunit": "10.4.*",
"friendsofphp/php-cs-fixer": "^3.63.2",
"matthiasnoback/symfony-config-test": "^5.2",
"phpro/grumphp": "^2.7.0",
"phpstan/phpstan": "^1.12",
"phpstan/phpstan-webmozart-assert": "^1.2.10",
"phpunit/phpunit": "^10.5.30",
"roave/security-advisories": "dev-master",
"symfony/phpunit-bridge": "6.3.*"
"symfony/phpunit-bridge": "6.4.*"
},
"autoload": {
"psr-4": {
Expand Down
29 changes: 28 additions & 1 deletion src/Client/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

namespace Answear\FanCourierBundle\Client;

use Answear\FanCourierBundle\ConfigProvider;
use Answear\FanCourierBundle\Exception\RequestException;
use Answear\FanCourierBundle\Exception\ResponseException;
use Answear\FanCourierBundle\Logger\FanCourierLogger;
use Answear\FanCourierBundle\Request\LoginRequest;
use Answear\FanCourierBundle\Request\RequestInterface;
use Answear\FanCourierBundle\Response\LoginResponse;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\GuzzleException;
Expand All @@ -15,21 +18,45 @@ class Client
{
private const CONNECTION_TIMEOUT = 10;
private const TIMEOUT = 30;


private ?string $token = null;

public function __construct(
private readonly RequestTransformerInterface $requestTransformer,
private readonly FanCourierLogger $logger,
private readonly ConfigProvider $configProvider,
private ?ClientInterface $client = null,
) {
$this->client ??= new GuzzleClient(['timeout' => self::TIMEOUT, 'connect_timeout' => self::CONNECTION_TIMEOUT]);
}

public function login(): void
{
$loginRequest = new LoginRequest(
$this->configProvider->username,
$this->configProvider->password,
);

$response = $this->request($loginRequest);

$loginResponse = LoginResponse::fromArray(
\json_decode($response->getBody()->getContents(), true),
);

$this->token = $loginResponse->token;
}

public function request(RequestInterface $request): ResponseInterface
{
$this->logger->setRequestId(uniqid('FANCOURIER-', more_entropy: true));

try {
$psrRequest = $this->requestTransformer->transform($request);

if ($this->token) {
$psrRequest = $psrRequest->withHeader('Authorization', 'Bearer ' . $this->token);
}

$this->logger->logRequest($request->getEndpoint(), $psrRequest);

$response = $this->client->send($psrRequest);
Expand Down
14 changes: 7 additions & 7 deletions src/Client/RequestTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Answear\FanCourierBundle\ConfigProvider;
use Answear\FanCourierBundle\Request\RequestInterface;
use Answear\FanCourierBundle\Serializer\Serializer;
use GuzzleHttp\Psr7\Request as HttpRequest;
use GuzzleHttp\Psr7\Uri;
use Psr\Http\Message\RequestInterface as PsrRequestInterface;
Expand All @@ -14,24 +15,23 @@ class RequestTransformer implements RequestTransformerInterface
{
public function __construct(
private readonly ConfigProvider $configProvider,
private Serializer $serializer,
) {
}

public function transform(RequestInterface $request): PsrRequestInterface
{
$url = $this->configProvider->apiUrl . $request->getEndpoint();

$formParams = [
'username' => $this->configProvider->username,
'client_id' => $this->configProvider->clientId,
'user_pass' => $this->configProvider->password,
];
if (!empty($request->getQueryParams())) {
$url .= '?' . http_build_query($request->getQueryParams());
}

return new HttpRequest(
$request->getMethod(),
new Uri($url),
['Content-Type' => 'application/x-www-form-urlencoded'],
http_build_query(array_merge($formParams, $request->getOptions()))
['Content-Type' => 'application/json'],
'GET' === $request->getMethod() ? null : $this->serializer->serialize($request),
);
}
}
1 change: 0 additions & 1 deletion src/ConfigProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
class ConfigProvider
{
public function __construct(
public readonly string $clientId,
public readonly string $username,
public readonly string $password,
public readonly string $apiUrl,
Expand Down
43 changes: 43 additions & 0 deletions src/DTO/AddressDTO.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace Answear\FanCourierBundle\DTO;

use Webmozart\Assert\Assert;

class AddressDTO
{
public function __construct(
public readonly string $locality,
public readonly string $county,
public readonly string $street,
public readonly string $streetNo,
public readonly string $zipCode,
public readonly string $floor,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this property be nullable? Otherwise, will it default to an empty string?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is an empty string by default

public readonly string $reference,
) {
}

/**
* @param array{locality: string, county: string, street: string, streetNo: string, zipCode: string, floor: string, reference: string} $addressArray
*/
public static function fromArray(array $addressArray): self
{
Assert::stringNotEmpty($addressArray['locality']);
Assert::stringNotEmpty($addressArray['county']);
Assert::stringNotEmpty($addressArray['street']);
Assert::stringNotEmpty($addressArray['streetNo']);
Assert::stringNotEmpty($addressArray['zipCode']);

return new self(
$addressArray['locality'],
$addressArray['county'],
$addressArray['street'],
$addressArray['streetNo'],
$addressArray['zipCode'],
$addressArray['floor'],
$addressArray['reference'],
);
}
}
4 changes: 2 additions & 2 deletions src/DTO/DrawerCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ class DrawerCollection
* @param DrawerDTO[] $drawers
*/
public function __construct(
public readonly array $drawers = []
public readonly array $drawers = [],
) {
}

/**
* @param array<array{number: int, type: string}> $drawerArray
* @param array<array{type: string, number: int}> $drawerArray
*/
public static function fromArray(array $drawerArray): self
{
Expand Down
2 changes: 1 addition & 1 deletion src/DTO/DrawerDTO.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class DrawerDTO
{
public function __construct(
public readonly int $number,
public readonly string $type
public readonly string $type,
) {
}
}
56 changes: 23 additions & 33 deletions src/DTO/PickupPointDTO.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,52 +10,42 @@ class PickupPointDTO
{
public function __construct(
public readonly string $id,
public readonly string $code,
public readonly string $name,
public readonly string $routingLocation,
public readonly string $description,
public readonly string $county,
public readonly string $locality,
public readonly string $address,
public readonly string $zipCode,
public readonly string $locationReference,
public readonly AddressDTO $address,
public readonly float $latitude,
public readonly float $longitude,
public readonly ScheduleCollection $schedule,
public readonly DrawerCollection $drawer
public readonly DrawerCollection $drawer,
) {
}

public static function fromArray(array $pickupPoint): self
{
Assert::stringNotEmpty($pickupPoint['Id']);
Assert::stringNotEmpty($pickupPoint['Name']);
Assert::stringNotEmpty($pickupPoint['RoutingLocation']);
Assert::stringNotEmpty($pickupPoint['Description']);
Assert::stringNotEmpty($pickupPoint['County']);
Assert::stringNotEmpty($pickupPoint['Locality']);
Assert::stringNotEmpty($pickupPoint['Address']);
Assert::stringNotEmpty($pickupPoint['ZipCode']);
Assert::stringNotEmpty($pickupPoint['LocationReference']);
Assert::float($pickupPoint['Latitude']);
Assert::float($pickupPoint['Longitude']);
Assert::range($pickupPoint['Latitude'], -90, 90);
Assert::range($pickupPoint['Longitude'], -180, 180);
Assert::count($pickupPoint['Schedule'], 7);
Assert::stringNotEmpty($pickupPoint['id']);
Assert::stringNotEmpty($pickupPoint['name']);
Assert::stringNotEmpty($pickupPoint['routingLocation']);
Assert::stringNotEmpty($pickupPoint['description']);
Assert::notEmpty($pickupPoint['address']);
Assert::stringNotEmpty($pickupPoint['latitude']);
Assert::stringNotEmpty($pickupPoint['longitude']);
Assert::range((float) $pickupPoint['latitude'], -90, 90);
Assert::range((float) $pickupPoint['longitude'], -180, 180);
Assert::count($pickupPoint['schedule'], 7);

return new self(
$pickupPoint['Id'],
$pickupPoint['Name'],
$pickupPoint['RoutingLocation'],
$pickupPoint['Description'],
$pickupPoint['County'],
$pickupPoint['Locality'],
$pickupPoint['Address'],
$pickupPoint['ZipCode'],
$pickupPoint['LocationReference'],
$pickupPoint['Latitude'],
$pickupPoint['Longitude'],
ScheduleCollection::fromArray($pickupPoint['Schedule']),
DrawerCollection::fromArray($pickupPoint['Drawer'])
$pickupPoint['id'],
$pickupPoint['code'],
$pickupPoint['name'],
$pickupPoint['routingLocation'],
$pickupPoint['description'],
AddressDTO::fromArray($pickupPoint['address']),
(float) $pickupPoint['latitude'],
(float) $pickupPoint['longitude'],
ScheduleCollection::fromArray($pickupPoint['schedule']),
DrawerCollection::fromArray($pickupPoint['drawer']),
);
}
}
10 changes: 5 additions & 5 deletions src/DTO/ScheduleCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@ class ScheduleCollection
* @param ScheduleDTO[] $schedules
*/
public function __construct(
public readonly array $schedules = []
public readonly array $schedules = [],
) {
}

/**
* @param array<array{startHour: string, stopHour: string}> $scheduleArray
* @param array<array{firstHour: string, secondHour: string}> $scheduleArray
*/
public static function fromArray(array $scheduleArray): self
{
$schedules = [];
foreach ($scheduleArray as $schedule) {
Assert::stringNotEmpty($schedule['startHour']);
Assert::stringNotEmpty($schedule['stopHour']);
$schedules[] = new ScheduleDTO($schedule['startHour'], $schedule['stopHour']);
Assert::stringNotEmpty($schedule['firstHour']);
Assert::stringNotEmpty($schedule['secondHour']);
$schedules[] = new ScheduleDTO($schedule['firstHour'], $schedule['secondHour']);
}

return new self($schedules);
Expand Down
4 changes: 2 additions & 2 deletions src/DTO/ScheduleDTO.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
class ScheduleDTO
{
public function __construct(
public readonly string $startHour,
public readonly string $stopHour
public readonly string $firstHour,
public readonly string $secondHour,
) {
}
}
1 change: 0 additions & 1 deletion src/DependencyInjection/AnswearFanCourierExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public function load(array $configs, ContainerBuilder $container): void

$definition = $container->getDefinition(ConfigProvider::class);
$definition->setArguments([
$this->config['clientId'],
$this->config['username'],
$this->config['password'],
$this->config['apiUrl'],
Expand Down
3 changes: 1 addition & 2 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ public function getConfigTreeBuilder(): TreeBuilder

$treeBuilder->getRootNode()
->children()
->scalarNode('clientId')->isRequired()->cannotBeEmpty()->end()
?->scalarNode('username')->isRequired()->cannotBeEmpty()->end()
->scalarNode('username')->isRequired()->cannotBeEmpty()->end()
?->scalarNode('password')->isRequired()->cannotBeEmpty()->end()
?->scalarNode('apiUrl')->isRequired()->cannotBeEmpty()->end()
?->scalarNode('logger')->defaultNull()->end()
Expand Down
Loading
Loading