diff --git a/src/Entities/Blocks/Block.php b/src/Entities/Blocks/Block.php index 68fb58e..507ef60 100644 --- a/src/Entities/Blocks/Block.php +++ b/src/Entities/Blocks/Block.php @@ -2,9 +2,11 @@ namespace FiveamCode\LaravelNotionApi\Entities\Blocks; -use DateTime; use FiveamCode\LaravelNotionApi\Entities\Entity; use FiveamCode\LaravelNotionApi\Exceptions\HandlingException; +use FiveamCode\LaravelNotionApi\Traits\HasArchive; +use FiveamCode\LaravelNotionApi\Traits\HasParent; +use FiveamCode\LaravelNotionApi\Traits\HasTimestamps; use Illuminate\Support\Arr; /** @@ -12,6 +14,8 @@ */ class Block extends Entity { + use HasTimestamps, HasArchive, HasParent; + /** * @var string */ @@ -37,16 +41,6 @@ class Block extends Entity */ protected string $text = '[warning: unsupported in notion api]'; - /** - * @var DateTime - */ - protected DateTime $createdTime; - - /** - * @var DateTime - */ - protected DateTime $lastEditedTime; - /** * @param array $responseData * @@ -65,12 +59,10 @@ protected function setResponseData(array $responseData): void protected function fillFromRaw(): void { - $this->fillId(); + parent::fillEssentials(); $this->fillType(); $this->fillRawContent(); $this->fillHasChildren(); - $this->fillCreatedTime(); - $this->fillLastEditedTime(); } private function fillType(): void @@ -126,22 +118,6 @@ public function hasChildren(): bool return $this->hasChildren; } - /** - * @return DateTime - */ - public function getCreatedTime(): DateTime - { - return $this->createdTime; - } - - /** - * @return DateTime - */ - public function getLastEditedTime(): DateTime - { - return $this->lastEditedTime; - } - public function getContent() { return $this->content; diff --git a/src/Entities/Database.php b/src/Entities/Database.php index 453c106..9df0eee 100644 --- a/src/Entities/Database.php +++ b/src/Entities/Database.php @@ -2,9 +2,12 @@ namespace FiveamCode\LaravelNotionApi\Entities; -use DateTime; use FiveamCode\LaravelNotionApi\Entities\Properties\Property; +use FiveamCode\LaravelNotionApi\Entities\PropertyItems\RichText; use FiveamCode\LaravelNotionApi\Exceptions\HandlingException; +use FiveamCode\LaravelNotionApi\Traits\HasArchive; +use FiveamCode\LaravelNotionApi\Traits\HasParent; +use FiveamCode\LaravelNotionApi\Traits\HasTimestamps; use Illuminate\Support\Arr; use Illuminate\Support\Collection; @@ -13,11 +16,18 @@ */ class Database extends Entity { + use HasTimestamps, HasArchive, HasParent; + /** * @var string */ protected string $title = ''; + /** + * @var string + */ + protected string $description = ''; + /** * @var string */ @@ -44,14 +54,19 @@ class Database extends Entity private string $url; /** - * @var string + * @var ?RichText */ - protected string $objectType = ''; + protected ?RichText $richTitle = null; /** - * @var array + * @var ?RichText */ - protected array $rawTitle = []; + protected ?RichText $richDescription = null; + + /** + * @var bool + */ + protected bool $isInline = false; /** * @var array @@ -73,16 +88,6 @@ class Database extends Entity */ protected Collection $properties; - /** - * @var DateTime - */ - protected DateTime $createdTime; - - /** - * @var DateTime - */ - protected DateTime $lastEditedTime; - protected function setResponseData(array $responseData): void { parent::setResponseData($responseData); @@ -94,22 +99,36 @@ protected function setResponseData(array $responseData): void private function fillFromRaw() { - $this->fillId(); + parent::fillEssentials(); $this->fillIcon(); $this->fillCover(); $this->fillTitle(); - $this->fillObjectType(); + $this->fillIsInline(); + $this->fillDescription(); $this->fillProperties(); $this->fillDatabaseUrl(); - $this->fillCreatedTime(); - $this->fillLastEditedTime(); } private function fillTitle(): void { if (Arr::exists($this->responseData, 'title') && is_array($this->responseData['title'])) { $this->title = Arr::first($this->responseData['title'], null, ['plain_text' => ''])['plain_text']; - $this->rawTitle = $this->responseData['title']; + $this->richTitle = new RichText($this->responseData['title']); + } + } + + private function fillIsInline(): void + { + if (Arr::exists($this->responseData, 'is_inline')) { + $this->isInline = $this->responseData['is_inline']; + } + } + + private function fillDescription(): void + { + if (Arr::exists($this->responseData, 'description') && is_array($this->responseData['description'])) { + $this->description = Arr::first($this->responseData['description'], null, ['plain_text' => ''])['plain_text']; + $this->richDescription = new RichText($this->responseData['description']); } } @@ -146,13 +165,6 @@ private function fillCover(): void } } - private function fillObjectType(): void - { - if (Arr::exists($this->responseData, 'object')) { - $this->objectType = $this->responseData['object']; - } - } - private function fillProperties(): void { if (Arr::exists($this->responseData, 'properties')) { @@ -184,17 +196,25 @@ public function getProperty(string $propertyKey): ?Property /** * @return string */ - public function getObjectType(): string + public function getTitle(): string { - return $this->objectType; + return $this->title; + } + + /** + * @return bool + */ + public function isInline(): bool + { + return $this->isInline; } /** * @return string */ - public function getTitle(): string + public function getDescription(): string { - return $this->title; + return $this->description; } /** @@ -246,42 +266,34 @@ public function getProperties(): Collection } /** - * @return array + * @return ?RichText */ - public function getRawTitle(): array + public function getRichTitle(): ?RichText { - return $this->rawTitle; + return $this->richTitle; } /** - * @return array + * @return ?RichText */ - public function getRawProperties(): array + public function getRichDescription(): ?RichText { - return $this->rawProperties; + return $this->richDescription; } /** * @return array */ - public function getPropertyKeys(): array - { - return $this->propertyKeys; - } - - /** - * @return DateTime - */ - public function getCreatedTime(): DateTime + public function getRawProperties(): array { - return $this->createdTime; + return $this->rawProperties; } /** * @return array */ - public function getLastEditedTime(): DateTime + public function getPropertyKeys(): array { - return $this->lastEditedTime; + return $this->propertyKeys; } } diff --git a/src/Entities/Entity.php b/src/Entities/Entity.php index 9e571bd..b23671f 100644 --- a/src/Entities/Entity.php +++ b/src/Entities/Entity.php @@ -2,7 +2,6 @@ namespace FiveamCode\LaravelNotionApi\Entities; -use Carbon\Carbon; use FiveamCode\LaravelNotionApi\Exceptions\HandlingException; use FiveamCode\LaravelNotionApi\Exceptions\NotionException; use Illuminate\Support\Arr; @@ -18,6 +17,11 @@ class Entity implements JsonSerializable */ private string $id; + /** + * @var string + */ + protected string $objectType = ''; + /** * @var array */ @@ -68,25 +72,62 @@ protected function setResponseData(array $responseData): void $this->responseData = $responseData; } - protected function fillCreatedTime() + protected function fillEssentials(): void + { + $this->fillId(); + $this->fillObjectType(); + $this->fillTraitAttributes(); + } + + private function fillTraitAttributes(): void { - if (Arr::exists($this->responseData, 'created_time')) { - $this->createdTime = new Carbon($this->responseData['created_time']); + $traitMapping = [ + 'FiveamCode\LaravelNotionApi\Traits\HasTimestamps' => function ($entity) { + $entity->fillTimestampableAttributes(); + }, + 'FiveamCode\LaravelNotionApi\Traits\HasParent' => function ($entity) { + $entity->fillParentAttributes(); + }, + 'FiveamCode\LaravelNotionApi\Traits\HasArchive' => function ($entity) { + $entity->fillArchivedAttributes(); + }, + ]; + + $traits = $this->class_uses_deep($this); + foreach ($traits as $trait) { + if (Arr::exists($traitMapping, $trait)) { + $traitMapping[$trait]($this); + } } } - protected function fillLastEditedTime() + private function class_uses_deep($class, $autoload = true) { - if (Arr::exists($this->responseData, 'last_edited_time')) { - $this->lastEditedTime = new Carbon($this->responseData['last_edited_time']); + $traits = []; + + do { + $traits = array_merge(class_uses($class, $autoload), $traits); + } while ($class = get_parent_class($class)); + + foreach ($traits as $trait => $same) { + $traits = array_merge(class_uses($trait, $autoload), $traits); } + + return array_unique($traits); } - protected function fillId() + private function fillId() { $this->id = $this->responseData['id']; } + private function fillObjectType(): void + { + if (Arr::exists($this->responseData, 'object')) { + $this->objectType = $this->responseData['object']; + } + } + /** * @return string */ @@ -100,6 +141,14 @@ public function setId($id): void $this->id = $id; } + /** + * @return string + */ + public function getObjectType(): string + { + return $this->objectType; + } + /** * @return array */ diff --git a/src/Entities/Page.php b/src/Entities/Page.php index 08d03d1..c857162 100644 --- a/src/Entities/Page.php +++ b/src/Entities/Page.php @@ -17,6 +17,9 @@ use FiveamCode\LaravelNotionApi\Entities\Properties\Title; use FiveamCode\LaravelNotionApi\Entities\Properties\Url; use FiveamCode\LaravelNotionApi\Exceptions\HandlingException; +use FiveamCode\LaravelNotionApi\Traits\HasArchive; +use FiveamCode\LaravelNotionApi\Traits\HasParent; +use FiveamCode\LaravelNotionApi\Traits\HasTimestamps; use Illuminate\Support\Arr; use Illuminate\Support\Collection; @@ -25,6 +28,8 @@ */ class Page extends Entity { + use HasTimestamps, HasArchive, HasParent; + /** * @var string */ @@ -55,21 +60,6 @@ class Page extends Entity */ private string $coverType = ''; - /** - * @var string - */ - private string $parentId = ''; - - /** - * @var string - */ - private string $parentType = ''; - - /** - * @var string - */ - protected string $objectType = ''; - /** * @var array */ @@ -90,16 +80,6 @@ class Page extends Entity */ protected Collection $properties; - /** - * @var DateTime - */ - protected DateTime $createdTime; - - /** - * @var DateTime - */ - protected DateTime $lastEditedTime; - /** * Page constructor. * @@ -131,23 +111,12 @@ protected function setResponseData(array $responseData): void private function fillFromRaw(): void { - $this->fillId(); - $this->fillParent(); - $this->fillObjectType(); + parent::fillEssentials(); $this->fillProperties(); $this->fillTitle(); // This has to be called after fillProperties(), since title is provided by properties $this->fillPageUrl(); $this->fillIcon(); $this->fillCover(); - $this->fillCreatedTime(); - $this->fillLastEditedTime(); - } - - private function fillObjectType(): void - { - if (Arr::exists($this->responseData, 'object')) { - $this->objectType = $this->responseData['object']; - } } /** @@ -216,20 +185,6 @@ private function fillPageUrl(): void } } - private function fillParent(): void - { - if (Arr::exists($this->responseData, 'parent')) { - $this->parentType = $this->responseData['parent']['type']; - if (Arr::exists($this->responseData['parent'], 'database_id')) { - $this->parentId = $this->responseData['parent']['database_id']; - } elseif (Arr::exists($this->responseData['parent'], 'page_id')) { - $this->parentId = $this->responseData['parent']['page_id']; - } elseif (Arr::exists($this->responseData['parent'], 'workspace')) { - $this->parentId = $this->responseData['parent']['workspace']; - } - } - } - /** * @param $propertyTitle * @param $property @@ -475,30 +430,6 @@ public function getProperty(string $propertyKey): ?Property return $this->propertyMap[$propertyKey]; } - /** - * @return string - */ - public function getObjectType(): string - { - return $this->objectType; - } - - /** - * @return string - */ - public function getParentId(): string - { - return $this->parentId; - } - - /** - * @return string - */ - public function getParentType(): string - { - return $this->parentType; - } - /** * @return array */ @@ -514,20 +445,4 @@ public function getPropertyKeys(): array { return $this->propertyKeys; } - - /** - * @return DateTime - */ - public function getCreatedTime(): DateTime - { - return $this->createdTime; - } - - /** - * @return DateTime - */ - public function getLastEditedTime(): DateTime - { - return $this->lastEditedTime; - } } diff --git a/src/Entities/Properties/Property.php b/src/Entities/Properties/Property.php index 2ceb17c..e647f31 100644 --- a/src/Entities/Properties/Property.php +++ b/src/Entities/Properties/Property.php @@ -62,7 +62,7 @@ protected function setResponseData(array $responseData): void protected function fillFromRaw(): void { - $this->fillId(); + parent::fillEssentials(); $this->fillType(); $this->fillContent(); } diff --git a/src/Entities/PropertyItems/SelectItem.php b/src/Entities/PropertyItems/SelectItem.php index 42e8437..1cf7cb3 100644 --- a/src/Entities/PropertyItems/SelectItem.php +++ b/src/Entities/PropertyItems/SelectItem.php @@ -36,7 +36,7 @@ protected function setResponseData(array $responseData): void protected function fillFromRaw(): void { - $this->fillId(); + parent::fillEssentials(); $this->fillName(); $this->fillColor(); } diff --git a/src/Entities/User.php b/src/Entities/User.php index e337bf3..87ae619 100644 --- a/src/Entities/User.php +++ b/src/Entities/User.php @@ -37,7 +37,7 @@ protected function setResponseData(array $responseData): void private function fillFromRaw(): void { - $this->fillId(); + parent::fillEssentials(); $this->fillName(); $this->fillAvatarUrl(); } diff --git a/src/Traits/HasArchive.php b/src/Traits/HasArchive.php new file mode 100644 index 0000000..1235ea7 --- /dev/null +++ b/src/Traits/HasArchive.php @@ -0,0 +1,41 @@ +fillArchived(); + } + + private function fillArchived(): void + { + if (Arr::exists($this->responseData, 'archived')) { + $this->archived = $this->responseData['archived']; + } + } + + /** + * @return bool + */ + public function isArchived(): bool + { + return $this->archived; + } +} diff --git a/src/Traits/HasParent.php b/src/Traits/HasParent.php new file mode 100644 index 0000000..8356ae6 --- /dev/null +++ b/src/Traits/HasParent.php @@ -0,0 +1,57 @@ +fillParent(); + } + + private function fillParent(): void + { + if (Arr::exists($this->responseData, 'parent') && Arr::exists($this->responseData['parent'], 'type')) { + $this->parentType = $this->responseData['parent']['type']; + if (Arr::exists($this->responseData['parent'], $this->parentType)) { + $this->parentId = $this->responseData['parent'][$this->parentType]; + } + } + } + + /** + * @return string + */ + public function getParentId(): string + { + return $this->parentId; + } + + /** + * @return string + */ + public function getParentType(): string + { + return $this->parentType; + } +} diff --git a/src/Traits/HasTimestamps.php b/src/Traits/HasTimestamps.php new file mode 100644 index 0000000..2e769c4 --- /dev/null +++ b/src/Traits/HasTimestamps.php @@ -0,0 +1,107 @@ +fillCreatedTime(); + $this->fillLastEditedTime(); + $this->fillCreatedBy(); + $this->fillLastEditedBy(); + } + + private function fillCreatedTime(): void + { + if (Arr::exists($this->responseData, 'created_time')) { + $this->createdTime = new Carbon($this->responseData['created_time']); + } + } + + private function fillLastEditedTime(): void + { + if (Arr::exists($this->responseData, 'last_edited_time')) { + $this->lastEditedTime = new Carbon($this->responseData['last_edited_time']); + } + } + + private function fillCreatedBy(): void + { + if (Arr::exists($this->responseData, 'created_by')) { + $this->createdBy = new User($this->responseData['created_by']); + } + } + + private function fillLastEditedBy(): void + { + if (Arr::exists($this->responseData, 'last_edited_by')) { + $this->lastEditedBy = new User($this->responseData['last_edited_by']); + } + } + + /** + * @return ?DateTime + */ + public function getCreatedTime(): ?DateTime + { + return $this->createdTime; + } + + /** + * @return ?DateTime + */ + public function getLastEditedTime(): ?DateTime + { + return $this->lastEditedTime; + } + + /** + * @return ?User + */ + public function getCreatedBy(): ?User + { + return $this->createdBy; + } + + /** + * @return ?User + */ + public function getLastEditedBy(): ?User + { + return $this->lastEditedBy; + } +} diff --git a/tests/EndpointBlocksTest.php b/tests/EndpointBlocksTest.php index f26ea0b..089814f 100644 --- a/tests/EndpointBlocksTest.php +++ b/tests/EndpointBlocksTest.php @@ -340,5 +340,13 @@ public function it_retrieves_a_single_block() $this->assertInstanceOf(Block::class, $block); $this->assertInstanceOf(Paragraph::class, $block); + $this->assertEquals('a6f8ebe8-d5df-4ffa-b543-bcd54d1c3bad', $block->getId()); + $this->assertEquals('paragraph', $block->getType()); + $this->assertEquals('This is a paragraph test', $block->getContent()->getPlainText()); + $this->assertEquals('block', $block->getObjectType()); + + $this->assertEquals('page_id', $block->getParentType()); + $this->assertEquals('f2939732-f694-4ce2-b613-f28db6ded673', $block->getParentId()); + $this->assertTrue($block->isArchived()); } } diff --git a/tests/EndpointDatabasesTest.php b/tests/EndpointDatabasesTest.php index 12502e0..42f3891 100644 --- a/tests/EndpointDatabasesTest.php +++ b/tests/EndpointDatabasesTest.php @@ -4,6 +4,7 @@ use Carbon\Carbon; use FiveamCode\LaravelNotionApi\Entities\Database; +use FiveamCode\LaravelNotionApi\Entities\PropertyItems\RichText; use FiveamCode\LaravelNotionApi\Exceptions\NotionException; use Illuminate\Support\Facades\Http; use Notion; @@ -90,13 +91,23 @@ public function it_returns_database_entity_with_filled_properties() // check properties $this->assertSame('Grocery List', $databaseResult->getTitle()); + $this->assertSame('Grocery List Description', $databaseResult->getDescription()); $this->assertSame('database', $databaseResult->getObjectType()); - - $this->assertCount(1, $databaseResult->getRawTitle()); + $this->assertSame('668d797c-76fa-4934-9b05-ad288df2d136', $databaseResult->getId()); + $this->assertTrue($databaseResult->isInline()); + $this->assertTrue($databaseResult->isArchived()); + + $this->assertInstanceOf(RichText::class, $databaseResult->getRichTitle()); + $this->assertInstanceOf(RichText::class, $databaseResult->getRichDescription()); + $this->assertCount(1, $databaseResult->getRichTitle()->getRawResponse()); + $this->assertCount(1, $databaseResult->getRichDescription()->getRawResponse()); $this->assertCount(12, $databaseResult->getRawProperties()); $this->assertInstanceOf(Carbon::class, $databaseResult->getCreatedTime()); $this->assertInstanceOf(Carbon::class, $databaseResult->getLastEditedTime()); + + $this->assertEquals('page_id', $databaseResult->getParentType()); + $this->assertEquals('f2939732-f694-4ce2-b613-f28db6ded673', $databaseResult->getParentId()); } /** @test */ diff --git a/tests/EndpointPagesTest.php b/tests/EndpointPagesTest.php index d0c3323..f8608b2 100644 --- a/tests/EndpointPagesTest.php +++ b/tests/EndpointPagesTest.php @@ -75,6 +75,7 @@ public function it_returns_page_entity_with_filled_properties() $this->assertCount(9, $pageResult->getPropertyKeys()); $this->assertSame('database_id', $pageResult->getParentType()); $this->assertSame('f2939732-f694-4ce2-b613-f28db6ded673', $pageResult->getParentId()); + $this->assertTrue($pageResult->isArchived()); // check date and datetime properties $this->assertTrue($pageResult->getProperty('DateWithTime')->hasTime()); diff --git a/tests/EndpointUsersTest.php b/tests/EndpointUsersTest.php index e9b17af..25e56c1 100644 --- a/tests/EndpointUsersTest.php +++ b/tests/EndpointUsersTest.php @@ -80,6 +80,7 @@ public function it_returns_a_specific_user_as_entity_object() $this->assertInstanceOf(User::class, $user); $this->assertEquals('Avocado Lovelace', $user->getName()); + $this->assertEquals('user', $user->getObjectType()); $this->assertEquals('https://secure.notion-static.com/e6a352a8-8381-44d0-a1dc-9ed80e62b53d.jpg', $user->getAvatarUrl()); } diff --git a/tests/stubs/endpoints/blocks/response_specific_block_200.json b/tests/stubs/endpoints/blocks/response_specific_block_200.json index 1b2a15f..de5336b 100644 --- a/tests/stubs/endpoints/blocks/response_specific_block_200.json +++ b/tests/stubs/endpoints/blocks/response_specific_block_200.json @@ -4,14 +4,18 @@ "created_time": "2021-05-17T13:51:00.000Z", "last_edited_time": "2021-06-10T17:40:00.000Z", "has_children": false, - "archived": false, + "archived": true, "type": "paragraph", + "parent": { + "type": "page_id", + "page_id": "f2939732-f694-4ce2-b613-f28db6ded673" + }, "paragraph": { "text": [ { "type": "text", "text": { - "content": "C:\\xampp\\php", + "content": "This is a paragraph test", "link": null }, "annotations": { @@ -22,7 +26,7 @@ "code": false, "color": "default" }, - "plain_text": "C:\\xampp\\php", + "plain_text": "This is a paragraph test", "href": null } ] diff --git a/tests/stubs/endpoints/databases/response_specific_200.json b/tests/stubs/endpoints/databases/response_specific_200.json index a900324..af8ae96 100644 --- a/tests/stubs/endpoints/databases/response_specific_200.json +++ b/tests/stubs/endpoints/databases/response_specific_200.json @@ -3,6 +3,31 @@ "id": "668d797c-76fa-4934-9b05-ad288df2d136", "created_time": "2020-03-17T19:10:04.968Z", "last_edited_time": "2020-03-17T21:49:37.913Z", + "parent": { + "type": "page_id", + "page_id": "f2939732-f694-4ce2-b613-f28db6ded673" + }, + "is_inline": true, + "archived": true, + "description": [ + { + "type": "text", + "text": { + "content": "Grocery List Description", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Grocery List Description", + "href": null + } + ], "title": [ { "type": "text", @@ -103,29 +128,28 @@ "id": "Z\\Eh", "type": "multi_select", "multi_select": { - "options": - [ - { - "id": "d209b920-212c-4040-9d4a-bdf349dd8b2a", - "name": "Duc Loi Market", - "color": "blue" - }, - { - "id": "70104074-0f91-467b-9787-00d59e6e1e41", - "name": "Rainbow Grocery", - "color": "gray" - }, - { - "id": "e6fd4f04-894d-4fa7-8d8b-e92d08ebb604", - "name": "Nijiya Market", - "color": "purple" - }, - { - "id": "6c3867c5-d542-4f84-b6e9-a420c43094e7", - "name": "Gus's Community Market", - "color": "yellow" - } - ] + "options": [ + { + "id": "d209b920-212c-4040-9d4a-bdf349dd8b2a", + "name": "Duc Loi Market", + "color": "blue" + }, + { + "id": "70104074-0f91-467b-9787-00d59e6e1e41", + "name": "Rainbow Grocery", + "color": "gray" + }, + { + "id": "e6fd4f04-894d-4fa7-8d8b-e92d08ebb604", + "name": "Nijiya Market", + "color": "purple" + }, + { + "id": "6c3867c5-d542-4f84-b6e9-a420c43094e7", + "name": "Gus's Community Market", + "color": "yellow" + } + ] } }, "+1": { diff --git a/tests/stubs/endpoints/pages/response_specific_200.json b/tests/stubs/endpoints/pages/response_specific_200.json index 268e117..b34e012 100644 --- a/tests/stubs/endpoints/pages/response_specific_200.json +++ b/tests/stubs/endpoints/pages/response_specific_200.json @@ -7,7 +7,7 @@ "type": "database_id", "database_id": "f2939732-f694-4ce2-b613-f28db6ded673" }, - "archived": false, + "archived": true, "properties": { "NumberProp": { "id": ">d{N",