Skip to content

Commit

Permalink
adds support for fipe endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
gutocf committed May 25, 2022
1 parent 8239e06 commit 5187e26
Show file tree
Hide file tree
Showing 24 changed files with 460 additions and 8 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
{
"cSpell.words": [
"brasilapi",
"caminhoes",
"cnpj",
"Cnpj",
"fipe",
"Fipe",
"ispb",
"Spatie"
]
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@
"phpstan": "vendor/bin/phpstan",
"coverage": "phpunit --colors=always --bootstrap=tests/bootstrap.php --configuration=phpunit.xml --coverage-html=tmp/_reports/coverage/"
}
}
}
16 changes: 12 additions & 4 deletions src/Adapter/AbstractAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Gutocf\BrasilAPI\Adapter;

use Gutocf\BrasilAPI\Exception\BadRequestException;
use Gutocf\BrasilAPI\Exception\InternalServerErrorException;
use Gutocf\BrasilAPI\Exception\NotFoundException;
use GuzzleHttp\ClientInterface;
Expand Down Expand Up @@ -36,11 +37,12 @@ public function __construct(ClientInterface $client)
*
* @param string $method HTTP method
* @param string $path Path to the resource
* @param array<string, mixed> $queryParams Query parameters
* @return mixed[]
*/
protected function request(string $method, string $path): array
protected function request(string $method, string $path, array $queryParams = []): array
{
$uri = $this->getUri($path);
$uri = $this->getUri($path, $queryParams);
$response = $this->client->request($method, $uri, self::DEFAULT_OPTIONS);
$this->handleErrors($response);

Expand All @@ -56,6 +58,10 @@ protected function request(string $method, string $path): array
*/
private function handleErrors(ResponseInterface $response): void
{
if ($response->getStatusCode() === 400) {
throw new BadRequestException($response);
}

if ($response->getStatusCode() === 404) {
throw new NotFoundException($response);
}
Expand Down Expand Up @@ -83,12 +89,14 @@ private function parseResponseBody(ResponseInterface $response): array
*
* @param string $path
* @return UriInterface
* @param array<string, mixed> $queryParams Query parameters
*/
private function getUri(string $path): UriInterface
private function getUri(string $path, array $queryParams = []): UriInterface
{
return (new Uri())
->withScheme(self::SCHEME)
->withHost(self::HOST)
->withPath($path);
->withPath($path)
->withQuery(http_build_query($queryParams));
}
}
2 changes: 1 addition & 1 deletion src/Adapter/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Adapter extends AbstractAdapter
/**
* @inheritDoc
*/
public function get(string $path): array
public function get(string $path, array $queryParams = []): array
{
return $this->request('GET', $path);
}
Expand Down
5 changes: 3 additions & 2 deletions src/Adapter/AdapterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ interface AdapterInterface
/**
* Sends a GET request.
*
* @param string $path
* @param string $path Path to the resource.
* @param array<string, mixed> $queryParams Query parameters.
* @throws \Gutocf\BrasilAPI\Exception\NotFoundException;
* @return mixed[] Data returned by the API.
*/
public function get(string $path): array;
public function get(string $path, array $queryParams = []): array;
}
11 changes: 11 additions & 0 deletions src/BrasilAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Gutocf\BrasilAPI\Service\V1\CepService as V1CepService;
use Gutocf\BrasilAPI\Service\V1\CnpjService as V1CnpjService;
use Gutocf\BrasilAPI\Service\V1\DddService as V1DddService;
use Gutocf\BrasilAPI\Service\V1\FipeService as V1FipeService;
use Gutocf\BrasilAPI\Service\V1\HolidaysService as V1HolidayService;
use Gutocf\BrasilAPI\Service\V2\CepService as V2CepService;
use GuzzleHttp\Client;
Expand Down Expand Up @@ -82,4 +83,14 @@ public function dddV1(): V1DddService
{
return new V1DddService($this->Adapter);
}

/**
* Returns a instance of fipe service.
*
* @return \Gutocf\BrasilAPI\Service\V1\FipeService
*/
public function fipeV1(): V1FipeService
{
return new V1FipeService($this->Adapter);
}
}
33 changes: 33 additions & 0 deletions src/Entity/V1/Fipe/Brand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Gutocf\BrasilAPI\Entity\V1\Fipe;

