Repository: symfony/ux
Pull request #2289 (2024-10-20 14:07:58)

[Icons] Patch to handle Iconify API change
| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| Issues        | Fix #...
| License       | MIT

Until now, the iconify API returned "200" even when the resource (icon data, svg) did not exist.

It will change in [the next days](https://github.com/symfony/ux/issues/2272#issuecomment-2424956881) 

This patch allow the `ux:icon:lock` and the CacheWarmer to work after this change.



Patch:
diff --git a/src/Icons/src/Iconify.php b/src/Icons/src/Iconify.php
index d9d3fa15..f26b1c7b 100644
--- a/src/Icons/src/Iconify.php
+++ b/src/Icons/src/Iconify.php
@@ -55,6 +55,10 @@ final class Iconify
 
         $response = $this->http->request('GET', \sprintf('/%s.json?icons=%s', $prefix, $name));
 
+        if (200 !== $response->getStatusCode()) {
+            throw new IconNotFoundException(\sprintf('The icon "%s:%s" does not exist on iconify.design.', $prefix, $name));
+        }
+
         try {
             $data = $response->toArray();
         } catch (JsonException) {
@@ -87,16 +91,17 @@ final class Iconify
             throw new IconNotFoundException(\sprintf('The icon "%s:%s" does not exist on iconify.design.', $prefix, $name));
         }
 
-        $content = $this->http
-            ->request('GET', \sprintf('/%s/%s.svg', $prefix, $name))
-            ->getContent()
-        ;
+        $response = $this->http->request('GET', \sprintf('/%s/%s.svg', $prefix, $name));
+
+        if (200 !== $response->getStatusCode()) {
+            throw new IconNotFoundException(\sprintf('The icon "%s:%s" does not exist on iconify.design.', $prefix, $name));
+        }
 
-        if (!str_starts_with($content, '<svg')) {
+        if (!str_starts_with($svg = $response->getContent(), '<svg')) {
             throw new IconNotFoundException(\sprintf('The icon "%s:%s" does not exist on iconify.design.', $prefix, $name));
         }
 
-        return $content;
+        return $svg;
     }
 
     public function getIconSets(): array


Test patch:
diff --git a/src/Icons/tests/Unit/IconifyTest.php b/src/Icons/tests/Unit/IconifyTest.php
index f61abf3a..a3aa50bf 100644
--- a/src/Icons/tests/Unit/IconifyTest.php
+++ b/src/Icons/tests/Unit/IconifyTest.php
@@ -144,6 +144,25 @@ class IconifyTest extends TestCase
         $iconify->fetchIcon('bi', 'heart');
     }
 
+    public function testFetchIconThrowsWhenStatusCodeNot200(): void
+    {
+        $iconify = new Iconify(
+            cache: new NullAdapter(),
+            endpoint: 'https://example.com',
+            http: new MockHttpClient([
+                new JsonMockResponse([
+                    'bi' => [],
+                ]),
+                new JsonMockResponse([], ['http_code' => 404]),
+            ]),
+        );
+
+        $this->expectException(IconNotFoundException::class);
+        $this->expectExceptionMessage('The icon "bi:heart" does not exist on iconify.design.');
+
+        $iconify->fetchIcon('bi', 'heart');
+    }
+
     public function testGetMetadata(): void
     {
         $responseFile = __DIR__.'/../Fixtures/Iconify/collections.json';
@@ -170,6 +189,21 @@ class IconifyTest extends TestCase
         $this->stringContains('-.224l.235-.468ZM6.013 2.06c-.649-.1', $svg);
     }
 
+    public function testFetchSvgThrowIconNotFoundExceptionWhenStatusCodeNot200(): void
+    {
+        $client = new MockHttpClient([
+            new MockResponse(file_get_contents(__DIR__.'/../Fixtures/Iconify/collections.json'), [
+                'response_headers' => ['content-type' => 'application/json'],
+            ]),
+            new MockResponse('', ['http_code' => 404]),
+        ]);
+        $iconify = new Iconify(new NullAdapter(), 'https://localhost', $client);
+
+        $this->expectException(IconNotFoundException::class);
+
+        $iconify->fetchSvg('fa6-regular', 'bar');
+    }
+
     private function createHttpClient(mixed $data, int $code = 200): MockHttpClient
     {
         $mockResponse = new JsonMockResponse($data, ['http_code' => $code]);