diff --git a/CHANGELOG.md b/CHANGELOG.md index 87dc459..8b853d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,11 +13,13 @@ Návod pro přechod: [UPGRADE-2.0.md](UPGRADE-2.0.md). - Data k odeslání, která nejdou převést na JSON (např. neplatné UTF-8), vyhodí `EcomailFlexibeeRequestFail`. Verze 1.4 v takovém případě tiše odeslala požadavek bez platného těla. Základní URL bez schématu nebo hostitele je odmítnuta při vytvoření klienta. ### Nové +- `Client::findByCodes()` načte více záznamů evidence podle seznamu kódů jedním requestem (#43). - `Client` a `CompanyClient` přijímají volitelný `HttpClient`. - `Config::debugMode()` a `Config::withEvidence()`. - Výchozí timeouty požadavků (5 s spojení, 25 s celkem). ### Opravy +- Kód záznamu s apostrofem je odmítnut výjimkou `EcomailFlexibeeInvalidRequestParameter`. Dříve mohl změnit význam filtru `(kod eq '…')`. - Ověření TLS certifikátu se při přihlášení přes session nevypíná, pokud je `$verifySSLCertificate` zapnuté. - Název firmy v URL pro obnovu zálohy je zakódovaný (`&` nebo `#` v názvu už nerozbije URL). diff --git a/README.md b/README.md index 0318f05..1170ffc 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,11 @@ $evidenceItem = $client->findByCode($evidenceItemCode, $uriParameters); $evidenceItem = $client->findLastInEvidence($evidenceItemCode, $uriParameters); ``` +Načtení více záznamů najednou podle seznamu kódů (jeden request s filtrem `kod in (...)`). Vrací pole `EvidenceResult` jen pro nalezené záznamy, pro prázdný seznam kódů request neposílá. +``` +$evidenceItems = $client->findByCodes(['KOD1', 'KOD2'], $uriParameters); +``` + ## Sumace ``` $client->sumInEvidence(); diff --git a/src/Client.php b/src/Client.php index 3bedad8..0a4a581 100644 --- a/src/Client.php +++ b/src/Client.php @@ -262,6 +262,36 @@ public function findByCode(string $code, array $uriParameters = []): EvidenceRes } } + /** + * @param list $codes + * @param array $uriParameters + * @return array<\EcomailFlexibee\Result\EvidenceResult> + * @throws \EcomailFlexibee\Exception\EcomailFlexibeeConnectionFail + * @throws \EcomailFlexibee\Exception\EcomailFlexibeeForbidden + * @throws \EcomailFlexibee\Exception\EcomailFlexibeeInvalidAuthorization + * @throws \EcomailFlexibee\Exception\EcomailFlexibeeInvalidRequestParameter + * @throws \EcomailFlexibee\Exception\EcomailFlexibeeMethodNotAllowed + * @throws \EcomailFlexibee\Exception\EcomailFlexibeeNotAcceptableRequest + * @throws \EcomailFlexibee\Exception\EcomailFlexibeeRequestFail + */ + public function findByCodes(array $codes, array $uriParameters = []): array + { + if ($codes === []) { + return []; + } + + return $this->responseHydrator->convertResponseToEvidenceResults( + $this->httpClient->request( + $this->queryBuilder->createUriByCodes($codes, $uriParameters), + Method::GET, + [], + [], + [], + $this->config, + ), + ); + } + /** * @param array $evidenceData * @param array $uriParameters diff --git a/src/Http/UrlBuilder.php b/src/Http/UrlBuilder.php index 7fdfe8f..468f3f4 100644 --- a/src/Http/UrlBuilder.php +++ b/src/Http/UrlBuilder.php @@ -9,8 +9,10 @@ use EcomailFlexibee\Exception\EcomailFlexibeeRequestFail; use EcomailFlexibee\Validator\ParameterValidator; +use function array_map; use function array_merge; use function http_build_query; +use function implode; use function ltrim; use function parse_url; use function rawurlencode; @@ -115,6 +117,22 @@ public function createUriByCodeOnly(string $code, array $uriParameters): string return $this->createUrl($this->buildPathWithIdOrFilter(sprintf('(kod eq \'%s\')', $code)), $uriParameters); } + /** + * @param list $codes + * @param array $uriParameters + * @throws \EcomailFlexibee\Exception\EcomailFlexibeeInvalidRequestParameter + */ + public function createUriByCodes(array $codes, array $uriParameters): string + { + foreach ($codes as $code) { + $this->validator->validateFlexibeeRequestCodeParameter($code); + } + + $quotedCodes = array_map(static fn (string $code): string => sprintf("'%s'", $code), $codes); + + return $this->createUrl($this->buildPathWithIdOrFilter(sprintf('(kod in (%s))', implode(',', $quotedCodes))), $uriParameters); + } + /** * @param array $uriParams */ diff --git a/src/Validator/ParameterValidator.php b/src/Validator/ParameterValidator.php index 2d66dd8..cb86a78 100644 --- a/src/Validator/ParameterValidator.php +++ b/src/Validator/ParameterValidator.php @@ -8,6 +8,7 @@ use function mb_strlen; use function mb_strtoupper; +use function str_contains; final class ParameterValidator { @@ -19,11 +20,13 @@ final class ParameterValidator */ public function validateFlexibeeRequestCodeParameter(string $code): void { - if (mb_strlen($code) <= self::CODE_MAX_LENGTH && mb_strtoupper($code) === $code) { + if (mb_strlen($code) <= self::CODE_MAX_LENGTH && mb_strtoupper($code) === $code && !str_contains($code, "'")) { return; } - throw new EcomailFlexibeeInvalidRequestParameter('Parameter code must have a maximum of 20 characters and must be an uppercase'); + throw new EcomailFlexibeeInvalidRequestParameter( + 'Parameter code must have a maximum of 20 characters, must be an uppercase and must not contain an apostrophe', + ); } } diff --git a/tests/ClientOfflineTest.php b/tests/ClientOfflineTest.php index 7c1e24c..3a1e450 100644 --- a/tests/ClientOfflineTest.php +++ b/tests/ClientOfflineTest.php @@ -11,6 +11,7 @@ use EcomailFlexibee\Http\HttpClient; use EcomailFlexibee\Http\Method; use EcomailFlexibee\Http\Response\FlexibeeResponse; +use EcomailFlexibee\Result\EvidenceResult; use Mockery; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; use Mockery\MockInterface; @@ -65,6 +66,27 @@ public function testGetByIdAndGetByCodeReturnTheEvidence(): void Assert::assertSame([['id' => '1', 'kod' => 'A']], $client->getByCode('A')->getData()); } + public function testFindByCodesReturnsAllFoundRecords(): void + { + $httpClient = Mockery::mock(HttpClient::class); + $httpClient->shouldReceive('request') + ->once() + ->with(Mockery::pattern("/\\(kod%20in%20\\('A','B'\\)\\)\\.json/"), Method::GET, [], [], [], Mockery::type(ClientConfig::class)) + ->andReturn($this->createResponse(['adresar' => [['kod' => 'A'], ['kod' => 'B']]])); + + $results = $this->createClient($httpClient)->findByCodes(['A', 'B']); + + Assert::assertEquals([new EvidenceResult(['kod' => 'A']), new EvidenceResult(['kod' => 'B'])], $results); + } + + public function testFindByCodesWithoutCodesSendsNoRequest(): void + { + $httpClient = Mockery::mock(HttpClient::class); + $httpClient->shouldNotReceive('request'); + + Assert::assertSame([], $this->createClient($httpClient)->findByCodes([])); + } + public function testFindByIdAndFindByCodeReturnEmptyResultWhenMissing(): void { $client = $this->createClient($this->respondWith([], statusCode: 404)); diff --git a/tests/Http/QueryBuilderTest.php b/tests/Http/QueryBuilderTest.php index 5dd2268..3978538 100644 --- a/tests/Http/QueryBuilderTest.php +++ b/tests/Http/QueryBuilderTest.php @@ -5,6 +5,7 @@ namespace EcomailFlexibeeTest\Http; use EcomailFlexibee\Config; +use EcomailFlexibee\Exception\EcomailFlexibeeInvalidRequestParameter; use EcomailFlexibee\Exception\EcomailFlexibeeRequestFail; use EcomailFlexibee\Http\UrlBuilder; use EcomailFlexibeeTest\Config as TestConfig; @@ -29,6 +30,21 @@ public function testCreateUriByCodeOnly(): void ); } + public function testCreateUriByCodes(): void + { + Assert::assertSame( + "https://demo.flexibee.eu/c/demo/adresar/(kod%20in%20('A','CODE:TEST')).json?limit=0&detail=custom%3Aid", + $this->urlBuilder->createUriByCodes(['A', 'CODE:TEST'], ['detail' => 'custom:id']), + ); + } + + public function testCreateUriByCodesValidatesEveryCode(): void + { + $this->expectException(EcomailFlexibeeInvalidRequestParameter::class); + + $this->urlBuilder->createUriByCodes(['A', 'lowercase'], []); + } + public function testCreateUriByEvidenceOnly(): void { Assert::assertSame( diff --git a/tests/Validator/ParameterValidatorTest.php b/tests/Validator/ParameterValidatorTest.php index 4b9a754..9dc6bef 100644 --- a/tests/Validator/ParameterValidatorTest.php +++ b/tests/Validator/ParameterValidatorTest.php @@ -52,7 +52,9 @@ public function getDataForTestValidateFlexibeeRequestCodeParameter(): Iterator public function testReportsOneMessageWhenBothRulesFail(): void { $this->expectException(EcomailFlexibeeInvalidRequestParameter::class); - $this->expectExceptionMessageMatches('/^Parameter code must have a maximum of 20 characters and must be an uppercase$/'); + $this->expectExceptionMessageMatches( + '/^Parameter code must have a maximum of 20 characters, must be an uppercase and must not contain an apostrophe$/', + ); $this->validator->validateFlexibeeRequestCodeParameter('lowercase-code-longer-than-twenty'); } @@ -64,6 +66,13 @@ public function testAcceptsUppercaseCodeWithDiacriticsAtTheLengthLimit(): void $this->validator->validateFlexibeeRequestCodeParameter('ŽLUŤOUČKÝ-KŮŇ-ÚPĚL-Á'); } + public function testRejectsCodeWithApostrophe(): void + { + $this->expectException(EcomailFlexibeeInvalidRequestParameter::class); + + $this->validator->validateFlexibeeRequestCodeParameter("A') OR (1=1"); + } + protected function setUp(): void { parent::setUp();