Skip to content

Commit

Permalink
feat: padronizar e adicionar campos customizados
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandreBellas committed Feb 18, 2024
1 parent d6f0878 commit ced24f2
Show file tree
Hide file tree
Showing 55 changed files with 1,430 additions and 153 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
/src/**/__tests__ export-ignore
*.spec.php export-ignore
.vscode export-ignore
demo export-ignore
18 changes: 17 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
"*.spec.php": "test-ts"
},

/************
* Editor *
************/
"editor.codeActionsOnSave": {
"source.organizeImports": "always"
},

/**************
* PHP unit *
**************/
Expand All @@ -20,7 +27,16 @@
/************
* cSpell *
************/
"cSpell.words": ["Bordero", "borderos", "Customizado", "Customizados"],
"cSpell.words": [
"Agrupador",
"agrupadores",
"Bordero",
"borderô",
"borderos",
"borderôs",
"Customizado",
"Customizados"
],

/***************
* SonarLint *
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ a partir do _endpoint_ `/token`. [Veja a referência](https://developer.bling.co
Nem todas as entidades do Bling estão permitidas para interação. As atuais são:

- [x] Borderos (`->borderos`)
- [ ] Campos customizados (`->camposCustomizados`)
- [x] Campos customizados (`->camposCustomizados`)
- [ ] Categorias - Lojas (`->categoriasLojas`)
- [ ] Categorias - Produtos (`->categoriasProdutos`)
- [ ] Categorias - Receitas e Despesas (`->categoriasReceitasDespesas`)
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "alebatistella/bling-erp-api",
"description": "Pacote de integração com a API do Bling ERP",
"type": "library",
"version": "1.0.0",
"version": "1.1.0",
"license": "MIT",
"autoload": {
"psr-4": {
Expand Down
3 changes: 3 additions & 0 deletions src/Bling.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace AleBatistella\BlingErpApi;

use AleBatistella\BlingErpApi\Entities\Borderos\Borderos;
use AleBatistella\BlingErpApi\Entities\CamposCustomizados\CamposCustomizados;
use AleBatistella\BlingErpApi\Entities\Shared\BaseEntity;
use AleBatistella\BlingErpApi\Exceptions\BlingInternalException;
use AleBatistella\BlingErpApi\Providers\IoC;
Expand All @@ -12,6 +13,7 @@
* Módulo conector à API do Bling.
*
* @property Borderos $borderos
* @property CamposCustomizados $camposCustomizados
*/
class Bling
{
Expand Down Expand Up @@ -55,6 +57,7 @@ public function __get(string $name)
{
return match ($name) {
'borderos' => $this->getModule(Borderos::class),
'camposCustomizados' => $this->getModule(CamposCustomizados::class),
default => throw new BlingInternalException("A entidade \"$name\" não existe.")
};
}
Expand Down
15 changes: 15 additions & 0 deletions src/BlingTest.spec.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use AleBatistella\BlingErpApi\Bling;
use AleBatistella\BlingErpApi\Entities\Borderos\Borderos;
use AleBatistella\BlingErpApi\Entities\CamposCustomizados\CamposCustomizados;
use PHPUnit\Framework\TestCase;

/**
Expand Down Expand Up @@ -48,4 +49,18 @@ public function testShouldGetBorderosCorrectly(): void

$this->assertInstanceOf($expected, $actual);
}

/**
* Testa obter a entidade Campos Customizados.
*
* @return void
*/
public function testShouldGetCamposCustomizadosCorrectly(): void
{
$expected = CamposCustomizados::class;

$actual = $this->getInstance()->camposCustomizados;

$this->assertInstanceOf($expected, $actual);
}
}
4 changes: 2 additions & 2 deletions src/Contracts/IResponseRootObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
use AleBatistella\BlingErpApi\Entities\Shared\DTO\Request\ResponseOptions;

