From ca369400494afa2cdf07137479376d652b0be902 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 8 Oct 2026 02:04:30 +0000 Subject: [PATCH] feat: expose searchable national service catalog Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- src/Domain/OfficialDomainCatalog.php | 34 +++++++++++++++++++ .../Unit/Domain/OfficialDomainCatalogTest.php | 19 +++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/Domain/OfficialDomainCatalog.php b/src/Domain/OfficialDomainCatalog.php index d9922c1..1e4229a 100644 --- a/src/Domain/OfficialDomainCatalog.php +++ b/src/Domain/OfficialDomainCatalog.php @@ -106,6 +106,40 @@ public function hasCountry(string $iso2): bool return $this->country($iso2) !== null; } + /** + * @return list + */ + public function searchNationalServices(?string $query = null, int $limit = 200): array + { + $normalizedQuery = mb_strtolower(trim((string) $query), 'UTF-8'); + $limit = max(1, min($limit, 500)); + $results = []; + + foreach ($this->table('servicos-nacionais-v1.01.tsv', 2) as $row) { + $code = $row[0]; + $description = $row[1]; + + if ( + $normalizedQuery !== '' + && !str_contains(mb_strtolower($code, 'UTF-8'), $normalizedQuery) + && !str_contains(mb_strtolower($description, 'UTF-8'), $normalizedQuery) + ) { + continue; + } + + $results[] = [ + 'code' => $code, + 'description' => $description, + ]; + + if (count($results) >= $limit) { + break; + } + } + + return $results; + } + public function hasNationalService(string $code): bool { return $this->nationalService($code) !== null; diff --git a/tests/Unit/Domain/OfficialDomainCatalogTest.php b/tests/Unit/Domain/OfficialDomainCatalogTest.php index fd8d589..50d71b4 100644 --- a/tests/Unit/Domain/OfficialDomainCatalogTest.php +++ b/tests/Unit/Domain/OfficialDomainCatalogTest.php @@ -46,6 +46,25 @@ public function testKnownNationalServiceAndNbsAreAvailableOffline(): void self::assertFalse($this->catalog->hasNbs('000000000')); } + public function testNationalServicesCanBeSearchedByCodeOrDescription(): void + { + $byCode = $this->catalog->searchNationalServices('010101', 10); + self::assertSame('010101', $byCode[0]['code'] ?? null); + self::assertSame('AnĂ¡lise e desenvolvimento de sistemas.', $byCode[0]['description'] ?? null); + + $byDescription = $this->catalog->searchNationalServices('desenvolvimento de sistemas', 10); + self::assertSame('010101', $byDescription[0]['code'] ?? null); + } + + public function testNationalServicesSearchIsBoundedAndCanListTheCatalog(): void + { + $firstTwo = $this->catalog->searchNationalServices(null, 2); + + self::assertCount(2, $firstTwo); + self::assertSame('010101', $firstTwo[0]['code']); + self::assertCount(338, $this->catalog->searchNationalServices(null, 500)); + } + public function testKnownIbsCbsOperationIndicatorIsAvailableOffline(): void { $indicator = $this->catalog->operationIndicator('020101');