use Spatie\DataTransferObject\FlexibleDataTransferObject;

class Brand extends FlexibleDataTransferObject
{
public ?string $name;
public ?string $value;

/**
* Constructor.
*
* @param array<string, mixed> $parameters
*/
public function __construct(array $parameters = [])
{
if (isset($parameters['nome'])) {
$parameters['name'] = $parameters['nome'];
unset($parameters['nome']);
}

if (isset($parameters['valor'])) {
$parameters['value'] = $parameters['valor'];
unset($parameters['valor']);
}

parent::__construct($parameters);
}
}
20 changes: 20 additions & 0 deletions src/Entity/V1/Fipe/Enum/VehicleType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Gutocf\BrasilAPI\Entity\V1\Fipe\Enum;

use MyCLabs\Enum\Enum;

/**
* @method static VehicleType TRUCKS()
* @method static VehicleType CARS()
* @method static VehicleType MOTORCYCLES()
* @extends Enum<string>
*/
final class VehicleType extends Enum
{
private const TRUCKS = 'caminhoes';
private const CARS = 'carros';
private const MOTORCYCLES = 'motos';
}
13 changes: 13 additions & 0 deletions src/Entity/V1/Fipe/ReferenceTable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Gutocf\BrasilAPI\Entity\V1\Fipe;

use Spatie\DataTransferObject\FlexibleDataTransferObject;

class ReferenceTable extends FlexibleDataTransferObject
{
public ?int $codigo;
public ?string $mes;
}
35 changes: 35 additions & 0 deletions src/Entity/V1/Fipe/Vehicle.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Gutocf\BrasilAPI\Entity\V1\Fipe;

use Spatie\DataTransferObject\FlexibleDataTransferObject;

class Vehicle extends FlexibleDataTransferObject
{
public ?float $valor;
public ?string $marca;
public ?string $modelo;
public ?int $anoModelo;
public ?string $combustivel;
public ?string $codigoFipe;
public ?string $mesReferencia;
public ?int $tipoVeiculo;
public ?string $siglaCombustivel;
public ?string $dataConsulta;

/**
* Constructor.
*
* @param array<string, mixed> $parameters
*/
public function __construct(array $parameters = [])
{
if (isset($parameters['valor'])) {
$parameters['valor'] = floatval(intval(preg_replace('/\D/', '', $parameters['valor'])) / 100);
}

parent::__construct($parameters);
}
}
15 changes: 15 additions & 0 deletions src/Exception/BadRequestException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Gutocf\BrasilAPI\Exception;

use Psr\Http\Message\ResponseInterface;

class BadRequestException extends AbstractHttpException
{
public function __construct(ResponseInterface $response)
{
parent::__construct($response, 400);
}
}
71 changes: 71 additions & 0 deletions src/Service/V1/FipeService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

namespace Gutocf\BrasilAPI\Service\V1;

use Gutocf\BrasilAPI\Entity\V1\Fipe\Brand;
use Gutocf\BrasilAPI\Entity\V1\Fipe\Enum\VehicleType;
use Gutocf\BrasilAPI\Entity\V1\Fipe\ReferenceTable;
use Gutocf\BrasilAPI\Entity\V1\Fipe\Vehicle;
use Gutocf\BrasilAPI\Service\AbstractService;