/**
* Objeto raiz de resposta.
* Interface para o objeto raiz de resposta.
*/
interface IResponseRootObject extends IResponseObject
{
/**
* Constrói a partir de uma resposta de requisição.
*/
public static function fromResponse(ResponseOptions $response): static;
public static function fromResponse(ResponseOptions $response): static|null;
}
86 changes: 86 additions & 0 deletions src/Entities/@shared/BaseResponseObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

namespace AleBatistella\BlingErpApi\Entities\Shared;

use AleBatistella\BlingErpApi\Contracts\IResponseObject;

/**
* Classe base para objetos de retorno.
*/
readonly abstract class BaseResponseObject implements IResponseObject
{
/**
* Construtor base.
*/
public function __construct(...$args)
{
}

/**
* Conjunto de regras de conversão para o método `from`.
*
* Função usada para ser sobrescrita quando há propriedades na classe que
* são "_array_ de objetos".
*
* @return array<string, IResponseObject>
*/
protected static function fromRules(): array
{
return [];
}

/**
* @inheritDoc
*/
public static function from(array $attributes): static
{
$reflectionProperties = (new \ReflectionClass(static::class))->getProperties();
$properties = [];
$fromRules = static::fromRules();

foreach ($reflectionProperties as $reflectionProperty) {
$propName = $reflectionProperty->getName();
$propType = $reflectionProperty->getType();
$propTypeName = $propType->getName();
$allowsNull = $reflectionProperty->getType()->allowsNull();

// Null check
if ($allowsNull && !array_key_exists($propName, $attributes)) {
$properties[$propName] = null;
continue;
}

if (in_array($propTypeName, ['int', 'string', 'bool', 'float'])) {
$properties[$propName] = $attributes[$propName];
} else if ($propTypeName === 'array') {
if (array_key_exists($propName, $fromRules)) {
// Array de objetos
$properties[$propName] = array_map(
fn(array $item) => $fromRules[$propName]::from($item),
$attributes[$propName]
);

continue;
}

// Array de primitivos
$properties[$propName] = $attributes[$propName];
} else {
// Objeto
/** @var IResponseObject */
$objSignature = $propTypeName;
$properties[$propName] = $objSignature::from($attributes[$propName]);
}
}

return new static(...$properties);
}

/**
* @inheritDoc
*/
public function toArray(): array
{
return objectToArray($this);
}
}
36 changes: 36 additions & 0 deletions src/Entities/@shared/BaseResponseRootObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace AleBatistella\BlingErpApi\Entities\Shared;

use AleBatistella\BlingErpApi\Contracts\IResponseRootObject;
use AleBatistella\BlingErpApi\Entities\Shared\DTO\Request\ResponseOptions;
use AleBatistella\BlingErpApi\Exceptions\BlingInternalException;

