diff --git a/.gitignore b/.gitignore index 903b824..bfab0ce 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ vendor .phpunit.result.cache coverage/ .phpunit.cache/ -.env* \ No newline at end of file +.env* +coverage-report \ No newline at end of file diff --git a/composer.json b/composer.json index 8e23c63..4c8c627 100644 --- a/composer.json +++ b/composer.json @@ -49,10 +49,13 @@ }, "scripts": { "test": "vendor/bin/pest", - "test-coverage": "vendor/bin/pest --coverage-html coverage" + "test-coverage": "phpdbg -qrr ./vendor/bin/pest --coverage-html ./coverage-report" }, "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true + } }, "extra": { "laravel": { diff --git a/src/Builder/DatabaseBuilder.php b/src/Builder/DatabaseBuilder.php new file mode 100644 index 0000000..434f703 --- /dev/null +++ b/src/Builder/DatabaseBuilder.php @@ -0,0 +1,239 @@ +payload = [ + 'is_inline' => false, + 'parent' => [], + 'title' => [ + [ + 'text' => [ + 'content' => '', + ], + ], + ], + 'properties' => [], + ]; + } + + /** + * Creates database within given page. + * + * @param string $pageId + * @return Database + */ + public function createInPage($pageId): Database + { + $this->payload['parent'] = [ + 'type' => 'page_id', + 'page_id' => $pageId, + ]; + + if ($this->payload['properties'] === []) { + $this->addTitle(); + } + + return $this->databasesEndpoint->create($this->payload()); + } + + /** + * Sets the title for the database creation. + * + * @param string $title + * @return DatabaseBuilder + */ + public function title(string $title): DatabaseBuilder + { + $this->payload['title'] = [ + [ + 'text' => [ + 'content' => $title, + ], + ], + ]; + + return $this; + } + + /** + * Sets the description for the database creation. + * + * @param string $description + * @return DatabaseBuilder + */ + public function description(string $description): DatabaseBuilder + { + $this->payload['description'] = [ + [ + 'text' => [ + 'content' => $description, + ], + ], + ]; + + return $this; + } + + /** + * Sets the created database as inline (currently not supported). + * + * @todo increase Notion API Version, to make this work + * + * @return DatabaseBuilder + */ + public function inline(): DatabaseBuilder + { + $this->payload['is_inline'] = true; + + return $this; + } + + /** + * Sets the icon for the database creation. + * + * @param string $icon + * @return DatabaseBuilder + */ + public function iconEmoji(string $icon): DatabaseBuilder + { + $this->payload['icon'] = [ + 'type' => 'emoji', + 'emoji' => $icon, + ]; + + return $this; + } + + /** + * Sets the icon for the database creation. + * + * @param string $url + * @return DatabaseBuilder + */ + public function iconExternal(string $url): DatabaseBuilder + { + $this->payload['icon'] = [ + 'type' => 'external', + 'external' => [ + 'url' => $url, + ], + ]; + + return $this; + } + + /** + * Sets the cover for the database creation. + * + * @param string $url + * @return DatabaseBuilder + */ + public function coverExternal(string $url): DatabaseBuilder + { + $this->payload['cover'] = [ + 'type' => 'external', + 'external' => [ + 'url' => $url, + ], + ]; + + return $this; + } + + /** + * Adds the property `title` database creation. + * + * @param string $name + * @return DatabaseBuilder + */ + public function addTitle(string $name = 'Name') + { + $this->add(PropertyBuilder::title($name)); + + return $this; + } + + /** + * Adds one or multiple properties to the database creation. + * + * @param PropertyBuilder|Collection|DatabaseSchemeBuilder $properties + * @return DatabaseBuilder + */ + public function add(PropertyBuilder|Collection|DatabaseSchemeBuilder $properties): DatabaseBuilder + { + if ($properties instanceof PropertyBuilder) { + $properties = collect([$properties]); + } + + if ($properties instanceof DatabaseSchemeBuilder) { + $properties = $properties->getProperties(); + } + + $properties->each(function (PropertyBuilder $property) { + $this->payload['properties'][$property->getName()] = $property->payload(); + }); + + return $this; + } + + /** + * Adds multiple properties to the database creation, similar to a Laravel migration. + * + * @param callable $callback + * @return DatabaseBuilder + */ + public function scheme(callable $callback): DatabaseBuilder + { + $builder = new DatabaseSchemeBuilder(); + $callback($builder); + + return $this->add($builder); + } + + /** + * Adds a raw property to the database creation. + * + * @param string $title + * @param string $propertyType + * @param array|null $content + * @return DatabaseBuilder + */ + public function addRaw(string $title, string $propertyType, array $content = null): DatabaseBuilder + { + $this->payload['properties'][$title] = []; + $this->payload['properties'][$title][$propertyType] = $content ?? new \stdClass(); + + return $this; + } + + /** + * Returns the payload for the database creation. + * + * @return array + */ + public function payload(): array + { + return $this->payload; + } +} diff --git a/src/Builder/DatabaseSchemeBuilder.php b/src/Builder/DatabaseSchemeBuilder.php new file mode 100644 index 0000000..ede2ff3 --- /dev/null +++ b/src/Builder/DatabaseSchemeBuilder.php @@ -0,0 +1,285 @@ +properties = collect(); + } + + /** + * @param PropertyBuilder $builder + * @return DatabaseSchemeBuilder + */ + public function push(PropertyBuilder $builder): DatabaseSchemeBuilder + { + $this->properties->push($builder); + + return $this; + } + + /** + * Add raw property to the database scheme. + * Please reference the Notion API documentation for the content array/object structure. + * + * @param string $name + * @param string $type + * @param array|object $content + * @return DatabaseSchemeBuilder + */ + public function raw(string $name, string $type, array|object $content): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::raw($name, $type, $content)); + } + + /** + * Add plain property to the database scheme. + * For simply adding properties, without required content. + * + * @param string $name + * @param string $type + * @return DatabaseSchemeBuilder + */ + public function plain(string $name, string $type): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::plain($name, $type)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function title(string $name = 'Name'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::title($name)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function richText(string $name = 'Text'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::richText($name)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function checkbox(string $name = 'Checkbox'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::checkbox($name)); + } + + /** + * (currently not supported). + * + * @todo increase Notion API Version, to make this work + * + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function status(string $name = 'Status'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::status($name)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function select(string $name, array $options): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::select($name, $options)); + } + + /** + * @param string $name + * @param array $options + * @return DatabaseSchemeBuilder + */ + public function multiSelect(string $name, array $options): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::multiSelect($name, $options)); + } + + /** + * @param string $name + * @param string $format + * @return DatabaseSchemeBuilder + */ + public function number(string $name = 'Number', string $format = 'number'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::number($name, $format)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function date(string $name = 'Date'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::date($name)); + } + + /** + * @param string $name + * @param string $databaseId + * @return DatabaseSchemeBuilder + */ + public function relation(string $name, string $databaseId): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::relation($name, $databaseId)); + } + + /** + * @param string $name + * @param string $expression + * @return DatabaseSchemeBuilder + */ + public function formula(string $name, string $expression): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::formula($name, $expression)); + } + + /** + * @param string $name + * @param string $rollupProperty + * @param string $relationProperty + * @param string $function + * @return DatabaseSchemeBuilder + */ + public function rollup(string $name, string $rollupProperty, string $relationProperty, string $function): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::rollup($name, $rollupProperty, $relationProperty, $function)); + } + + /** + * @param string $name + * @param string $rollupPropertyName + * @param string $relationPropertyName + * @param string $function + * @return DatabaseSchemeBuilder + */ + public function rollupByName(string $name, string $rollupPropertyName, string $relationPropertyName, string $function): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::rollupByName($name, $rollupPropertyName, $relationPropertyName, $function)); + } + + /** + * @param string $name + * @param string $rollupPropertyId + * @param string $relationPropertyId + * @param string $function + * @return DatabaseSchemeBuilder + */ + public function rollupById(string $name, string $rollupPropertyId, string $relationPropertyId, string $function): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::rollupById($name, $rollupPropertyId, $relationPropertyId, $function)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function url(string $name = 'Url'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::url($name)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function email(string $name = 'Email'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::email($name)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function phoneNumber(string $name = 'Phone Number'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::phoneNumber($name)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function people(string $name = 'People'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::people($name)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function files(string $name = 'Files'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::files($name)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function createdBy(string $name = 'Created By'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::createdBy($name)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function createdTime(string $name = 'Created Time'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::createdTime($name)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function lastEditedBy(string $name = 'Last Edited Time'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::lastEditedBy($name)); + } + + /** + * @param string $name + * @return DatabaseSchemeBuilder + */ + public function lastEditedTime(string $name = 'Last Edited Time'): DatabaseSchemeBuilder + { + return $this->push(PropertyBuilder::lastEditedTime($name)); + } + + /** + * @return Collection + */ + public function getProperties(): Collection + { + return $this->properties; + } +} diff --git a/src/Builder/PropertyBuilder.php b/src/Builder/PropertyBuilder.php new file mode 100644 index 0000000..793ac87 --- /dev/null +++ b/src/Builder/PropertyBuilder.php @@ -0,0 +1,316 @@ + $type, + $type => $content, + ]); + } + + /** + * Add plain property to the database scheme. + * For simply adding properties, without required content. + * + * @param string $name + * @param string $type + * @return PropertyBuilder + */ + public static function plain(string $name, string $type): PropertyBuilder + { + return self::raw($name, $type, new \stdClass()); + } + + /** + * @param string $name + * @return PropertyBuilder + */ + public static function title(string $name = 'Name'): PropertyBuilder + { + return self::plain($name, Property::TITLE); + } + + /** + * @param string $name + * @return PropertyBuilder + */ + public static function richText(string $name = 'Text'): PropertyBuilder + { + return self::plain($name, Property::RICH_TEXT); + } + + /** + * @param string $name + * @return PropertyBuilder + */ + public static function checkbox(string $name = 'Checkbox'): PropertyBuilder + { + return self::plain($name, Property::CHECKBOX); + } + + /** + * (currently not supported). + * + * @todo increase Notion API Version, to make this work + * + * @param string $name + * @return PropertyBuilder + */ + public static function status(string $name = 'Status'): PropertyBuilder + { + return self::plain($name, Property::STATUS); + } + + /** + * @param string $name + * @param array $options + * @return PropertyBuilder + */ + public static function select(string $name, array $options): PropertyBuilder + { + return self::raw($name, Property::SELECT, [ + 'options' => $options, + ]); + } + + /** + * @param string $name + * @param array $options + * @return PropertyBuilder + */ + public static function multiSelect(string $name, array $options): PropertyBuilder + { + return self::raw($name, Property::MULTI_SELECT, [ + 'options' => $options, + ]); + } + + /** + * @param string $name + * @param string $format + * @return PropertyBuilder + */ + public static function number(string $name = 'Number', $format = 'number'): PropertyBuilder + { + return self::raw($name, Property::NUMBER, [ + 'format' => $format, + ]); + } + + /** + * @param string $name + * @return PropertyBuilder + */ + public static function date(string $name = 'Date'): PropertyBuilder + { + return self::plain($name, Property::DATE); + } + + /** + * @param string $name + * @param string $databaseId + * @return PropertyBuilder + */ + public static function relation(string $name, string $databaseId): PropertyBuilder + { + return self::raw($name, Property::RELATION, [ + 'database_id' => $databaseId, + ]); + } + + /** + * @param string $name + * @param string $expression + * @return PropertyBuilder + */ + public static function formula(string $name, string $expression) + { + return self::raw($name, Property::FORMULA, [ + 'expression' => $expression, + ]); + } + + /** + * @param string $name + * @param string $rollupProperty + * @param string $relationProperty + * @param string $function + * @return PropertyBuilder + */ + public static function rollup(string $name, string $rollupProperty, string $relationProperty, string $function): PropertyBuilder + { + return self::rollupByName($name, $rollupProperty, $relationProperty, $function); + } + + /** + * @param string $name + * @param string $rollupPropertyName + * @param string $relationPropertyName + * @param string $function + * @return PropertyBuilder + */ + public static function rollupByName(string $name, string $rollupPropertyName, string $relationPropertyName, string $function): PropertyBuilder + { + return self::raw($name, Property::ROLLUP, [ + 'relation_property_name' => $relationPropertyName, + 'rollup_property_name' => $rollupPropertyName, + 'function' => $function, + ]); + } + + /** + * @param string $name + * @param string $rollupPropertyId + * @param string $relationPropertyId + * @param string $function + * @return PropertyBuilder + */ + public static function rollupById(string $name, string $rollupPropertyId, string $relationPropertyId, string $function): PropertyBuilder + { + return self::raw($name, Property::ROLLUP, [ + 'relation_property_id' => $relationPropertyId, + 'rollup_property_id' => $rollupPropertyId, + 'function' => $function, + ]); + } + + /** + * @param string $name + * @return PropertyBuilder + */ + public static function url(string $name = 'Url'): PropertyBuilder + { + return self::plain($name, Property::URL); + } + + /** + * @param string $name + * @return PropertyBuilder + */ + public static function email(string $name = 'Email'): PropertyBuilder + { + return self::plain($name, Property::EMAIL); + } + + /** + * @param string $name + * @return PropertyBuilder + */ + public static function phoneNumber(string $name = 'Phone Number'): PropertyBuilder + { + return self::plain($name, Property::PHONE_NUMBER); + } + + /** + * @param string $name + * @return PropertyBuilder + */ + public static function people(string $name = 'People'): PropertyBuilder + { + return self::plain($name, Property::PEOPLE); + } + + /** + * @param string $name + * @return PropertyBuilder + */ + public static function files(string $name = 'Files'): PropertyBuilder + { + return self::plain($name, Property::FILES); + } + + /** + * @param string $name + * @return PropertyBuilder + */ + public static function createdBy(string $name = 'Created By'): PropertyBuilder + { + return self::plain($name, Property::CREATED_BY); + } + + /** + * @param string $name + * @return PropertyBuilder + */ + public static function createdTime(string $name = 'Created Time'): PropertyBuilder + { + return self::plain($name, Property::CREATED_TIME); + } + + /** + * @param string $name + * @return PropertyBuilder + */ + public static function lastEditedBy(string $name = 'Last Edited By'): PropertyBuilder + { + return self::plain($name, Property::LAST_EDITED_BY); + } + + /** + * @param string $name + * @return PropertyBuilder + */ + public static function lastEditedTime(string $name = 'Last Edited Time'): PropertyBuilder + { + return self::plain($name, Property::LAST_EDITED_TIME); + } + + /** + * @param string $name + * @param array $payload + */ + private function __construct(private string $name, private array $payload) + { + } + + /** + * @return string + * + * @throws HandlingException + */ + public function getName(): string + { + if ($this->name == '') { + throw new HandlingException('Properties must have a name. No name given for the property structure:'.json_encode($this->payload)); + } + + return $this->name; + } + + /** + * @return array + */ + public function payload(): array + { + return $this->payload; + } +} diff --git a/src/Console/Commands/MakeNotionModel.php b/src/Console/Commands/MakeNotionModel.php new file mode 100644 index 0000000..027fa08 --- /dev/null +++ b/src/Console/Commands/MakeNotionModel.php @@ -0,0 +1,77 @@ +argument('database_id'); + $databaseName = $this->argument('database_name'); + + $notion = new Notion(config('laravel-notion-api.notion-api-token')); + + $this->info("Fetching structure of {$databaseId} database from Notion..."); + + $databaseStruct = $notion->databases()->find($databaseId); + $phpDocsProperties = ''; + $visibleArray = ''; + $propertyTypeMape = ''; + $propertyTitleMap = ''; + + if ($databaseName === null) { + $databaseName = Str::singular(Str::studly(Str::slug($databaseStruct->getTitle(), '_'))); + } + + foreach ($databaseStruct->getProperties() as $propertyInfo) { + $reflection = new \ReflectionMethod($propertyInfo, 'getContent'); + $propType = $reflection->getReturnType() ?? 'mixed'; + $notionPropType = Str::studly(Str::slug($propertyInfo->getType(), '_')); + + if ($reflection->getReturnType() !== null && ! $propType->isBuiltin()) { + $propType = '\\'.$propType->getName(); + } + + $propName = Str::slug($propertyInfo->getTitle(), '_'); + $phpDocsProperties .= " * @property {$propType} \${$propName} $notionPropType \n"; + $visibleArray .= " '$propName',\n"; + $propertyTypeMape .= " '$propName' => '$notionPropType',\n"; + $propertyTitleMap .= " '$propName' => '{$propertyInfo->getTitle()}',\n"; + } + + $contents = 'info("Model for database {$this->argument('database_id')} has been created at 'app/NotionModels/$databaseName.php' ."); + + return 0; + } +} diff --git a/src/Console/Commands/stub/notion-model.blade.php b/src/Console/Commands/stub/notion-model.blade.php new file mode 100644 index 0000000..57cc7ac --- /dev/null +++ b/src/Console/Commands/stub/notion-model.blade.php @@ -0,0 +1,24 @@ +namespace App\NotionModels; + +use FiveamCode\LaravelNotionApi\Models\NotionModel; + +/** +{{$phpDocsProperties}}*/ +class {{$databaseName}} extends NotionModel +{ + public static $databaseId = '{{$databaseId}}'; + + public static $cacheDurationInSeconds = 0; + + public static $convertPropsToText = false; + + public static $visible = [ +{{$visibleArray}} ]; + + public static $propertyTypeMap = [ +{{$propertyTypeMape}} ]; + + public static $propertyTitleMap = [ +{{$propertyTitleMap}} ]; + +} \ No newline at end of file diff --git a/src/Endpoints/Comments.php b/src/Endpoints/Comments.php index f220c66..5d8d64f 100644 --- a/src/Endpoints/Comments.php +++ b/src/Endpoints/Comments.php @@ -38,14 +38,15 @@ public function __construct(Notion $notion) } /** - * Retrieve a list of comments - * url: https://api.notion.com/{version}/comments?block_id=* [get] - * notion-api-docs: https://developers.notion.com/reference/retrieve-a-comment. + * Retrieve a list of comments. + * + * @url https://api.notion.com/{version}/comments?block_id=* [get] + * + * @reference https://developers.notion.com/reference/retrieve-a-comment. * * @param string $blockId * @return CommentCollection * - * @throws HandlingException * @throws NotionException */ public function ofBlock(string $blockId): CommentCollection @@ -88,14 +89,15 @@ public function onPage(string $pageId): self } /** - * Create a comment - * url: https://api.notion.com/{version}/comments [post] - * notion-api-docs: https://developers.notion.com/reference/create-a-comment. + * Create a comment. + * + * @url https://api.notion.com/{version}/comments [post] + * + * @reference https://developers.notion.com/reference/create-a-comment. * * @param CommentEntity $comment * @return CommentEntity * - * @throws HandlingException * @throws NotionException */ public function create($comment): CommentEntity diff --git a/src/Endpoints/Database.php b/src/Endpoints/Database.php index 84eb58d..3c53695 100644 --- a/src/Endpoints/Database.php +++ b/src/Endpoints/Database.php @@ -98,7 +98,7 @@ public function getPostData(): array } /** - * @param $filter + * @param $filter * @return Database $this * * @throws HandlingException diff --git a/src/Endpoints/Databases.php b/src/Endpoints/Databases.php index 80381cf..fa39d38 100644 --- a/src/Endpoints/Databases.php +++ b/src/Endpoints/Databases.php @@ -2,6 +2,7 @@ namespace FiveamCode\LaravelNotionApi\Endpoints; +use FiveamCode\LaravelNotionApi\Builder\DatabaseBuilder; use FiveamCode\LaravelNotionApi\Entities\Collections\DatabaseCollection; use FiveamCode\LaravelNotionApi\Entities\Database; use FiveamCode\LaravelNotionApi\Exceptions\HandlingException; @@ -16,9 +17,11 @@ class Databases extends Endpoint implements EndpointInterface { /** - * List databases - * url: https://api.notion.com/{version}/databases - * notion-api-docs: https://developers.notion.com/reference/get-databases. + * List databases. + * + * @url https://api.notion.com/{version}/databases + * + * @reference https://developers.notion.com/reference/get-databases. * * @return DatabaseCollection * @@ -35,9 +38,11 @@ public function all(): DatabaseCollection } /** - * Retrieve a database - * url: https://api.notion.com/{version}/databases/{database_id} - * notion-api-docs: https://developers.notion.com/reference/retrieve-a-database. + * Retrieve a database. + * + * @url https://api.notion.com/{version}/databases/{database_id} + * + * @reference https://developers.notion.com/reference/retrieve-a-database. * * @param string $databaseId * @return Database @@ -52,4 +57,37 @@ public function find(string $databaseId): Database return new Database($result); } + + /** + * Returns a `DatabaseBuilder`reference, which helps building + * the scheme and information for creation a database. + * + * @return DatabaseBuilder + */ + public function build() + { + return new DatabaseBuilder($this); + } + + /** + * Create a database + * Recommendation: use `build()` to eloquently create databases. + * + * @url https://api.notion.com/{version}/databases (post) + * + * @reference https://developers.notion.com/reference/create-a-database. + * + * @param array $payload + * @return Database + * + * @throws HandlingException + * @throws NotionException + */ + public function create(array $payload): Database + { + $result = $this + ->post($this->url(Endpoint::DATABASES), $payload); + + return new Database($result->json()); + } } diff --git a/src/Endpoints/Resolve.php b/src/Endpoints/Resolve.php new file mode 100644 index 0000000..3019bd9 --- /dev/null +++ b/src/Endpoints/Resolve.php @@ -0,0 +1,120 @@ +notion->users()->find($user->getId()); + } + + /** + * Resolve Parent of an entity. + * + * @param Entity $entity + * @return Page|Database|Block + * + * @throws HandlingException + * @throws NotionException + */ + public function parentOf(Entity $entity) + { + if (! in_array(HasParent::class, class_uses_recursive(get_class($entity)))) { + throw new HandlingException("The given entity '{$entity->getObjectType()}' does not have a parent."); + } + + return $this->parent($entity->getParent()); + } + + /** + * Resolve Parent. + * + * @param NotionParent $parent + * @return Page|Database|Block + * + * @throws HandlingException + * @throws NotionException + */ + public function parent(NotionParent $parent): Page|Database|Block|NotionParent + { + switch ($parent->getObjectType()) { + case 'page_id': + return $this->notion->pages()->find($parent->getId()); + case 'database_id': + return $this->notion->databases()->find($parent->getId()); + case 'block_id': + return $this->notion->block($parent->getId())->retrieve(); + case 'workspace': + return $parent; + // throw new HandlingException('A Notion Workspace cannot be resolved by the Notion API.'); + default: + throw new HandlingException('Unknown parent type while resolving the notion parent'); + } + } + + /** + * Resolve Relations. + * + * @param Relation $relation + * @return Collection + * + * @throws HandlingException + * @throws NotionException + */ + public function relations(Relation $relation, bool $onlyTitles = false): Collection + { + $pages = collect(); + $relationIds = $relation->getRelation()->map(function ($o) { + return $o['id']; + }); + + foreach ($relationIds as $relationId) { + if ($onlyTitles) { + $pages->add($this->notion->pages()->find($relationId)->getTitle()); + } else { + $pages->add($this->notion->pages()->find($relationId)); + } + } + + return $pages; + } +} diff --git a/src/Entities/Blocks/Block.php b/src/Entities/Blocks/Block.php index 507ef60..c206a6a 100644 --- a/src/Entities/Blocks/Block.php +++ b/src/Entities/Blocks/Block.php @@ -142,7 +142,7 @@ public function setRawContent($rawContent) } /** - * @param $rawContent + * @param $rawContent * @return Block * * @throws HandlingException diff --git a/src/Entities/Database.php b/src/Entities/Database.php index 9df0eee..f959d86 100644 --- a/src/Entities/Database.php +++ b/src/Entities/Database.php @@ -8,6 +8,7 @@ use FiveamCode\LaravelNotionApi\Traits\HasArchive; use FiveamCode\LaravelNotionApi\Traits\HasParent; use FiveamCode\LaravelNotionApi\Traits\HasTimestamps; +use FiveamCode\LaravelNotionApi\Traits\HasTitle; use Illuminate\Support\Arr; use Illuminate\Support\Collection; @@ -16,12 +17,7 @@ */ class Database extends Entity { - use HasTimestamps, HasArchive, HasParent; - - /** - * @var string - */ - protected string $title = ''; + use HasTimestamps, HasArchive, HasParent, HasTitle; /** * @var string @@ -53,11 +49,6 @@ class Database extends Entity */ private string $url; - /** - * @var ?RichText - */ - protected ?RichText $richTitle = null; - /** * @var ?RichText */ @@ -102,21 +93,12 @@ private function fillFromRaw() parent::fillEssentials(); $this->fillIcon(); $this->fillCover(); - $this->fillTitle(); $this->fillIsInline(); $this->fillDescription(); $this->fillProperties(); $this->fillDatabaseUrl(); } - 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->richTitle = new RichText($this->responseData['title']); - } - } - private function fillIsInline(): void { if (Arr::exists($this->responseData, 'is_inline')) { @@ -193,14 +175,6 @@ public function getProperty(string $propertyKey): ?Property return $this->propertyMap[$propertyKey]; } - /** - * @return string - */ - public function getTitle(): string - { - return $this->title; - } - /** * @return bool */ diff --git a/src/Entities/Entity.php b/src/Entities/Entity.php index b23671f..705b716 100644 --- a/src/Entities/Entity.php +++ b/src/Entities/Entity.php @@ -91,6 +91,9 @@ private function fillTraitAttributes(): void 'FiveamCode\LaravelNotionApi\Traits\HasArchive' => function ($entity) { $entity->fillArchivedAttributes(); }, + 'FiveamCode\LaravelNotionApi\Traits\HasTitle' => function ($entity) { + $entity->fillTitleAttributes(); + }, ]; $traits = $this->class_uses_deep($this); diff --git a/src/Entities/NotionParent.php b/src/Entities/NotionParent.php new file mode 100644 index 0000000..a3b861e --- /dev/null +++ b/src/Entities/NotionParent.php @@ -0,0 +1,70 @@ +fillFromRaw(); + } + + private function fillFromRaw(): void + { + parent::fillEssentials(); + } + + /** + * @return bool + */ + public function isBlock(): bool + { + return $this->getObjectType() === 'block_id'; + } + + /** + * @return bool + */ + public function isPage(): bool + { + return $this->getObjectType() === 'page_id'; + } + + /** + * @return bool + */ + public function isDatabase(): bool + { + return $this->getObjectType() === 'database_id'; + } + + /** + * @return bool + */ + public function isWorkspace(): bool + { + return $this->getObjectType() === 'workspace'; + } +} diff --git a/src/Entities/Page.php b/src/Entities/Page.php index c857162..63b20c3 100644 --- a/src/Entities/Page.php +++ b/src/Entities/Page.php @@ -113,7 +113,7 @@ private function fillFromRaw(): void { parent::fillEssentials(); $this->fillProperties(); - $this->fillTitle(); // This has to be called after fillProperties(), since title is provided by properties + $this->fillTitle(); // This has to be called after fillProperties(), since title is provided by properties (hence this is not a trait!) $this->fillPageUrl(); $this->fillIcon(); $this->fillCover(); @@ -186,8 +186,8 @@ private function fillPageUrl(): void } /** - * @param $propertyTitle - * @param $property + * @param $propertyTitle + * @param $property * @return Page */ public function set(string $propertyKey, Property $property): Page @@ -204,8 +204,8 @@ public function set(string $propertyKey, Property $property): Page } /** - * @param $propertyTitle - * @param $number + * @param $propertyTitle + * @param $number * @return Page */ public function setNumber(string $propertyTitle, float $number): Page @@ -216,8 +216,8 @@ public function setNumber(string $propertyTitle, float $number): Page } /** - * @param $propertyTitle - * @param $text + * @param $propertyTitle + * @param $text * @return Page */ public function setTitle(string $propertyTitle, string $text): Page @@ -228,8 +228,8 @@ public function setTitle(string $propertyTitle, string $text): Page } /** - * @param $propertyTitle - * @param $text + * @param $propertyTitle + * @param $text * @return Page */ public function setText(string $propertyTitle, string $text): Page @@ -240,8 +240,8 @@ public function setText(string $propertyTitle, string $text): Page } /** - * @param $propertyTitle - * @param $name + * @param $propertyTitle + * @param $name * @return Page */ public function setSelect(string $propertyTitle, string $name): Page @@ -252,8 +252,8 @@ public function setSelect(string $propertyTitle, string $name): Page } /** - * @param $propertyTitle - * @param $url + * @param $propertyTitle + * @param $url * @return Page */ public function setUrl(string $propertyTitle, string $url): Page @@ -264,8 +264,8 @@ public function setUrl(string $propertyTitle, string $url): Page } /** - * @param $propertyTitle - * @param $phoneNumber + * @param $propertyTitle + * @param $phoneNumber * @return Page */ public function setPhoneNumber(string $propertyTitle, string $phoneNumber): Page @@ -276,8 +276,8 @@ public function setPhoneNumber(string $propertyTitle, string $phoneNumber): Page } /** - * @param $propertyTitle - * @param $email + * @param $propertyTitle + * @param $email * @return Page */ public function setEmail(string $propertyTitle, string $email): Page @@ -288,8 +288,8 @@ public function setEmail(string $propertyTitle, string $email): Page } /** - * @param $propertyTitle - * @param $names + * @param $propertyTitle + * @param $names * @return Page */ public function setMultiSelect(string $propertyTitle, array $names): Page @@ -300,8 +300,8 @@ public function setMultiSelect(string $propertyTitle, array $names): Page } /** - * @param $propertyTitle - * @param $checked + * @param $propertyTitle + * @param $checked * @return Page */ public function setCheckbox(string $propertyTitle, bool $checked): Page @@ -312,9 +312,9 @@ public function setCheckbox(string $propertyTitle, bool $checked): Page } /** - * @param $propertyTitle - * @param $start - * @param $end + * @param $propertyTitle + * @param $start + * @param $end * @return Page */ public function setDate(string $propertyTitle, DateTime $start, ?DateTime $end = null): Page @@ -325,9 +325,9 @@ public function setDate(string $propertyTitle, DateTime $start, ?DateTime $end = } /** - * @param $propertyTitle - * @param $start - * @param $end + * @param $propertyTitle + * @param $start + * @param $end * @return Page */ public function setDateTime(string $propertyTitle, DateTime $start, ?DateTime $end = null): Page @@ -338,8 +338,8 @@ public function setDateTime(string $propertyTitle, DateTime $start, ?DateTime $e } /** - * @param $propertyTitle - * @param $relationIds + * @param $propertyTitle + * @param $relationIds * @return Page */ public function setRelation(string $propertyTitle, array $relationIds): Page @@ -350,8 +350,8 @@ public function setRelation(string $propertyTitle, array $relationIds): Page } /** - * @param $propertyTitle - * @param $userIds + * @param $propertyTitle + * @param $userIds * @return Page */ public function setPeople(string $propertyTitle, array $userIds): Page diff --git a/src/Entities/Properties/Checkbox.php b/src/Entities/Properties/Checkbox.php index d869694..8e43dab 100644 --- a/src/Entities/Properties/Checkbox.php +++ b/src/Entities/Properties/Checkbox.php @@ -11,7 +11,7 @@ class Checkbox extends Property implements Modifiable { /** - * @param $checked + * @param $checked * @return Checkbox */ public static function value(bool $checked): Checkbox diff --git a/src/Entities/Properties/Date.php b/src/Entities/Properties/Date.php index 332d1f2..3606185 100644 --- a/src/Entities/Properties/Date.php +++ b/src/Entities/Properties/Date.php @@ -14,8 +14,8 @@ class Date extends Property implements Modifiable { /** - * @param $start - * @param $end + * @param $start + * @param $end * @return Date */ public static function value(?DateTime $start, ?DateTime $end = null): Date @@ -46,8 +46,8 @@ public static function value(?DateTime $start, ?DateTime $end = null): Date } /** - * @param $start - * @param $end + * @param $start + * @param $end * @return Date */ public static function valueWithTime(?DateTime $start, ?DateTime $end = null): Date diff --git a/src/Entities/Properties/Email.php b/src/Entities/Properties/Email.php index 5ec63b4..fae7820 100644 --- a/src/Entities/Properties/Email.php +++ b/src/Entities/Properties/Email.php @@ -10,7 +10,7 @@ class Email extends Property implements Modifiable { /** - * @param $email + * @param $email * @return Email */ public static function value(string $email): Email diff --git a/src/Entities/Properties/MultiSelect.php b/src/Entities/Properties/MultiSelect.php index 4b8741c..7c97782 100644 --- a/src/Entities/Properties/MultiSelect.php +++ b/src/Entities/Properties/MultiSelect.php @@ -19,7 +19,7 @@ class MultiSelect extends Property implements Modifiable private Collection $options; /** - * @param $names + * @param $names * @return MultiSelect */ public static function value(array $names): MultiSelect diff --git a/src/Entities/Properties/People.php b/src/Entities/Properties/People.php index 674257f..12c57d0 100644 --- a/src/Entities/Properties/People.php +++ b/src/Entities/Properties/People.php @@ -13,7 +13,7 @@ class People extends Property implements Modifiable { /** - * @param $userIds + * @param $userIds * @return People */ public static function value(array $userIds): People diff --git a/src/Entities/Properties/PhoneNumber.php b/src/Entities/Properties/PhoneNumber.php index 5bc26fe..e36f644 100644 --- a/src/Entities/Properties/PhoneNumber.php +++ b/src/Entities/Properties/PhoneNumber.php @@ -10,7 +10,7 @@ class PhoneNumber extends Property implements Modifiable { /** - * @param $phoneNumber + * @param $phoneNumber * @return PhoneNumber */ public static function value(string $phoneNumber): PhoneNumber diff --git a/src/Entities/Properties/Property.php b/src/Entities/Properties/Property.php index e647f31..0eb0c9e 100644 --- a/src/Entities/Properties/Property.php +++ b/src/Entities/Properties/Property.php @@ -11,6 +11,27 @@ */ class Property extends Entity { + const TITLE = 'title'; + const RICH_TEXT = 'rich_text'; + const NUMBER = 'number'; + const STATUS = 'status'; + const SELECT = 'select'; + const MULTI_SELECT = 'multi_select'; + const DATE = 'date'; + const PEOPLE = 'people'; + const FILES = 'files'; + const CHECKBOX = 'checkbox'; + const URL = 'url'; + const EMAIL = 'email'; + const PHONE_NUMBER = 'phone_number'; + const FORMULA = 'formula'; + const RELATION = 'relation'; + const ROLLUP = 'rollup'; + const CREATED_TIME = 'created_time'; + const CREATED_BY = 'created_by'; + const LAST_EDITED_TIME = 'last_edited_time'; + const LAST_EDITED_BY = 'last_edited_by'; + /** * @var string */ @@ -136,7 +157,7 @@ public function getContent() /** * @param string $propertyKey - * @param $rawContent + * @param $rawContent * @return Property * * @throws HandlingException diff --git a/src/Entities/Properties/Relation.php b/src/Entities/Properties/Relation.php index 3fa8ca7..fb6d829 100644 --- a/src/Entities/Properties/Relation.php +++ b/src/Entities/Properties/Relation.php @@ -11,7 +11,7 @@ class Relation extends Property implements Modifiable { /** - * @param $relationIds + * @param $relationIds * @return Relation */ public static function value(array $relationIds): Relation diff --git a/src/Entities/Properties/Select.php b/src/Entities/Properties/Select.php index d950c1b..fae091d 100644 --- a/src/Entities/Properties/Select.php +++ b/src/Entities/Properties/Select.php @@ -18,7 +18,7 @@ class Select extends Property implements Modifiable private Collection $options; /** - * @param $name + * @param $name * @return Select */ public static function value(string $name): Select diff --git a/src/Entities/Properties/Text.php b/src/Entities/Properties/Text.php index 84df2e4..bfa0912 100644 --- a/src/Entities/Properties/Text.php +++ b/src/Entities/Properties/Text.php @@ -17,7 +17,7 @@ class Text extends Property implements Modifiable protected string $plainText = ''; /** - * @param $text + * @param $text * @return Text */ public static function value($text): Text diff --git a/src/Entities/Properties/Title.php b/src/Entities/Properties/Title.php index 5d7e502..d307017 100644 --- a/src/Entities/Properties/Title.php +++ b/src/Entities/Properties/Title.php @@ -17,7 +17,7 @@ class Title extends Property implements Modifiable protected string $plainText = ''; /** - * @param $text + * @param $text * @return Title */ public static function value($text): Title diff --git a/src/Entities/Properties/Url.php b/src/Entities/Properties/Url.php index 90210fe..ac5544e 100644 --- a/src/Entities/Properties/Url.php +++ b/src/Entities/Properties/Url.php @@ -10,7 +10,7 @@ class Url extends Property implements Modifiable { /** - * @param $url + * @param $url * @return Url */ public static function value(string $url): Url diff --git a/src/Entities/PropertyItems/SelectItem.php b/src/Entities/PropertyItems/SelectItem.php index 1cf7cb3..e248222 100644 --- a/src/Entities/PropertyItems/SelectItem.php +++ b/src/Entities/PropertyItems/SelectItem.php @@ -72,7 +72,7 @@ public function getName(): string } /** - * @param $color + * @param $color */ public function setColor($color): void { @@ -80,7 +80,7 @@ public function setColor($color): void } /** - * @param $name + * @param $name */ public function setName($name): void { diff --git a/src/LaravelNotionApiServiceProvider.php b/src/LaravelNotionApiServiceProvider.php index 75eabd8..1b88d0a 100644 --- a/src/LaravelNotionApiServiceProvider.php +++ b/src/LaravelNotionApiServiceProvider.php @@ -2,6 +2,7 @@ namespace FiveamCode\LaravelNotionApi; +use FiveamCode\LaravelNotionApi\Console\Commands\MakeNotionModel; use FiveamCode\LaravelNotionApi\Macros\PestHttpRecorder; use Illuminate\Support\ServiceProvider; @@ -19,6 +20,10 @@ public function boot() $this->publishes([ __DIR__.'/../config/config.php' => config_path('laravel-notion-api.php'), ], 'config'); + + $this->commands([ + MakeNotionModel::class, + ]); } } diff --git a/src/Macros/PestHttpRecorder.php b/src/Macros/PestHttpRecorder.php index 7d69c37..4d7842f 100644 --- a/src/Macros/PestHttpRecorder.php +++ b/src/Macros/PestHttpRecorder.php @@ -4,6 +4,7 @@ use GuzzleHttp\Client; use Illuminate\Http\Client\Request; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Http; use Illuminate\Support\Str; @@ -37,6 +38,8 @@ class HttpRecorder private $usePrettyJson = true; + private $requestNames = []; + public function storeIn($directory) { $this->snapshotDirectory = $directory; @@ -51,21 +54,32 @@ public function minifyJson() return $this; } + public function nameForNextRequest($name) + { + array_push($this->requestNames, $name); + } + public function handle(Request $request) { $forceRecording = in_array('--force-recording', $_SERVER['argv']); $urlInfo = parse_url($request->url()); + $payload = null; // create specific filename for storing snapshots + $header = $request->headers(); $method = Str::lower($request->method()); $name = Str::slug(Str::replace('/', '-', $urlInfo['path'])); - $query = Str::slug(Str::replace('&', '_', Str::replace('=', '-', $urlInfo['query']))); + $payload = ($method === 'get') ? ($urlInfo['query'] ?? null) : $request->body(); + $queryName = array_pop($this->requestNames) ?? hash('adler32', $payload); - $fileName = "{$method}_{$name}_{$query}.json"; + $fileName = "{$method}_{$name}_{$queryName}.json"; $directoryPath = "tests/{$this->snapshotDirectory}"; $filePath = "{$directoryPath}/{$fileName}"; + // filter out Notion API Token Header + $header = Arr::except($header, ['Authorization']); + if ($forceRecording || ! File::exists($filePath)) { File::makeDirectory($directoryPath, 0744, true, true); @@ -77,7 +91,10 @@ public function handle(Request $request) ]); $recordedResponse = [ + 'header' => $header, + 'method' => $method, 'status' => $response->getStatusCode(), + 'payload' => ($method === 'get') ? $payload : json_decode($payload, true), 'data' => json_decode($response->getBody()->getContents(), true), ]; diff --git a/src/Models/NotionModel.php b/src/Models/NotionModel.php new file mode 100644 index 0000000..96e9e0d --- /dev/null +++ b/src/Models/NotionModel.php @@ -0,0 +1,166 @@ + + */ + public static ?Collection $cursorHistory = null; + + /** + * @var string + */ + public static $databaseId = null; + + /** + * @var string + */ + public static $preCacheKey = 'laravel-notion-api.notion-model-'; + + /** + * @var int + */ + public static $cacheDurationInSeconds = 0; + + /** + * @var bool + */ + public static $convertPropsToText = false; + + public Page $page; + + public function __construct($databaseId = null) + { + if ($databaseId == null) { + $databaseId = static::$databaseId; + } + } + + public function getPage(): Page + { + return $this->page; + } + + /** + * @return array + */ + public function toArray(): array + { + $array = get_object_vars($this); + unset($array['page']); + + return $array; + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return $this->toArray(); + } + + public static function createInstance() + { + return new static(); + } + + /** + * @return string + */ + public static function databaseId(): string + { + if (static::$databaseId !== null) { + return static::$databaseId; + } + throw new \Exception('::getDatabaseId() or ::databaseId must be overridden'); + } + + /** + * @return + */ + public static function notionInstance(): Notion + { + return new Notion(config('laravel-notion-api.notion-api-token')); + } + + /** + * @return NotionQueryBuilder + */ + public static function query(): NotionQueryBuilder + { + return new NotionQueryBuilder(static::class); + } + + /** + * @return NotionQueryBuilder + */ + public static function where($property, $operator, $value = null) + { + return static::query()->where($property, $operator, $value); + } + + /** + * @return void + */ + public static function purge(): void + { + Cache::forget(static::cacheKey()); + } + + public static function all() + { + return self::query()->get(); + } + + /** + * @return ?static + */ + public static function first() + { + return static::query() + ->limit(1) + ->get() + ->first(); + } + + /** + * @return static + */ + public static function find($pageId): Page + { + $page = static::notionInstance() + ->pages() + ->find($pageId); + + if ($page === null) { + return null; + } + + if (str_replace('-', '', $page->getParentId()) !== str_replace('-', '', static::databaseId())) { + throw new HandlingException('Page is not within the corresponding Database'); + } + + return $page; + } + + public static function getNextCursor(): ?string + { + return self::$nextCursor; + } +} diff --git a/src/Models/NotionQueryBuilder.php b/src/Models/NotionQueryBuilder.php new file mode 100644 index 0000000..c06437a --- /dev/null +++ b/src/Models/NotionQueryBuilder.php @@ -0,0 +1,275 @@ +modelClass = $class; + $this->endpoint = $this->modelClass::notionInstance() + ->database($this->modelClass::$databaseId); + } + + public function pluck($value, $key = null): Collection + { + $pageCollection = $this->internalQuery(); + + return $pageCollection->pluck('props.'.$value, $key !== null ? 'props.'.$key : null); + } + + private function queryToNotion(int $limit = 100): PageCollection + { + $notionQuery = $this->modelClass::notionInstance() + ->database($this->modelClass::databaseId()) + ->limit($limit); + + if ($this->filters) { + $notionQuery->filterBy($this->filters); + } + + if ($this->modelClass::$offset) { + $notionQuery->offset(new StartCursor($this->modelClass::$offset)); + } + + return $notionQuery->query(); + } + + /** + * @return Collection + */ + private function internalQuery(int $limit = 100): Collection + { + if ($this->modelClass::$cursorHistory === null) { + $this->modelClass::$cursorHistory = new Collection(); + } + + if ($this->modelClass::$cacheDurationInSeconds === 0) { + $queryResponse = $this->queryToNotion($limit); + } else { + $queryResponse = Cache::remember(static::cacheKey(), $this->modelClass::$cacheDurationInSeconds, function () use ($limit) { + return $this->queryToNotion($limit); + }); + } + + $instances = collect(); + + foreach ($queryResponse->asCollection() as $pageItem) { + $instance = $this->modelClass::createInstance($this->modelClass::$databaseId); + $instance->page = $pageItem; + + foreach ($pageItem->getProperties() as $propertyItem) { + $propertyContent = $propertyItem->getContent(); + if ($this->modelClass::$convertPropsToText || $this->localConvertPropsToText) { + $propertyContent = $propertyItem->asText(); + } + $instance->{Str::slug($propertyItem->getTitle(), '_')} = $propertyContent; + } + + $instances->add($instance); + } + + self::$nextCursor = $queryResponse->nextCursor(); + $this->modelClass::$cursorHistory->add(self::$nextCursor); + + return $instances; + } + + /** + * @return Collection + */ + public function get() + { + return $this->internalQuery(); + } + + /** + * @return static + */ + public function first() + { + return $this->get()->first(); + } + + /** + * @return string + */ + public function getAsJson() + { + return $this->internalQuery()->asJson(); + } + + public function getAll() + { + throw new \Exception('Not implemented yet'); + } + + public function getAllAsJson() + { + throw new \Exception('Not implemented yet'); + } + + public function propsToText() + { + $this->localConvertPropsToText = true; + + return $this; + } + + public function offset($offset) + { + $this->endpoint->offset($offset); + + return $this; + } + + public function limit($offset) + { + $this->endpoint->limit($offset); + + return $this; + } + + public function orderBy($property, $direction = 'asc') + { + if ($this->sortings == null) { + $this->sortings = collect(); + } + + if ($direction == 'asc') { + $direction = 'ascending'; + } + if ($direction == 'desc') { + $direction = 'descending'; + } + + $this->sortings + ->add(Sorting::propertySort($property, $direction)); + + return $this; + } + + public function whereNull($property) + { + return $this->where($property, Operators::IS_EMPTY, null); + } + + public function whereNotNull($property) + { + return $this->where($property, Operators::IS_NOT_EMPTY, null); + } + + public function where($property, $operator, $value = null) + { + if ($this->filters == null) { + $this->filters = new FilterBag(); + } + + if ($value == null) { + $value = $operator; + $operator = Operators::EQUALS; + } else { + switch (Str::lower($operator)) { + case '=': + $operator = Operators::EQUALS; + break; + case '!=': + $operator = Operators::DOES_NOT_EQUAL; + break; + case '<': + $operator = Operators::LESS_THAN; + break; + case '<=': + $operator = Operators::LESS_THAN_OR_EQUAL_TO; + break; + case '>': + $operator = Operators::GREATER_THAN; + break; + case '>=': + $operator = Operators::GREATER_THAN_OR_EQUAL_TO; + break; + case 'contains': + $operator = Operators::CONTAINS; + break; + } + } + + if (Arr::has($this->modelClass::$propertyTitleMap, $property)) { + $property = $this->modelClass::$propertyTitleMap[$property]; + } + + if (is_string($value)) { + $this->filters->addFilter( + Filter::textFilter($property, $operator, $value) + ); + } elseif (is_numeric($value)) { + $this->filters->addFilter( + Filter::numberFilter($property, $operator, $value) + ); + } else { + $this->filters->addFilter( + Filter::textFilter($property, $operator, $value) + ); + } + + return $this; + } + + public function paginate($pageSize = 100) + { + $this->endpoint->limit($pageSize); + $offset = Request::get('cursor'); + + if ($offset !== null) { + $this->endpoint->offset(new StartCursor($offset)); + } + + $result = $this->internalQuery(); + + return [ + 'per_page' => $pageSize, + 'next_cursor' => $result->getRawNextCursor(), + 'next_page_url' => Request::fullUrlWithQuery(['cursor' => $result->getRawNextCursor()]), + 'from' => $result->asCollection()->first()->getId(), + 'to' => $result->asCollection()->last()->getId(), + 'data' => $result->asCollection(), + ]; + } + + /** + * @return string + */ + private function cacheKey(): string + { + $postCacheKey = ''; + if ($this->nextCursor !== null) { + $postCacheKey = '-'.$this->nextCursor->__toString(); + } + + return $this->modelClass::$preCacheKey.$this->modelClass::$databaseId.$postCacheKey; + } +} diff --git a/src/Notion.php b/src/Notion.php index e371dc2..9d8f7ba 100644 --- a/src/Notion.php +++ b/src/Notion.php @@ -8,6 +8,7 @@ use FiveamCode\LaravelNotionApi\Endpoints\Databases; use FiveamCode\LaravelNotionApi\Endpoints\Endpoint; use FiveamCode\LaravelNotionApi\Endpoints\Pages; +use FiveamCode\LaravelNotionApi\Endpoints\Resolve; use FiveamCode\LaravelNotionApi\Endpoints\Search; use FiveamCode\LaravelNotionApi\Endpoints\Users; use FiveamCode\LaravelNotionApi\Exceptions\HandlingException; @@ -196,6 +197,11 @@ public function comments(): Comments return new Comments($this); } + public function resolve(): Resolve + { + return new Resolve($this); + } + /** * @return string */ diff --git a/src/Query/Filters/Filter.php b/src/Query/Filters/Filter.php index def06c8..da3caf7 100644 --- a/src/Query/Filters/Filter.php +++ b/src/Query/Filters/Filter.php @@ -53,7 +53,7 @@ public function __construct( * * @param string $property * @param string $comparisonOperator - * @param $value + * @param $value * @return Filter */ public static function textFilter(string $property, string $comparisonOperator, string $value): Filter @@ -160,8 +160,8 @@ public static function filterQuery(Collection $filter): array /** * Checks if the given comparison operator is valid for the given filter type. * - * @param $filterType - * @param $operator + * @param $filterType + * @param $operator * * @throws HandlingException */ diff --git a/src/Traits/HasParent.php b/src/Traits/HasParent.php index 8356ae6..5bd8501 100644 --- a/src/Traits/HasParent.php +++ b/src/Traits/HasParent.php @@ -2,6 +2,7 @@ namespace FiveamCode\LaravelNotionApi\Traits; +use FiveamCode\LaravelNotionApi\Entities\NotionParent; use Illuminate\Support\Arr; /** @@ -54,4 +55,15 @@ public function getParentType(): string { return $this->parentType; } + + /** + * @return NotionParent + */ + public function getParent() + { + return new NotionParent([ + 'id' => $this->getParentId(), + 'object' => $this->getParentType(), + ]); + } } diff --git a/src/Traits/HasTitle.php b/src/Traits/HasTitle.php new file mode 100644 index 0000000..d5f641f --- /dev/null +++ b/src/Traits/HasTitle.php @@ -0,0 +1,63 @@ +fillTitle(); + } + + 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->richTitle = new RichText($this->responseData['title']); + } + } + + public function setTitle($title): self + { + $this->title = $title; + $this->responseData['title'] = [ + [ + 'type' => 'text', + 'text' => [ + 'content' => $title, + ], + ], + ]; + + return $this; + } + + /** + * @return string + */ + public function getTitle(): string + { + return $this->title; + } +} diff --git a/tests/EndpointBlocksTest.php b/tests/EndpointBlocksTest.php index b1dd23f..cb9f8cc 100644 --- a/tests/EndpointBlocksTest.php +++ b/tests/EndpointBlocksTest.php @@ -317,7 +317,7 @@ public function classProvider(): array * * @dataProvider classProvider * - * @param $entityClass + * @param $entityClass */ public function it_throws_an_handling_exception_for_wrong_type($entityClass) { diff --git a/tests/RecordedEndpointCommentsTest.php b/tests/RecordedEndpointCommentsTest.php index 15ae88a..9fb24a9 100644 --- a/tests/RecordedEndpointCommentsTest.php +++ b/tests/RecordedEndpointCommentsTest.php @@ -7,12 +7,16 @@ use FiveamCode\LaravelNotionApi\Exceptions\NotionException; use Illuminate\Support\Facades\Http; +$httpRecorder = null; + beforeEach(function () { - Http::recordAndFakeLater('https://api.notion.com/v1/comments*') + $this->httpRecorder = Http::recordAndFakeLater('https://api.notion.com/v1/comments*') ->storeIn('snapshots/comments'); }); it('should fetch list of comments with an accurate representation of attributes', function () { + $this->httpRecorder->nameForNextRequest('list-of-comments'); + $commentCollection = \Notion::comments()->ofBlock('cb588bcbcbdb4f2eac3db05446b8f5d9'); $collection = $commentCollection->asCollection(); @@ -41,6 +45,7 @@ }); it('should throw correct exception if block_id has not been found when listing comments', function () { + $this->httpRecorder->nameForNextRequest('comment-not-found'); $this->expectException(NotionException::class); $this->expectExceptionMessage('Not Found'); $this->expectExceptionCode(404); diff --git a/tests/RecordedEndpointDatabasesCreationTest.php b/tests/RecordedEndpointDatabasesCreationTest.php new file mode 100644 index 0000000..950da37 --- /dev/null +++ b/tests/RecordedEndpointDatabasesCreationTest.php @@ -0,0 +1,162 @@ +httpRecorder = Http::recordAndFakeLater('https://api.notion.com/v1/databases*') + ->storeIn('snapshots/databases'); +}); + +it('should throw a handling exception if no title property is added', function () { + $this->httpRecorder->nameForNextRequest('400-no-title-property'); + $this->expectException(\FiveamCode\LaravelNotionApi\Exceptions\NotionException::class); + $this->expectExceptionMessage('Bad Request: (validation_error) (Title is not provided)'); + $this->expectExceptionCode(400); + + Notion::databases() + ->build() + ->add(PropertyBuilder::checkbox('Test Checkbox')) + ->createInPage('0adbc2eb57e84569a700a70d537615be'); +}); + +it('should create a new database with all available properties', function () { + $this->httpRecorder->nameForNextRequest('all-properties'); + + $selectOptions = [ + [ + 'name' => 'testing', + 'color' => 'blue', + ], + ]; + + $multiSelectOptions = [ + [ + 'name' => 'testing2', + 'color' => 'yellow', + ], + ]; + + $scheme = PropertyBuilder::bulk() + ->title('Test Title') + ->plain('Test Custom RichText', 'rich_text') + ->richText('Test RichText') + ->checkbox('Test Checkbox') + // ->status() //TODO: Currently not supported due to Notion API versioning + ->select('Test Select', $selectOptions) + ->multiSelect('Test MultiSelect', $multiSelectOptions) + ->number('Test Number', 'dollar') + ->date('Test Date') + ->formula('Test Formula', 'prop("Test MultiSelect")') + ->url('Test Url') + ->email('Test Email') + ->phoneNumber('Test PhoneNumber') + ->people('Test People') + ->files('Test Files') + ->relation('Test Relation', '375da18ab01d42d18e95a9dc6a901db1') + ->rollup('Test Rollup', 'Tag', 'Test Relation', 'unique') + ->createdBy('Test Created By') + ->createdTime('Test Created Time') + ->lastEditedBy('Test Last Edited By') + ->lastEditedTime('Test Last Edited Time'); + + $databaseEntity = Notion::databases() + ->build() + // ->inline() //TODO: Currently not supported due to Notion API versioning + ->title('Created By Testing Database') + ->coverExternal('https://example.com/cover.jpg') + ->iconExternal('https://example.com/cover.jpg') + ->description('This Database has been created by a Pest Test from Laravel') + ->add($scheme) + ->createInPage('0adbc2eb57e84569a700a70d537615be'); + + expect($databaseEntity->getCover())->toEqual('https://example.com/cover.jpg'); + expect($databaseEntity->getIcon())->toEqual('https://example.com/cover.jpg'); + //TODO: Currently not supported due to Notion API versioning + // expect($databaseEntity->getDescription())->toEqual('This Database has been created by a Pest Test from Laravel'); + expect($databaseEntity->getTitle())->toEqual('Created By Testing Database'); + expect($databaseEntity->getParentId())->toEqual('0adbc2eb-57e8-4569-a700-a70d537615be'); + + expect($databaseEntity->getProperties())->toHaveCount(20); + expect($databaseEntity->getProperty('Test Title'))->toBeInstanceOf(Title::class); + expect($databaseEntity->getProperty('Test Custom RichText'))->toBeInstanceOf(Text::class); + expect($databaseEntity->getProperty('Test RichText'))->toBeInstanceOf(Text::class); + expect($databaseEntity->getProperty('Test Checkbox'))->toBeInstanceOf(Checkbox::class); + expect($databaseEntity->getProperty('Test Select'))->toBeInstanceOf(Select::class); + expect($databaseEntity->getProperty('Test MultiSelect'))->toBeInstanceOf(MultiSelect::class); + expect($databaseEntity->getProperty('Test Number'))->toBeInstanceOf(Number::class); + expect($databaseEntity->getProperty('Test Date'))->toBeInstanceOf(Date::class); + expect($databaseEntity->getProperty('Test Formula'))->toBeInstanceOf(Formula::class); + expect($databaseEntity->getProperty('Test Url'))->toBeInstanceOf(Url::class); + expect($databaseEntity->getProperty('Test Email'))->toBeInstanceOf(Email::class); + expect($databaseEntity->getProperty('Test PhoneNumber'))->toBeInstanceOf(PhoneNumber::class); + expect($databaseEntity->getProperty('Test People'))->toBeInstanceOf(People::class); + expect($databaseEntity->getProperty('Test Files'))->toBeInstanceOf(Files::class); + expect($databaseEntity->getProperty('Test Relation'))->toBeInstanceOf(Relation::class); + expect($databaseEntity->getProperty('Test Rollup'))->toBeInstanceOf(Rollup::class); + expect($databaseEntity->getProperty('Test Created By'))->toBeInstanceOf(CreatedBy::class); + expect($databaseEntity->getProperty('Test Created Time'))->toBeInstanceOf(CreatedTime::class); + expect($databaseEntity->getProperty('Test Last Edited By'))->toBeInstanceOf(LastEditedBy::class); + expect($databaseEntity->getProperty('Test Last Edited Time'))->toBeInstanceOf(LastEditedTime::class); + + expect($databaseEntity->getProperty('Test Relation')->getRelation()[0])->toBe('375da18a-b01d-42d1-8e95-a9dc6a901db1'); + + expect($databaseEntity->getProperty('Test Rollup')->getContent()['rollup_property_name'])->toBe('Tag'); + expect($databaseEntity->getProperty('Test Rollup')->getContent()['relation_property_name'])->toBe('Test Relation'); + expect($databaseEntity->getProperty('Test Rollup')->getContent()['function'])->toBe('unique'); + + expect($databaseEntity->getProperty('Test Select')->getOptions())->toHaveCount(count($selectOptions)); + expect($databaseEntity->getProperty('Test Select')->getOptions()[0]->getName())->toEqual($selectOptions[0]['name']); + expect($databaseEntity->getProperty('Test Select')->getOptions()[0]->getColor())->toEqual($selectOptions[0]['color']); + + expect($databaseEntity->getProperty('Test MultiSelect')->getOptions())->toHaveCount(count($multiSelectOptions)); + expect($databaseEntity->getProperty('Test MultiSelect')->getOptions()[0]->getName())->toEqual($multiSelectOptions[0]['name']); + expect($databaseEntity->getProperty('Test MultiSelect')->getOptions()[0]->getColor())->toEqual($multiSelectOptions[0]['color']); + + expect($databaseEntity->getProperty('Test Number')->getRawResponse()['number']['format'])->toBe('dollar'); +}); + +it('should create a new database with default title property', function () { + $this->httpRecorder->nameForNextRequest('with-emoji-icon'); + + $databaseEntity = Notion::databases() + ->build() + ->createInPage('0adbc2eb57e84569a700a70d537615be'); + + expect($databaseEntity->getProperties())->toHaveCount(1); + expect($databaseEntity->getProperty('Name'))->toBeInstanceOf(Title::class); +}); + +it('should create a new database with emoji icon', function () { + $this->httpRecorder->nameForNextRequest('only-title-properties'); + + $databaseEntity = Notion::databases() + ->build() + ->iconEmoji('👍') + ->createInPage('0adbc2eb57e84569a700a70d537615be'); + + expect($databaseEntity->getProperties())->toHaveCount(1); + expect($databaseEntity->getProperty('Name'))->toBeInstanceOf(Title::class); + expect($databaseEntity->getIcon())->toBe('👍'); +}); diff --git a/tests/RecordedEndpointResolveTest.php b/tests/RecordedEndpointResolveTest.php new file mode 100644 index 0000000..6c3e599 --- /dev/null +++ b/tests/RecordedEndpointResolveTest.php @@ -0,0 +1,133 @@ +httpRecorder = Http::recordAndFakeLater([ + 'https://api.notion.com/v1/databases*', + 'https://api.notion.com/v1/pages*', + 'https://api.notion.com/v1/blocks*', + 'https://api.notion.com/v1/users*', + ])->storeIn('snapshots/resolve'); +}); + +it('should resolve the users of specific page properties', function () { + $this->httpRecorder->nameForNextRequest('for-user-resolve'); + $page = Notion::pages()->find('8890c263e97c45339ef5616d5e75360e'); + + $createdBy = $page->getProperty('Created by'); + $lastEditedBy = $page->getProperty('Last edited by'); + $person = $page->getProperty('Person'); + + $createdByUser = Notion::resolve()->user($createdBy->getUser()); + $lastEditedByUser = Notion::resolve()->user($lastEditedBy->getUser()); + $personUser = Notion::resolve()->user($person->getPeople()->first()); + + expect($createdByUser)->toBeInstanceOf(\FiveamCode\LaravelNotionApi\Entities\User::class); + expect($createdByUser->getName())->toBe('TestUser for NotionForLaravel'); + expect($createdByUser->getId())->toBe('455aad58-7aec-4a39-8c0f-37cab3ca38f5'); + + expect($lastEditedByUser)->toBeInstanceOf(\FiveamCode\LaravelNotionApi\Entities\User::class); + expect($lastEditedByUser->getName())->toBe('TestUser for NotionForLaravel'); + expect($lastEditedByUser->getId())->toBe('455aad58-7aec-4a39-8c0f-37cab3ca38f5'); + + expect($personUser)->toBeInstanceOf(\FiveamCode\LaravelNotionApi\Entities\User::class); + expect($personUser->getName())->toBe('TestUser for NotionForLaravel'); + expect($personUser->getId())->toBe('455aad58-7aec-4a39-8c0f-37cab3ca38f5'); +}); + +it('should resolve the page parent of a page', function () { + $page = Notion::pages()->find('a652fac351cc4cc79f5b17eb702793ed'); + $parentPage = Notion::resolve()->parent($page->getParent()); + + expect($page->getParent()->isPage())->toBeTrue(); + + expect($parentPage)->toBeInstanceOf(\FiveamCode\LaravelNotionApi\Entities\Page::class); + expect($parentPage->getId())->toBe('5ac149b9-d8f1-4d8d-ac05-facefc16ebf7'); + expect($parentPage->getTitle())->toBe('Resolve Endpoint - Testing Suite'); +}); + +it('should return the workspace parent of a page without resolving it', function () { + $page = Notion::pages()->find('91f70932ee6347b59bc243e09b4cc9b0'); + $parentWorkspace = Notion::resolve()->parent($page->getParent()); + + expect($page->getParent()->isWorkspace())->toBeTrue(); + + expect($parentWorkspace)->toBeInstanceOf(NotionParent::class); + expect($parentWorkspace->getId())->toBe('1'); + expect($parentWorkspace->getObjectType())->toBe('workspace'); +}); + +it('should resolve the database parent of a page', function () { + $page = Notion::pages()->find('415d9b6c6e454f42aab2b6e13804cfe9'); + + expect($page->getParent()->isDatabase())->toBeTrue(); + + $database = Notion::resolve()->parent($page->getParent()); + expect($database)->toBeInstanceOf(\FiveamCode\LaravelNotionApi\Entities\Database::class); + expect($database->getId())->toBe('8a0ef209-8c8a-4fd1-a21c-db7ab327e870'); + expect($database->getTitle())->toBe('Test Table as Parent'); +}); + +it('should resolve the block parent of a block', function () { + $block = Notion::block('d5f9419b44204c909501b1e2b7569503')->retrieve(); + + expect($block->getParent()->isBlock())->toBeTrue(); + + $parentBlock = Notion::resolve()->parent($block->getParent()); + expect($parentBlock)->toBeInstanceOf(\FiveamCode\LaravelNotionApi\Entities\Blocks\Block::class); + expect($parentBlock->getId())->toBe('0971ac1a-b6f2-4acc-b706-f5f2ed16ffd6'); + expect($parentBlock->getType())->toBe('paragraph'); +}); + +it('should resolve the page parent of a block', function () { + $block = Notion::block('0971ac1a-b6f2-4acc-b706-f5f2ed16ffd6')->retrieve(); + + $pageParent = Notion::resolve()->parent($block->getParent()); + expect($pageParent)->toBeInstanceOf(\FiveamCode\LaravelNotionApi\Entities\Page::class); + expect($pageParent->getId())->toBe('d946d011-966d-4b14-973f-dc5580f5b024'); + expect($pageParent->getTitle())->toBe('Page for Block Parent Resolve Testing'); + + $pageParent = Notion::resolve()->parentOf($block); + expect($pageParent)->toBeInstanceOf(\FiveamCode\LaravelNotionApi\Entities\Page::class); + expect($pageParent->getId())->toBe('d946d011-966d-4b14-973f-dc5580f5b024'); + expect($pageParent->getTitle())->toBe('Page for Block Parent Resolve Testing'); +}); + +it('should throw a handling exception when unknown parent type', function () { + expect(fn () => new NotionParent(['object' => 'unknown', 'id' => '1234']))->toThrow('invalid json-array: the given object is not a valid parent'); +}); + +it('should throw a handling exception when entity without parent', function () { + $entityWithoutParent = new User(['object' => 'user', 'id' => '1234']); + expect(fn () => Notion::resolve()->parentOf($entityWithoutParent))->toThrow("The given entity 'user' does not have a parent."); +}); + +it('should resolve the pages of a database relation', function () { + $page = Notion::pages()->find('1c56e2ad3d95458c935dae6d57769037'); + + $relationPropertyItems = $page->getProperty('Parent Relation Database'); + $relationPages = Notion::resolve()->relations($relationPropertyItems); + + expect($relationPages)->toBeInstanceOf(\Illuminate\Support\Collection::class); + expect($relationPages->count())->toBe(3); + expect($relationPages->first())->toBeInstanceOf(\FiveamCode\LaravelNotionApi\Entities\Page::class); + expect($relationPages->first()->getId())->toBe('cfb10a19-30cc-43a9-8db0-04c43f8cf315'); + expect($relationPages->first()->getTitle())->toBe('test 1'); +}); + +it('should resolve the page titles of a database relation', function () { + $page = Notion::pages()->find('1c56e2ad3d95458c935dae6d57769037'); + + $relationPropertyItems = $page->getProperty('Parent Relation Database'); + $relationPageTitles = Notion::resolve()->relations($relationPropertyItems, true); + + expect($relationPageTitles)->toBeInstanceOf(\Illuminate\Support\Collection::class); + expect($relationPageTitles->count())->toBe(3); + expect($relationPageTitles->first())->toBeString(); + expect($relationPageTitles->first())->toBe('test 1'); +}); diff --git a/tests/snapshots/comments/get_v1-comments_block-id-cb588bcbcbdb4f2eac3db05446b8f5d9-page-size-100.json b/tests/snapshots/comments/get_v1-comments_1c611225.json similarity index 84% rename from tests/snapshots/comments/get_v1-comments_block-id-cb588bcbcbdb4f2eac3db05446b8f5d9-page-size-100.json rename to tests/snapshots/comments/get_v1-comments_1c611225.json index 5769c30..e92b7b9 100644 --- a/tests/snapshots/comments/get_v1-comments_block-id-cb588bcbcbdb4f2eac3db05446b8f5d9-page-size-100.json +++ b/tests/snapshots/comments/get_v1-comments_1c611225.json @@ -1,5 +1,18 @@ { + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", "status": 200, + "payload": "block_id=cb588bcbcbdb4f2eac3db05446b8f5d9&page_size=100&", "data": { "object": "list", "results": [ diff --git a/tests/snapshots/comments/get_v1-comments_4aed127b.json b/tests/snapshots/comments/get_v1-comments_4aed127b.json new file mode 100644 index 0000000..1aba430 --- /dev/null +++ b/tests/snapshots/comments/get_v1-comments_4aed127b.json @@ -0,0 +1,22 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 404, + "payload": "block_id=cbf6b0af-6eaa-45ca-9715-9fa147ef6b17&page_size=100&", + "data": { + "object": "error", + "status": 404, + "code": "object_not_found", + "message": "Could not find block with ID: cbf6b0af-6eaa-45ca-9715-9fa147ef6b17. Make sure the relevant pages and databases are shared with your integration." + } +} \ No newline at end of file diff --git a/tests/snapshots/comments/get_v1-comments_block-id-cbf6b0af-6eaa-45ca-9715-9fa147ef6b17-page-size-100.json b/tests/snapshots/comments/get_v1-comments_block-id-cbf6b0af-6eaa-45ca-9715-9fa147ef6b17-page-size-100.json deleted file mode 100644 index 743a987..0000000 --- a/tests/snapshots/comments/get_v1-comments_block-id-cbf6b0af-6eaa-45ca-9715-9fa147ef6b17-page-size-100.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "status": 404, - "data": { - "object": "error", - "status": 404, - "code": "object_not_found", - "message": "Could not find block with ID: cbf6b0af-6eaa-45ca-9715-9fa147ef6b17. Make sure the relevant pages and databases are shared with your integration." - } -} \ No newline at end of file diff --git a/tests/snapshots/comments/get_v1-comments_comment-not-found.json b/tests/snapshots/comments/get_v1-comments_comment-not-found.json new file mode 100644 index 0000000..1aba430 --- /dev/null +++ b/tests/snapshots/comments/get_v1-comments_comment-not-found.json @@ -0,0 +1,22 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 404, + "payload": "block_id=cbf6b0af-6eaa-45ca-9715-9fa147ef6b17&page_size=100&", + "data": { + "object": "error", + "status": 404, + "code": "object_not_found", + "message": "Could not find block with ID: cbf6b0af-6eaa-45ca-9715-9fa147ef6b17. Make sure the relevant pages and databases are shared with your integration." + } +} \ No newline at end of file diff --git a/tests/snapshots/comments/get_v1-comments_list-of-comments.json b/tests/snapshots/comments/get_v1-comments_list-of-comments.json new file mode 100644 index 0000000..e92b7b9 --- /dev/null +++ b/tests/snapshots/comments/get_v1-comments_list-of-comments.json @@ -0,0 +1,59 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": "block_id=cb588bcbcbdb4f2eac3db05446b8f5d9&page_size=100&", + "data": { + "object": "list", + "results": [ + { + "object": "comment", + "id": "99457ae4-8262-413a-b224-0bd82346d885", + "parent": { + "type": "page_id", + "page_id": "cb588bcb-cbdb-4f2e-ac3d-b05446b8f5d9" + }, + "discussion_id": "f203fa27-fe02-40c9-be9f-fb35e2e956ba", + "created_time": "2023-02-18T10:53:00.000Z", + "last_edited_time": "2023-02-18T10:53:00.000Z", + "created_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "rich_text": [ + { + "type": "text", + "text": { + "content": "This is a Test Comment for Laravel", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "This is a Test Comment for Laravel", + "href": null + } + ] + } + ], + "next_cursor": null, + "has_more": false, + "type": "comment", + "comment": [] + } +} \ No newline at end of file diff --git a/tests/snapshots/databases/post_v1-databases_400-no-title-property.json b/tests/snapshots/databases/post_v1-databases_400-no-title-property.json new file mode 100644 index 0000000..4d80e4c --- /dev/null +++ b/tests/snapshots/databases/post_v1-databases_400-no-title-property.json @@ -0,0 +1,47 @@ +{ + "header": { + "Content-Length": [ + "191" + ], + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Content-Type": [ + "application\/json" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "post", + "status": 400, + "payload": { + "is_inline": false, + "parent": { + "type": "page_id", + "page_id": "0adbc2eb57e84569a700a70d537615be" + }, + "title": [ + { + "text": { + "content": "" + } + } + ], + "properties": { + "Test Checkbox": { + "type": "checkbox", + "checkbox": [] + } + } + }, + "data": { + "object": "error", + "status": 400, + "code": "validation_error", + "message": "Title is not provided" + } +} \ No newline at end of file diff --git a/tests/snapshots/databases/post_v1-databases_all-properties.json b/tests/snapshots/databases/post_v1-databases_all-properties.json new file mode 100644 index 0000000..6046862 --- /dev/null +++ b/tests/snapshots/databases/post_v1-databases_all-properties.json @@ -0,0 +1,366 @@ +{ + "header": { + "Content-Length": [ + "1729" + ], + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Content-Type": [ + "application\/json" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "post", + "status": 200, + "payload": { + "is_inline": false, + "parent": { + "type": "page_id", + "page_id": "0adbc2eb57e84569a700a70d537615be" + }, + "title": [ + { + "text": { + "content": "Created By Testing Database" + } + } + ], + "properties": { + "Test Title": { + "type": "title", + "title": [] + }, + "Test Custom RichText": { + "type": "rich_text", + "rich_text": [] + }, + "Test RichText": { + "type": "rich_text", + "rich_text": [] + }, + "Test Checkbox": { + "type": "checkbox", + "checkbox": [] + }, + "Test Select": { + "type": "select", + "select": { + "options": [ + { + "name": "testing", + "color": "blue" + } + ] + } + }, + "Test MultiSelect": { + "type": "multi_select", + "multi_select": { + "options": [ + { + "name": "testing2", + "color": "yellow" + } + ] + } + }, + "Test Number": { + "type": "number", + "number": { + "format": "dollar" + } + }, + "Test Date": { + "type": "date", + "date": [] + }, + "Test Formula": { + "type": "formula", + "formula": { + "expression": "prop(\"Test MultiSelect\")" + } + }, + "Test Url": { + "type": "url", + "url": [] + }, + "Test Email": { + "type": "email", + "email": [] + }, + "Test PhoneNumber": { + "type": "phone_number", + "phone_number": [] + }, + "Test People": { + "type": "people", + "people": [] + }, + "Test Files": { + "type": "files", + "files": [] + }, + "Test Relation": { + "type": "relation", + "relation": { + "database_id": "375da18ab01d42d18e95a9dc6a901db1" + } + }, + "Test Rollup": { + "type": "rollup", + "rollup": { + "relation_property_name": "Test Relation", + "rollup_property_name": "Tag", + "function": "unique" + } + }, + "Test Created By": { + "type": "created_by", + "created_by": [] + }, + "Test Created Time": { + "type": "created_time", + "created_time": [] + }, + "Test Last Edited By": { + "type": "last_edited_by", + "last_edited_by": [] + }, + "Test Last Edited Time": { + "type": "last_edited_time", + "last_edited_time": [] + } + }, + "cover": { + "type": "external", + "external": { + "url": "https:\/\/example.com\/cover.jpg" + } + }, + "icon": { + "type": "external", + "external": { + "url": "https:\/\/example.com\/cover.jpg" + } + }, + "description": [ + { + "text": { + "content": "This Database has been created by a Pest Test from Laravel" + } + } + ] + }, + "data": { + "object": "database", + "id": "8b0013db-0fbf-49a5-ad64-9de6d4670e17", + "cover": { + "type": "external", + "external": { + "url": "https:\/\/example.com\/cover.jpg" + } + }, + "icon": { + "type": "external", + "external": { + "url": "https:\/\/example.com\/cover.jpg" + } + }, + "created_time": "2023-06-10T08:30:00.000Z", + "created_by": { + "object": "user", + "id": "1068e45a-6f6d-4b78-abd0-0d1d44bde855" + }, + "last_edited_by": { + "object": "user", + "id": "1068e45a-6f6d-4b78-abd0-0d1d44bde855" + }, + "last_edited_time": "2023-06-10T08:30:00.000Z", + "title": [ + { + "type": "text", + "text": { + "content": "Created By Testing Database", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Created By Testing Database", + "href": null + } + ], + "description": [], + "is_inline": false, + "properties": { + "Test People": { + "id": ":[aJ", + "name": "Test People", + "type": "people", + "people": [] + }, + "Test Date": { + "id": ";LyC", + "name": "Test Date", + "type": "date", + "date": [] + }, + "Test Number": { + "id": "<`a@", + "name": "Test Number", + "type": "number", + "number": { + "format": "dollar" + } + }, + "Test Created By": { + "id": "Anad", + "function": "unique" + } + }, + "Test Custom RichText": { + "id": "iaDK", + "name": "Test Custom RichText", + "type": "rich_text", + "rich_text": [] + }, + "Test Relation": { + "id": "m>ad", + "name": "Test Relation", + "type": "relation", + "relation": { + "database_id": "375da18a-b01d-42d1-8e95-a9dc6a901db1", + "synced_property_name": "Related to Created By Testing Database (Test Relation)", + "synced_property_id": "{Ykb" + } + }, + "Test Email": { + "id": "pZRE", + "name": "Test Email", + "type": "email", + "email": [] + }, + "Test Checkbox": { + "id": "r@Xw", + "name": "Test Checkbox", + "type": "checkbox", + "checkbox": [] + }, + "Test Last Edited By": { + "id": "rXhE", + "name": "Test Last Edited By", + "type": "last_edited_by", + "last_edited_by": [] + }, + "Test Created Time": { + "id": "rejS", + "name": "Test Created Time", + "type": "created_time", + "created_time": [] + }, + "Test Last Edited Time": { + "id": "yXTc", + "name": "Test Last Edited Time", + "type": "last_edited_time", + "last_edited_time": [] + }, + "Test Title": { + "id": "title", + "name": "Test Title", + "type": "title", + "title": [] + } + }, + "parent": { + "type": "page_id", + "page_id": "0adbc2eb-57e8-4569-a700-a70d537615be" + }, + "url": "https:\/\/www.notion.so\/8b0013db0fbf49a5ad649de6d4670e17", + "public_url": null, + "archived": false + } +} \ No newline at end of file diff --git a/tests/snapshots/databases/post_v1-databases_only-title-properties.json b/tests/snapshots/databases/post_v1-databases_only-title-properties.json new file mode 100644 index 0000000..77f4903 --- /dev/null +++ b/tests/snapshots/databases/post_v1-databases_only-title-properties.json @@ -0,0 +1,100 @@ +{ + "header": { + "Content-Length": [ + "223" + ], + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Content-Type": [ + "application\/json" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "post", + "status": 200, + "payload": { + "is_inline": false, + "parent": { + "type": "page_id", + "page_id": "0adbc2eb57e84569a700a70d537615be" + }, + "title": [ + { + "text": { + "content": "" + } + } + ], + "properties": { + "Name": { + "type": "title", + "title": [] + } + }, + "icon": { + "type": "emoji", + "emoji": "\ud83d\udc4d" + } + }, + "data": { + "object": "database", + "id": "0bd8bcfc-36b0-4f6d-9726-25ba075fa2f1", + "cover": null, + "icon": { + "type": "emoji", + "emoji": "\ud83d\udc4d" + }, + "created_time": "2023-06-10T08:06:00.000Z", + "created_by": { + "object": "user", + "id": "1068e45a-6f6d-4b78-abd0-0d1d44bde855" + }, + "last_edited_by": { + "object": "user", + "id": "1068e45a-6f6d-4b78-abd0-0d1d44bde855" + }, + "last_edited_time": "2023-06-10T08:06:00.000Z", + "title": [ + { + "type": "text", + "text": { + "content": "", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "", + "href": null + } + ], + "description": [], + "is_inline": false, + "properties": { + "Name": { + "id": "title", + "name": "Name", + "type": "title", + "title": [] + } + }, + "parent": { + "type": "page_id", + "page_id": "0adbc2eb-57e8-4569-a700-a70d537615be" + }, + "url": "https:\/\/www.notion.so\/0bd8bcfc36b04f6d972625ba075fa2f1", + "public_url": null, + "archived": false + } +} \ No newline at end of file diff --git a/tests/snapshots/databases/post_v1-databases_with-emoji-icon.json b/tests/snapshots/databases/post_v1-databases_with-emoji-icon.json new file mode 100644 index 0000000..52c54f5 --- /dev/null +++ b/tests/snapshots/databases/post_v1-databases_with-emoji-icon.json @@ -0,0 +1,93 @@ +{ + "header": { + "Content-Length": [ + "176" + ], + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Content-Type": [ + "application\/json" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "post", + "status": 200, + "payload": { + "is_inline": false, + "parent": { + "type": "page_id", + "page_id": "0adbc2eb57e84569a700a70d537615be" + }, + "title": [ + { + "text": { + "content": "" + } + } + ], + "properties": { + "Name": { + "type": "title", + "title": [] + } + } + }, + "data": { + "object": "database", + "id": "8e0c5fe2-9c95-43d9-aa0a-6ef47c97904b", + "cover": null, + "icon": null, + "created_time": "2023-06-10T08:06:00.000Z", + "created_by": { + "object": "user", + "id": "1068e45a-6f6d-4b78-abd0-0d1d44bde855" + }, + "last_edited_by": { + "object": "user", + "id": "1068e45a-6f6d-4b78-abd0-0d1d44bde855" + }, + "last_edited_time": "2023-06-10T08:06:00.000Z", + "title": [ + { + "type": "text", + "text": { + "content": "", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "", + "href": null + } + ], + "description": [], + "is_inline": false, + "properties": { + "Name": { + "id": "title", + "name": "Name", + "type": "title", + "title": [] + } + }, + "parent": { + "type": "page_id", + "page_id": "0adbc2eb-57e8-4569-a700-a70d537615be" + }, + "url": "https:\/\/www.notion.so\/8e0c5fe29c9543d9aa0a6ef47c97904b", + "public_url": null, + "archived": false + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-blocks-0971ac1a-b6f2-4acc-b706-f5f2ed16ffd6_00000001.json b/tests/snapshots/resolve/get_v1-blocks-0971ac1a-b6f2-4acc-b706-f5f2ed16ffd6_00000001.json new file mode 100644 index 0000000..6feb7cd --- /dev/null +++ b/tests/snapshots/resolve/get_v1-blocks-0971ac1a-b6f2-4acc-b706-f5f2ed16ffd6_00000001.json @@ -0,0 +1,59 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "block", + "id": "0971ac1a-b6f2-4acc-b706-f5f2ed16ffd6", + "parent": { + "type": "page_id", + "page_id": "d946d011-966d-4b14-973f-dc5580f5b024" + }, + "created_time": "2023-06-09T16:38:00.000Z", + "last_edited_time": "2023-06-09T16:39:00.000Z", + "created_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "last_edited_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "has_children": true, + "archived": false, + "type": "paragraph", + "paragraph": { + "color": "default", + "text": [ + { + "type": "text", + "text": { + "content": "This is a parent block", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "This is a parent block", + "href": null + } + ] + } + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-blocks-d5f9419b44204c909501b1e2b7569503_00000001.json b/tests/snapshots/resolve/get_v1-blocks-d5f9419b44204c909501b1e2b7569503_00000001.json new file mode 100644 index 0000000..66bc60c --- /dev/null +++ b/tests/snapshots/resolve/get_v1-blocks-d5f9419b44204c909501b1e2b7569503_00000001.json @@ -0,0 +1,59 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "block", + "id": "d5f9419b-4420-4c90-9501-b1e2b7569503", + "parent": { + "type": "block_id", + "block_id": "0971ac1a-b6f2-4acc-b706-f5f2ed16ffd6" + }, + "created_time": "2023-06-09T16:39:00.000Z", + "last_edited_time": "2023-06-09T16:39:00.000Z", + "created_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "last_edited_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "has_children": false, + "archived": false, + "type": "paragraph", + "paragraph": { + "color": "default", + "text": [ + { + "type": "text", + "text": { + "content": "Of this child block", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Of this child block", + "href": null + } + ] + } + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-databases-8a0ef209-8c8a-4fd1-a21c-db7ab327e870_00000001.json b/tests/snapshots/resolve/get_v1-databases-8a0ef209-8c8a-4fd1-a21c-db7ab327e870_00000001.json new file mode 100644 index 0000000..485076f --- /dev/null +++ b/tests/snapshots/resolve/get_v1-databases-8a0ef209-8c8a-4fd1-a21c-db7ab327e870_00000001.json @@ -0,0 +1,81 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "database", + "id": "8a0ef209-8c8a-4fd1-a21c-db7ab327e870", + "cover": null, + "icon": { + "type": "external", + "external": { + "url": "https:\/\/www.notion.so\/icons\/people_red.svg" + } + }, + "created_time": "2023-06-09T16:32:00.000Z", + "created_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "last_edited_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "last_edited_time": "2023-06-09T16:35:00.000Z", + "title": [ + { + "type": "text", + "text": { + "content": "Test Table as Parent", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Test Table as Parent", + "href": null + } + ], + "description": [], + "is_inline": true, + "properties": { + "Tags": { + "id": "[PdA", + "name": "Tags", + "type": "multi_select", + "multi_select": { + "options": [] + } + }, + "Name": { + "id": "title", + "name": "Name", + "type": "title", + "title": [] + } + }, + "parent": { + "type": "page_id", + "page_id": "c3b3afe9-1381-470f-a8b1-4a44b9a3bf81" + }, + "url": "https:\/\/www.notion.so\/8a0ef2098c8a4fd1a21cdb7ab327e870", + "public_url": null, + "archived": false + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-pages-05b473e4-d38f-484b-a74b-d3273932e9b4_00000001.json b/tests/snapshots/resolve/get_v1-pages-05b473e4-d38f-484b-a74b-d3273932e9b4_00000001.json new file mode 100644 index 0000000..21e6075 --- /dev/null +++ b/tests/snapshots/resolve/get_v1-pages-05b473e4-d38f-484b-a74b-d3273932e9b4_00000001.json @@ -0,0 +1,74 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "page", + "id": "05b473e4-d38f-484b-a74b-d3273932e9b4", + "created_time": "2023-06-09T16:50:00.000Z", + "last_edited_time": "2023-06-09T16:51:00.000Z", + "created_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "last_edited_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "cover": null, + "icon": null, + "parent": { + "type": "database_id", + "database_id": "96f3b744-c9b5-48f2-849f-521aa7a83f27" + }, + "archived": false, + "properties": { + "Origin Relation Database ": { + "id": "RI]J", + "type": "relation", + "relation": [ + { + "id": "1c56e2ad-3d95-458c-935d-ae6d57769037" + } + ], + "has_more": false + }, + "Name": { + "id": "title", + "type": "title", + "title": [ + { + "type": "text", + "text": { + "content": "test 3", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "test 3", + "href": null + } + ] + } + }, + "url": "https:\/\/www.notion.so\/test-3-05b473e4d38f484ba74bd3273932e9b4", + "public_url": null + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-pages-1c56e2ad3d95458c935dae6d57769037_00000001.json b/tests/snapshots/resolve/get_v1-pages-1c56e2ad3d95458c935dae6d57769037_00000001.json new file mode 100644 index 0000000..f2517d6 --- /dev/null +++ b/tests/snapshots/resolve/get_v1-pages-1c56e2ad3d95458c935dae6d57769037_00000001.json @@ -0,0 +1,80 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "page", + "id": "1c56e2ad-3d95-458c-935d-ae6d57769037", + "created_time": "2023-06-09T16:50:00.000Z", + "last_edited_time": "2023-06-09T16:52:00.000Z", + "created_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "last_edited_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "cover": null, + "icon": null, + "parent": { + "type": "database_id", + "database_id": "274ec07d-ae73-4f3a-a907-5f879ca0b6d7" + }, + "archived": false, + "properties": { + "Parent Relation Database": { + "id": "GyPD", + "type": "relation", + "relation": [ + { + "id": "cfb10a19-30cc-43a9-8db0-04c43f8cf315" + }, + { + "id": "e226b338-0118-461d-a9ed-09c9a6f1e4e2" + }, + { + "id": "05b473e4-d38f-484b-a74b-d3273932e9b4" + } + ], + "has_more": false + }, + "Name": { + "id": "title", + "type": "title", + "title": [ + { + "type": "text", + "text": { + "content": "Origin Relation Page", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Origin Relation Page", + "href": null + } + ] + } + }, + "url": "https:\/\/www.notion.so\/Origin-Relation-Page-1c56e2ad3d95458c935dae6d57769037", + "public_url": null + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-pages-415d9b6c6e454f42aab2b6e13804cfe9_00000001.json b/tests/snapshots/resolve/get_v1-pages-415d9b6c6e454f42aab2b6e13804cfe9_00000001.json new file mode 100644 index 0000000..d9436d9 --- /dev/null +++ b/tests/snapshots/resolve/get_v1-pages-415d9b6c6e454f42aab2b6e13804cfe9_00000001.json @@ -0,0 +1,69 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "page", + "id": "415d9b6c-6e45-4f42-aab2-b6e13804cfe9", + "created_time": "2023-06-09T16:32:00.000Z", + "last_edited_time": "2023-06-09T16:32:00.000Z", + "created_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "last_edited_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "cover": null, + "icon": null, + "parent": { + "type": "database_id", + "database_id": "8a0ef209-8c8a-4fd1-a21c-db7ab327e870" + }, + "archived": false, + "properties": { + "Tags": { + "id": "[PdA", + "type": "multi_select", + "multi_select": [] + }, + "Name": { + "id": "title", + "type": "title", + "title": [ + { + "type": "text", + "text": { + "content": "Child Page", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Child Page", + "href": null + } + ] + } + }, + "url": "https:\/\/www.notion.so\/Child-Page-415d9b6c6e454f42aab2b6e13804cfe9", + "public_url": null + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-pages-5ac149b9-d8f1-4d8d-ac05-facefc16ebf7_00000001.json b/tests/snapshots/resolve/get_v1-pages-5ac149b9-d8f1-4d8d-ac05-facefc16ebf7_00000001.json new file mode 100644 index 0000000..4099414 --- /dev/null +++ b/tests/snapshots/resolve/get_v1-pages-5ac149b9-d8f1-4d8d-ac05-facefc16ebf7_00000001.json @@ -0,0 +1,69 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "page", + "id": "5ac149b9-d8f1-4d8d-ac05-facefc16ebf7", + "created_time": "2023-05-03T00:06:00.000Z", + "last_edited_time": "2023-06-09T16:49:00.000Z", + "created_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "last_edited_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "cover": null, + "icon": { + "type": "external", + "external": { + "url": "https:\/\/www.notion.so\/icons\/dependency_red.svg" + } + }, + "parent": { + "type": "page_id", + "page_id": "91f70932-ee63-47b5-9bc2-43e09b4cc9b0" + }, + "archived": false, + "properties": { + "title": { + "id": "title", + "type": "title", + "title": [ + { + "type": "text", + "text": { + "content": "Resolve Endpoint - Testing Suite", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Resolve Endpoint - Testing Suite", + "href": null + } + ] + } + }, + "url": "https:\/\/www.notion.so\/Resolve-Endpoint-Testing-Suite-5ac149b9d8f14d8dac05facefc16ebf7", + "public_url": null + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-pages-8890c263e97c45339ef5616d5e75360e_for-user-resolve.json b/tests/snapshots/resolve/get_v1-pages-8890c263e97c45339ef5616d5e75360e_for-user-resolve.json new file mode 100644 index 0000000..686dc2f --- /dev/null +++ b/tests/snapshots/resolve/get_v1-pages-8890c263e97c45339ef5616d5e75360e_for-user-resolve.json @@ -0,0 +1,119 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "page", + "id": "8890c263-e97c-4533-9ef5-616d5e75360e", + "created_time": "2023-06-09T16:14:00.000Z", + "last_edited_time": "2023-06-09T16:14:00.000Z", + "created_by": { + "object": "user", + "id": "455aad58-7aec-4a39-8c0f-37cab3ca38f5" + }, + "last_edited_by": { + "object": "user", + "id": "455aad58-7aec-4a39-8c0f-37cab3ca38f5" + }, + "cover": null, + "icon": null, + "parent": { + "type": "database_id", + "database_id": "f853b17f-0e70-45f8-8185-29bbe705b2b4" + }, + "archived": false, + "properties": { + "Last edited by": { + "id": "[Pd_", + "type": "last_edited_by", + "last_edited_by": { + "object": "user", + "id": "455aad58-7aec-4a39-8c0f-37cab3ca38f5", + "name": "TestUser for NotionForLaravel", + "avatar_url": null, + "type": "person", + "person": { + "email": "testuser@5amco.de" + } + } + }, + "Created by": { + "id": "bE]Q", + "type": "created_by", + "created_by": { + "object": "user", + "id": "455aad58-7aec-4a39-8c0f-37cab3ca38f5", + "name": "TestUser for NotionForLaravel", + "avatar_url": null, + "type": "person", + "person": { + "email": "testuser@5amco.de" + } + } + }, + "Tags": { + "id": "k}C>", + "type": "multi_select", + "multi_select": [ + { + "id": "769bb074-e4ca-459a-b24f-181d5babfcc6", + "name": "test page 1", + "color": "purple" + } + ] + }, + "Person": { + "id": "vFp]", + "type": "people", + "people": [ + { + "object": "user", + "id": "455aad58-7aec-4a39-8c0f-37cab3ca38f5", + "name": "TestUser for NotionForLaravel", + "avatar_url": null, + "type": "person", + "person": { + "email": "testuser@5amco.de" + } + } + ] + }, + "Name": { + "id": "title", + "type": "title", + "title": [ + { + "type": "text", + "text": { + "content": "Test Page 1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Test Page 1", + "href": null + } + ] + } + }, + "url": "https:\/\/www.notion.so\/Test-Page-1-8890c263e97c45339ef5616d5e75360e", + "public_url": null + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-pages-91f70932ee6347b59bc243e09b4cc9b0_00000001.json b/tests/snapshots/resolve/get_v1-pages-91f70932ee6347b59bc243e09b4cc9b0_00000001.json new file mode 100644 index 0000000..0c6e22e --- /dev/null +++ b/tests/snapshots/resolve/get_v1-pages-91f70932ee6347b59bc243e09b4cc9b0_00000001.json @@ -0,0 +1,69 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "page", + "id": "91f70932-ee63-47b5-9bc2-43e09b4cc9b0", + "created_time": "2021-06-12T16:36:00.000Z", + "last_edited_time": "2023-05-03T00:06:00.000Z", + "created_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "last_edited_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "cover": null, + "icon": { + "type": "external", + "external": { + "url": "https:\/\/www.notion.so\/icons\/chemistry_red.svg" + } + }, + "parent": { + "type": "workspace", + "workspace": true + }, + "archived": false, + "properties": { + "title": { + "id": "title", + "type": "title", + "title": [ + { + "type": "text", + "text": { + "content": "Testing Suite Content", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Testing Suite Content", + "href": null + } + ] + } + }, + "url": "https:\/\/www.notion.so\/Testing-Suite-Content-91f70932ee6347b59bc243e09b4cc9b0", + "public_url": null + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-pages-a652fac351cc4cc79f5b17eb702793ed_00000001.json b/tests/snapshots/resolve/get_v1-pages-a652fac351cc4cc79f5b17eb702793ed_00000001.json new file mode 100644 index 0000000..df4f764 --- /dev/null +++ b/tests/snapshots/resolve/get_v1-pages-a652fac351cc4cc79f5b17eb702793ed_00000001.json @@ -0,0 +1,69 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "page", + "id": "a652fac3-51cc-4cc7-9f5b-17eb702793ed", + "created_time": "2023-05-03T00:07:00.000Z", + "last_edited_time": "2023-06-09T16:38:00.000Z", + "created_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "last_edited_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "cover": null, + "icon": { + "type": "external", + "external": { + "url": "https:\/\/www.notion.so\/icons\/people_red.svg" + } + }, + "parent": { + "type": "page_id", + "page_id": "5ac149b9-d8f1-4d8d-ac05-facefc16ebf7" + }, + "archived": false, + "properties": { + "title": { + "id": "title", + "type": "title", + "title": [ + { + "type": "text", + "text": { + "content": "Page for Page Parent Resolve Testing", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Page for Page Parent Resolve Testing", + "href": null + } + ] + } + }, + "url": "https:\/\/www.notion.so\/Page-for-Page-Parent-Resolve-Testing-a652fac351cc4cc79f5b17eb702793ed", + "public_url": null + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-pages-cfb10a19-30cc-43a9-8db0-04c43f8cf315_00000001.json b/tests/snapshots/resolve/get_v1-pages-cfb10a19-30cc-43a9-8db0-04c43f8cf315_00000001.json new file mode 100644 index 0000000..cce5b53 --- /dev/null +++ b/tests/snapshots/resolve/get_v1-pages-cfb10a19-30cc-43a9-8db0-04c43f8cf315_00000001.json @@ -0,0 +1,74 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "page", + "id": "cfb10a19-30cc-43a9-8db0-04c43f8cf315", + "created_time": "2023-06-09T16:50:00.000Z", + "last_edited_time": "2023-06-09T16:51:00.000Z", + "created_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "last_edited_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "cover": null, + "icon": null, + "parent": { + "type": "database_id", + "database_id": "96f3b744-c9b5-48f2-849f-521aa7a83f27" + }, + "archived": false, + "properties": { + "Origin Relation Database ": { + "id": "RI]J", + "type": "relation", + "relation": [ + { + "id": "1c56e2ad-3d95-458c-935d-ae6d57769037" + } + ], + "has_more": false + }, + "Name": { + "id": "title", + "type": "title", + "title": [ + { + "type": "text", + "text": { + "content": "test 1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "test 1", + "href": null + } + ] + } + }, + "url": "https:\/\/www.notion.so\/test-1-cfb10a1930cc43a98db004c43f8cf315", + "public_url": null + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-pages-d946d011-966d-4b14-973f-dc5580f5b024_00000001.json b/tests/snapshots/resolve/get_v1-pages-d946d011-966d-4b14-973f-dc5580f5b024_00000001.json new file mode 100644 index 0000000..e71edcb --- /dev/null +++ b/tests/snapshots/resolve/get_v1-pages-d946d011-966d-4b14-973f-dc5580f5b024_00000001.json @@ -0,0 +1,69 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "page", + "id": "d946d011-966d-4b14-973f-dc5580f5b024", + "created_time": "2023-06-09T16:38:00.000Z", + "last_edited_time": "2023-06-09T16:39:00.000Z", + "created_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "last_edited_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "cover": null, + "icon": { + "type": "external", + "external": { + "url": "https:\/\/www.notion.so\/icons\/traffic-cone_red.svg" + } + }, + "parent": { + "type": "page_id", + "page_id": "5ac149b9-d8f1-4d8d-ac05-facefc16ebf7" + }, + "archived": false, + "properties": { + "title": { + "id": "title", + "type": "title", + "title": [ + { + "type": "text", + "text": { + "content": "Page for Block Parent Resolve Testing", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Page for Block Parent Resolve Testing", + "href": null + } + ] + } + }, + "url": "https:\/\/www.notion.so\/Page-for-Block-Parent-Resolve-Testing-d946d011966d4b14973fdc5580f5b024", + "public_url": null + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-pages-e226b338-0118-461d-a9ed-09c9a6f1e4e2_00000001.json b/tests/snapshots/resolve/get_v1-pages-e226b338-0118-461d-a9ed-09c9a6f1e4e2_00000001.json new file mode 100644 index 0000000..5e245fd --- /dev/null +++ b/tests/snapshots/resolve/get_v1-pages-e226b338-0118-461d-a9ed-09c9a6f1e4e2_00000001.json @@ -0,0 +1,74 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "page", + "id": "e226b338-0118-461d-a9ed-09c9a6f1e4e2", + "created_time": "2023-06-09T16:50:00.000Z", + "last_edited_time": "2023-06-09T16:51:00.000Z", + "created_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "last_edited_by": { + "object": "user", + "id": "04536682-603a-4531-a18f-4fa89fdfb4a8" + }, + "cover": null, + "icon": null, + "parent": { + "type": "database_id", + "database_id": "96f3b744-c9b5-48f2-849f-521aa7a83f27" + }, + "archived": false, + "properties": { + "Origin Relation Database ": { + "id": "RI]J", + "type": "relation", + "relation": [ + { + "id": "1c56e2ad-3d95-458c-935d-ae6d57769037" + } + ], + "has_more": false + }, + "Name": { + "id": "title", + "type": "title", + "title": [ + { + "type": "text", + "text": { + "content": "test 2", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "test 2", + "href": null + } + ] + } + }, + "url": "https:\/\/www.notion.so\/test-2-e226b3380118461da9ed09c9a6f1e4e2", + "public_url": null + } +} \ No newline at end of file diff --git a/tests/snapshots/resolve/get_v1-users-455aad58-7aec-4a39-8c0f-37cab3ca38f5_00000001.json b/tests/snapshots/resolve/get_v1-users-455aad58-7aec-4a39-8c0f-37cab3ca38f5_00000001.json new file mode 100644 index 0000000..0d2daf2 --- /dev/null +++ b/tests/snapshots/resolve/get_v1-users-455aad58-7aec-4a39-8c0f-37cab3ca38f5_00000001.json @@ -0,0 +1,26 @@ +{ + "header": { + "User-Agent": [ + "GuzzleHttp\/7" + ], + "Host": [ + "api.notion.com" + ], + "Notion-Version": [ + "2021-05-13" + ] + }, + "method": "get", + "status": 200, + "payload": null, + "data": { + "object": "user", + "id": "455aad58-7aec-4a39-8c0f-37cab3ca38f5", + "name": "TestUser for NotionForLaravel", + "avatar_url": null, + "type": "person", + "person": { + "email": "testuser@5amco.de" + } + } +} \ No newline at end of file