class FipeService extends AbstractService
{
/**
* Retrieve price for a vehicle.
*
* @param string $code Fipe code of the vehicle
* @param int|null $referenceTableId The reference table id
* @throws \Gutocf\BrasilAPI\Exception\InternalServerErrorException
* @throws \Gutocf\BrasilAPI\Exception\BadRequestException
* @throws \Gutocf\BrasilAPI\Exception\NotFoundException
* @return \Gutocf\BrasilAPI\Entity\V1\Fipe\Vehicle[]
*/
public function getAllVehicleByCode(string $code, int $referenceTableId = null): array
{
$path = sprintf('/api/feriados/v1/%s', $code);
$queryParams = ['tabela_referencia' => $referenceTableId];
$data = $this->adapter->get($path, $queryParams);

return array_map(function ($data) {
return new Vehicle($data);
}, $data);
}

/**
* Retrieve price for a vehicle.
*
* @throws \Gutocf\BrasilAPI\Exception\InternalServerErrorException
* @return \Gutocf\BrasilAPI\Entity\V1\Fipe\ReferenceTable[]
*/
public function getReferenceTables(): array
{
$data = $this->adapter->get('/api/fipe/tabelas/v1');

return array_map(function ($data) {
return new ReferenceTable($data);
}, $data);
}

/**
* Retrieve a list of holidays for a given year.
*
* @param \Gutocf\BrasilAPI\Entity\V1\Fipe\Enum\VehicleType|null $vehicleType The vehicle type
* @param int|null $referenceTableId The reference table id
* @throws \Gutocf\BrasilAPI\Exception\BadRequestException
* @throws \Gutocf\BrasilAPI\Exception\InternalServerErrorException
* @throws \Gutocf\BrasilAPI\Exception\NotFoundException
* @return \Gutocf\BrasilAPI\Entity\V1\Fipe\Brand[] Vehicle brands.
*/
public function getAllBrandsByType(?VehicleType $vehicleType = null, ?int $referenceTableId = null): array
{
$path = sprintf('/api/feriados/v1/%s', $vehicleType?->getValue());
$queryParams = ['tabela_referencia' => $referenceTableId];
$data = $this->adapter->get($path, $queryParams);

return array_map(function ($data) {
return new Brand($data);
}, $data);
}
}
36 changes: 36 additions & 0 deletions tests/Adapter/AbstractAdapterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Gutocf\BrasilAPI\Tests\Adapter;

use Gutocf\BrasilAPI\Adapter\AbstractAdapter;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Uri;
use PHPUnit\Framework\TestCase;

class AbstractAdapterTest extends TestCase
{
private AbstractAdapter $adapter;

public function setUp(): void
{
$this->adapter = $this->getMockForAbstractClass(AbstractAdapter::class, [new Client()]);
}

public function testGetUri(): void
{
$reflection = new \ReflectionClass(get_class($this->adapter));
$method = $reflection->getMethod('getUri');
$method->setAccessible(true);
/** @var \GuzzleHttp\Psr7\Uri $uri */
$uri = $method->invoke($this->adapter, '/path/to/resource', [
'foo' => 'bar',
'baz' => 'qux',
'foobar' => '',
'quux' => null
]);
$this->assertInstanceOf(Uri::class, $uri);
$this->assertEquals('https://brasilapi.com.br/path/to/resource?foo=bar&baz=qux&foobar=', $uri->__toString());
}
}
7 changes: 7 additions & 0 deletions tests/BrasilAPITest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Gutocf\BrasilAPI\Service\V1\CepService as V1CepService;
use Gutocf\BrasilAPI\Service\V1\CnpjService as V1CnpjService;
use Gutocf\BrasilAPI\Service\V1\DddService as V1DddService;
use Gutocf\BrasilAPI\Service\V1\FipeService as V1FipeService;
use Gutocf\BrasilAPI\Service\V1\HolidaysService as V1HolidaysService;
use Gutocf\BrasilAPI\Service\V2\CepService as V2CepService;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -57,4 +58,10 @@ public function testDddV1(): void
$service = $this->BrasilAPI->dddV1();
$this->assertInstanceOf(V1DddService::class, $service);
}

public function testFipeV1(): void
{
$service = $this->BrasilAPI->fipeV1();
$this->assertInstanceOf(V1FipeService::class, $service);
}
}
18 changes: 18 additions & 0 deletions tests/Entity/V1/Fipe/BrandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Gutocf\BrasilAPI\Tests\Entity\V1\Fipe;

use Gutocf\BrasilAPI\Entity\V1\Fipe\Brand;
use PHPUnit\Framework\TestCase;

class BrandTest extends TestCase
{
public function testProperties(): void
{
$data = loadFixture('Entity/V1/Fipe/brand');
$brand = new Brand($data);
$this->assertEquals($brand->name, $data['nome']);
$this->assertEquals($brand->value, $data['valor']);
$this->assertObjectNotHasAttribute('invalid', $brand);
}
}
Loading

0 comments on commit 5187e26

Please sign in to comment.