/**
* Classe base para objetos raiz de retorno.
*/
readonly abstract class BaseResponseRootObject extends BaseResponseObject implements IResponseRootObject
{
/**
* @inheritDoc
*/
public abstract static function fromResponse(ResponseOptions $response): static|null;

/**
* Lança `BlingInternalException` caso a resposta da API esteja
* inconsistente.
*
* @param ResponseOptions $response
*
* @return never
*/
protected static function throwForInconsistentResponseOptions(ResponseOptions $response): never
{
throw new BlingInternalException(
message: "Resposta inconsistente da API: $response->method $response->endpoint",
responseHeaders: $response->headers,
responseBody: $response->body,
code: $response->status,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,12 @@ public function testShouldInstantiateFromResponseOptions(): void
}
}';
$rawResponseArray = json_decode($rawResponse, true);
$response = new class (endpoint: fake()->word(),
$response = new ResponseOptions(
endpoint: fake()->word(),
method: fake()->word(),
status: 200,
body: new class ($rawResponseArray) extends Body { },
) extends ResponseOptions {
};
body: new Body($rawResponseArray),
);
$expected = ErrorResponse::class;

$actual = ErrorResponse::fromResponse($response);
Expand Down
6 changes: 4 additions & 2 deletions src/Entities/@shared/DTO/Request/Body.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

namespace AleBatistella\BlingErpApi\Entities\Shared\DTO\Request;

use AleBatistella\BlingErpApi\Exceptions\BlingInternalException;

/**
* Corpo da requisição.
*/
class Body
readonly class Body
{
/**
* Constrói o objeto.
Expand All @@ -14,7 +16,7 @@ class Body
*
* @return self
*/
public function __construct(public readonly array $content)
public function __construct(public array $content)
{
}
}
19 changes: 13 additions & 6 deletions src/Entities/@shared/DTO/Request/RequestOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,26 @@
*/
class RequestOptions
{
public ?QueryParams $queryParams;
public ?Body $body;
public ?Headers $headers;

/**
* Constrói o objeto.
*
* @param string $endpoint
* @param ?QueryParams $queryParams
* @param ?Headers $headers
* @param ?Body $body
* @param QueryParams|array|null $queryParams
* @param Headers|array|null $headers
* @param Body|array|null $body
*/
public function __construct(
public string $endpoint,
public ?QueryParams $queryParams = null,
public ?Headers $headers = null,
public ?Body $body = null,
QueryParams|array|null $queryParams = null,
Headers|array|null $headers = null,
Body|array|null $body = null,
) {
$this->queryParams = is_array($queryParams) ? new QueryParams($queryParams) : $queryParams;
$this->headers = is_array($headers) ? new Headers($headers) : $headers;
$this->body = is_array($body) ? new Body($body) : $body;
}
}
10 changes: 8 additions & 2 deletions src/Entities/@shared/DTO/Request/ResponseOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/**
* Dados da resposta de uma requisição.
*/
abstract class ResponseOptions
class ResponseOptions
{
/**
* Constrói o objeto.
Expand All @@ -29,10 +29,16 @@ public function __construct(
if ($status >= 400) {
try {
$errorResponse = ErrorResponse::fromResponse($this);
throw new BlingApiException(rawResponse: $errorResponse, status: $status);

throw new BlingApiException(
rawResponse: $errorResponse,
status: $status
);
} catch (\TypeError $e) {
throw new BlingInternalException(
message: "Não foi possível realizar a chamada HTTP: $method $endpoint",
responseHeaders: $headers,
responseBody: $body,
code: $status,
previous: $e
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ public function testShouldInstantiateWithNormalConditions(): void
{
$expected = ResponseOptions::class;

$actual = new class (endpoint: fake()->word(),
$actual = new ResponseOptions(
endpoint: fake()->word(),
method: fake()->word(),
status: 200
) extends ResponseOptions {
};
);

$this->assertInstanceOf($expected, $actual);
}
Expand Down Expand Up @@ -68,12 +68,12 @@ public function testShouldInstantiateWithErrorAndExpectedBody(): void
}';
$rawResponseArray = json_decode($rawResponse, true);

new class (endpoint: fake()->word(),
new ResponseOptions(
endpoint: fake()->word(),
method: fake()->word(),
status: 400,
body: new class ($rawResponseArray) extends Body { }
) extends ResponseOptions {
};
body: new Body($rawResponseArray)
);
}

/**
Expand All @@ -88,11 +88,11 @@ public function testShouldInstantiateWithErrorAndNotExpectedBody(): void
$this->expectException(BlingInternalException::class);
$this->expectExceptionMessage("Não foi possível realizar a chamada HTTP: $method $endpoint");

new class (endpoint: $endpoint,
new ResponseOptions(
endpoint: $endpoint,
method: $method,
status: 400,
body: new class (['teste' => '123']) extends Body { }
) extends ResponseOptions {
};
body: new Body(['teste' => '123'])
);
}
}
Loading

0 comments on commit ced24f2

Please sign in to comment.