diff --git a/src/Resource/File/Icon.php b/src/Resource/File/Icon.php new file mode 100644 index 0000000..3348427 --- /dev/null +++ b/src/Resource/File/Icon.php @@ -0,0 +1,37 @@ +getRawData()[$this->getType()]; + $this->icon = IconProperty::fromRawData($data); + } + + public function getIcon(): ?IconProperty + { + return $this->icon; + } + + public function setIcon(IconProperty $icon): self + { + $this->icon = $icon; + + return $this; + } +} diff --git a/src/Resource/Property/IconProperty.php b/src/Resource/Property/IconProperty.php new file mode 100644 index 0000000..76443f5 --- /dev/null +++ b/src/Resource/Property/IconProperty.php @@ -0,0 +1,66 @@ +name = (string) ($rawData['name'] ?? ''); + $property->color = (string) ($rawData['color'] ?? ''); + + return $property; + } + + public function getName(): string + { + return $this->name; + } + + public function setName(string $name): self + { + $this->name = $name; + + return $this; + } + + public function getColor(): string + { + return $this->color; + } + + public function setColor(string $color): self + { + $this->color = $color; + + return $this; + } + + public function getUrl(string $mode = 'light'): string + { + if ($this->name === '' || $this->color === '') { + return ''; + } + + $mode = $mode === 'dark' ? 'dark' : 'light'; + + return self::NOTION_ICONS_BASE_URL + . '/' + . rawurlencode($this->name) + . '_' + . rawurlencode($this->color) + . '.svg?mode=' + . $mode; + } +} diff --git a/tests/Endpoint/DatabasesEndpointTest.php b/tests/Endpoint/DatabasesEndpointTest.php index 5225509..13826c0 100644 --- a/tests/Endpoint/DatabasesEndpointTest.php +++ b/tests/Endpoint/DatabasesEndpointTest.php @@ -21,7 +21,10 @@ use Brd6\NotionSdkPhp\Resource\Database\PropertyObject\SelectPropertyObject; use Brd6\NotionSdkPhp\Resource\Database\PropertyObject\TitlePropertyObject; use Brd6\NotionSdkPhp\Resource\File\Emoji; +use Brd6\NotionSdkPhp\Resource\File\Icon; +use Brd6\NotionSdkPhp\Resource\Page; use Brd6\NotionSdkPhp\Resource\Page\Parent\PageIdParent; +use Brd6\NotionSdkPhp\Resource\Pagination\PageOrDatabaseResults; use Brd6\NotionSdkPhp\Resource\Pagination\PageResults; use Brd6\NotionSdkPhp\Resource\Pagination\PaginationRequest; use Brd6\NotionSdkPhp\Resource\Property\SelectProperty; @@ -81,6 +84,45 @@ public function testQueryDatabase(): void $this->assertNotEmpty($resultPage->getId()); } + public function testQueryDatabaseWithIconObject(): void + { + $httpClient = new MockHttpClient(function (string $method, string $url, array $options) { + $this->assertEquals('POST', $method); + $this->assertStringContainsString('databases/21d89ea8-2c3c-8062-a6a2-f83f68500122/query', $url); + + return new MockResponseFactory( + (string) file_get_contents('tests/Fixtures/client_databases_query_page_with_icon_object_200.json'), + [ + 'http_code' => 200, + ], + ); + }); + + $options = (new ClientOptions()) + ->setAuth('secret_valid-auth') + ->setHttpClient($httpClient); + + $client = new Client($options); + + /** @var PageOrDatabaseResults $paginationResponse */ + $paginationResponse = $client->databases()->query('21d89ea8-2c3c-8062-a6a2-f83f68500122'); + + $this->assertNotNull($paginationResponse); + $this->assertInstanceOf(PageOrDatabaseResults::class, $paginationResponse); + $this->assertGreaterThan(0, count($paginationResponse->getResults())); + + /** @var Page $resultPage */ + $resultPage = $paginationResponse->getResults()[0]; + $icon = $resultPage->getIcon(); + + $this->assertNotNull($icon); + $this->assertInstanceOf(Icon::class, $icon); + $this->assertSame('icon', $icon->getType()); + $this->assertNotNull($icon->getIcon()); + $this->assertSame('book', $icon->getIcon()->getName()); + $this->assertSame('gray', $icon->getIcon()->getColor()); + } + public function testQueryDatabaseWithPagination(): void { $httpClient = new MockHttpClient(function (string $method, string $url, array $options) { diff --git a/tests/Fixtures/client_databases_query_page_with_icon_object_200.json b/tests/Fixtures/client_databases_query_page_with_icon_object_200.json new file mode 100644 index 0000000..eb5508a --- /dev/null +++ b/tests/Fixtures/client_databases_query_page_with_icon_object_200.json @@ -0,0 +1,38 @@ +{ + "object": "list", + "results": [ + { + "object": "page", + "id": "21d89ea8-2c3c-8024-850c-e90d903807c3", + "created_time": "2025-06-25T09:45:00.000Z", + "last_edited_time": "2025-06-25T09:45:00.000Z", + "created_by": { + "object": "user", + "id": "7f03dda0-a132-49d7-b8b2-29c9ed1b1f0e" + }, + "last_edited_by": { + "object": "user", + "id": "7f03dda0-a132-49d7-b8b2-29c9ed1b1f0e" + }, + "cover": null, + "icon": { + "type": "icon", + "icon": { + "name": "book", + "color": "gray" + } + }, + "parent": { + "type": "database_id", + "database_id": "21d89ea8-2c3c-8062-a6a2-f83f68500122" + }, + "archived": false, + "properties": {}, + "url": "https://www.notion.so/Overcoming-Work-Obstacles-21d89ea82c3c8024850ce90d903807c3" + } + ], + "next_cursor": null, + "has_more": false, + "type": "page_or_database", + "page_or_database": {} +} diff --git a/tests/Resource/DataSourceTest.php b/tests/Resource/DataSourceTest.php index 303880e..aeb2cf1 100644 --- a/tests/Resource/DataSourceTest.php +++ b/tests/Resource/DataSourceTest.php @@ -7,6 +7,7 @@ use Brd6\NotionSdkPhp\Exception\InvalidResourceException; use Brd6\NotionSdkPhp\Resource\DataSource; use Brd6\NotionSdkPhp\Resource\Database\PropertyObject\RelationPropertyObject; +use Brd6\NotionSdkPhp\Resource\File\Icon; use Brd6\NotionSdkPhp\Resource\Page\Parent\DatabaseIdParent; use Brd6\NotionSdkPhp\Resource\Page\Parent\PageIdParent; use PHPUnit\Framework\TestCase; @@ -52,4 +53,32 @@ public function testDataSource(): void ); $this->assertNotEmpty($dataSource->toArray()); } + + public function testDataSourceWithIconObject(): void + { + /** @var array $rawData */ + $rawData = (array) json_decode( + (string) file_get_contents('tests/Fixtures/client_data_sources_retrieve_200.json'), + true, + ); + + $rawData['icon'] = [ + 'type' => 'icon', + 'icon' => [ + 'name' => 'book', + 'color' => 'gray', + ], + ]; + + /** @var DataSource $dataSource */ + $dataSource = DataSource::fromRawData($rawData); + + $icon = $dataSource->getIcon(); + $this->assertNotNull($icon); + $this->assertInstanceOf(Icon::class, $icon); + $this->assertSame('icon', $icon->getType()); + $this->assertNotNull($icon->getIcon()); + $this->assertSame('book', $icon->getIcon()->getName()); + $this->assertSame('gray', $icon->getIcon()->getColor()); + } } diff --git a/tests/Resource/DatabaseTest.php b/tests/Resource/DatabaseTest.php index e7ef294..f2618cd 100644 --- a/tests/Resource/DatabaseTest.php +++ b/tests/Resource/DatabaseTest.php @@ -9,6 +9,7 @@ use Brd6\NotionSdkPhp\Resource\Database\PartialDataSource; use Brd6\NotionSdkPhp\Resource\File\Emoji; use Brd6\NotionSdkPhp\Resource\File\External; +use Brd6\NotionSdkPhp\Resource\File\Icon; use PHPUnit\Framework\TestCase; use function array_filter; @@ -119,4 +120,32 @@ public function testDatabaseWithDataSources(): void $this->assertNotEmpty($database->getDataSources()[0]->getId()); $this->assertNotEmpty($database->getDataSources()[0]->getName()); } + + public function testDatabaseWithIconObject(): void + { + /** @var array $rawData */ + $rawData = (array) json_decode( + (string) file_get_contents('tests/Fixtures/client_databases_retrieve_database_200.json'), + true, + ); + + $rawData['icon'] = [ + 'type' => 'icon', + 'icon' => [ + 'name' => 'book', + 'color' => 'gray', + ], + ]; + + /** @var Database $database */ + $database = Database::fromRawData($rawData); + + $icon = $database->getIcon(); + $this->assertNotNull($icon); + $this->assertInstanceOf(Icon::class, $icon); + $this->assertSame('icon', $icon->getType()); + $this->assertNotNull($icon->getIcon()); + $this->assertSame('book', $icon->getIcon()->getName()); + $this->assertSame('gray', $icon->getIcon()->getColor()); + } } diff --git a/tests/Resource/File/IconTest.php b/tests/Resource/File/IconTest.php new file mode 100644 index 0000000..b007cb0 --- /dev/null +++ b/tests/Resource/File/IconTest.php @@ -0,0 +1,44 @@ + 'icon', + 'icon' => [ + 'name' => 'add', + 'color' => 'blue', + ], + ]); + + $this->assertInstanceOf(Icon::class, $file); + $this->assertSame('icon', $file->getType()); + $this->assertNotNull($file->getIcon()); + $this->assertSame('add', $file->getIcon()->getName()); + $this->assertSame('blue', $file->getIcon()->getColor()); + } + + public function testToArrayRoundTrip(): void + { + $rawData = [ + 'type' => 'icon', + 'icon' => [ + 'name' => 'book', + 'color' => 'gray', + ], + ]; + + $file = AbstractFile::fromRawData($rawData); + + $this->assertEquals($rawData, $file->toArray()); + } +} diff --git a/tests/Resource/PageTest.php b/tests/Resource/PageTest.php index 599a42c..50b9794 100644 --- a/tests/Resource/PageTest.php +++ b/tests/Resource/PageTest.php @@ -7,6 +7,7 @@ use Brd6\NotionSdkPhp\Exception\InvalidResourceException; use Brd6\NotionSdkPhp\Resource\File\Emoji; use Brd6\NotionSdkPhp\Resource\File\External; +use Brd6\NotionSdkPhp\Resource\File\Icon; use Brd6\NotionSdkPhp\Resource\Page; use Brd6\NotionSdkPhp\Resource\Page\PropertyValue\NumberPropertyValue; use PHPUnit\Framework\TestCase; @@ -70,6 +71,34 @@ public function testPageWithPageObject(): void $this->assertNotEmpty($external->getUrl()); } + public function testPageWithIconObject(): void + { + /** @var array $rawData */ + $rawData = (array) json_decode( + (string) file_get_contents('tests/Fixtures/client_request_retrieve_page_200.json'), + true, + ); + + $rawData['icon'] = [ + 'type' => 'icon', + 'icon' => [ + 'name' => 'book', + 'color' => 'gray', + ], + ]; + + /** @var Page $page */ + $page = Page::fromRawData($rawData); + + $icon = $page->getIcon(); + $this->assertNotNull($icon); + $this->assertInstanceOf(Icon::class, $icon); + $this->assertSame('icon', $icon->getType()); + $this->assertNotNull($icon->getIcon()); + $this->assertSame('book', $icon->getIcon()->getName()); + $this->assertSame('gray', $icon->getIcon()->getColor()); + } + public function testPageProperties(): void { /** @var Page $page */ diff --git a/tests/Resource/Property/IconPropertyTest.php b/tests/Resource/Property/IconPropertyTest.php new file mode 100644 index 0000000..abd0059 --- /dev/null +++ b/tests/Resource/Property/IconPropertyTest.php @@ -0,0 +1,64 @@ + 'book', + 'color' => 'gray', + ]); + + $this->assertSame('book', $property->getName()); + $this->assertSame('gray', $property->getColor()); + } + + public function testFromRawDataWithMissingValues(): void + { + $property = IconProperty::fromRawData([]); + + $this->assertSame('', $property->getName()); + $this->assertSame('', $property->getColor()); + $this->assertSame('', $property->getUrl()); + } + + public function testGetUrl(): void + { + $property = IconProperty::fromRawData([ + 'name' => 'add', + 'color' => 'blue', + ]); + + $this->assertSame( + 'https://www.notion.so/icons/add_blue.svg?mode=light', + $property->getUrl(), + ); + $this->assertSame( + 'https://www.notion.so/icons/add_blue.svg?mode=dark', + $property->getUrl('dark'), + ); + $this->assertSame( + 'https://www.notion.so/icons/add_blue.svg?mode=light', + $property->getUrl('unsupported'), + ); + } + + public function testToArrayRoundTrip(): void + { + $rawData = [ + 'name' => 'book', + 'color' => 'gray', + ]; + + $property = IconProperty::fromRawData($rawData); + + $this->assertSame($rawData, $property->toArray()); + } +}