diff --git a/CHANGELOG.md b/CHANGELOG.md index 48cc9ef..49a0d0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,12 +106,41 @@ * Remove named parameters for `Children` class * Update documentation -## v1.2.0: -> Release date: 27/Jul/2021 -* Implement new endpoints - * Update page: https://developers.notion.com/reference/patch-page#archive-delete-a-page - * Create database: https://developers.notion.com/reference/create-a-database -* Add `Page` support for responses -* Add more colors for Text -* Add list of endpoints implemented on package -* Improve coverage \ No newline at end of file +## v2.0.0-beta1 +> Release date: 30/Aug/2021 +* 🐣 Add constructor for empty `Database`. +* 🐣 Add parameter `blocks` for `Children` constructor. +* πŸ— Remove deprecated code: + * `textSeparation` + * Parameter constructors for `Children`: + * `heading` + * `paragraph` + * `toDo` +* 🐣 Add suggestions on issue [#11](https://github.com/jonathangomz/notion_api/issues/11): + * Update exports to improve usage + * Add private folder (`src/`) +* 🐣 Add constructors with only single text content with default style for: + * `Paragraph`: `Paragraph.text('some text here...')` + * `ToDo`: `ToDo.text('some text here...', checked: true)` + * `Heading`: `Heading.text('some text here...', type: 2)` + * `BulletedListItem`: `BulletedListItem.text('some text here...')` + * `NumberedListItem`: `NumberedListItem.text('some text here...')` + * `Toggle`: `Toggle.text('some text here...', children: [])` +* 🐣 Add more constructors for `Heading` class: + * `one`: Heading with type 1 by default. + * `two`: Heading with type 2 by default. + * `three`: Heading with type 3 by default. +* 🐣 Add more constructors for `Text` class: + * `code`: Text with code style by default. + * `italic`: Text with italic style by default. + * `bold`: Text with bold style by default. + * `underline`: Text with underline style by default. + * `color`: Text with different color of default. +* 🐣 Add `list(List texts, String separator, String lastSeparator)`: + * **A static method** + * Generate a textual list of texts separated by comma (by default). + +## v2.0.0-beta2 +> Release date: 06/Aug/2021 +* πŸ— Add more suggestions on issue [#11](https://github.com/jonathangomz/notion_api/issues/11): + * Copy some terminologies from [`notion-sdk-js`](https://github.com/makenotion/notion-sdk-js) \ No newline at end of file diff --git a/README.md b/README.md index 4f035d9..ece170b 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,22 @@ See the [ROADMAP](ROADMAP.md) file to see what is coming next. - [API implemented](#api-implemented) - [Usage](#usage) - - [`NotionClient` class](#notionclient-class) + - [`Client` class](#client-class) - [Individual classes](#individual-classes) - [A few examples](#a-few-examples) - [Append blocks children](#append-blocks-children) - [Create a page](#create-a-page) - [Errors](#errors) - - [Create page with chidren](#create-page-with-chidren) + - [Create page with children](#create-page-with-children) - [Contributions](#contributions) - [Rules](#rules) - [Tests](#tests) - [Example:](#example) -- [Next release](#next-release) - - [v1.2.1:](#v121) +- [Releases](#releases) + - [Next](#next) + - [v2.0.0](#v200) + - [Last](#last) + - [v2.0.0-beta2](#v200-beta2) # API implemented | Endpoint | Avaliable | Notes @@ -27,10 +30,10 @@ See the [ROADMAP](ROADMAP.md) file to see what is coming next. | Retrieve a database | βœ… | | Query a database | πŸ— | Working on it | List databases | βœ… | -| Create a database | βœ… | Workin on more properties +| Create a database | βœ… | Working on more properties | Retrieve a page | βœ… | -| Create a page | βœ… | Workin on more properties -| Update a page | βœ… | Workin on more properties +| Create a page | βœ… | Working on more properties +| Update a page | βœ… | Working on more properties | Retrieve block children | βœ… | | Append block children | βœ… | | Retrieve a user | | @@ -41,10 +44,10 @@ See the [ROADMAP](ROADMAP.md) file to see what is coming next. # Usage **Important**: The methods return a `NotionResponse`. You can find how to use it in its [documentation][1]. -## `NotionClient` class -You only have to create a new instance of the `NotionClient` class and all the API requests will be available as class methods. +## `Client` class +You only have to create a new instance of the `Client` class and all the API requests will be available as class methods. ```dart -NotionClient notion = NotionClient(token: 'YOUR SECRET TOKEN FROM INTEGRATIONS PAGE'); +Client notion = Client(token: 'YOUR SECRET TOKEN FROM INTEGRATIONS PAGE'); ``` ## Individual classes @@ -53,12 +56,12 @@ You can also use individual request group class like `NotionPagesClient` or `Not **Example** ```dart // With main class -NotionClient notion = NotionClient(token: 'YOUR_TOKEN'); -notion.databases.fetchAll(); +Client notion = Client(token: 'YOUR_TOKEN'); +notion.databases.list(); // With individual class NotionDatabasesClient databases = NotionDatabasesClient(token: 'YOUR_TOKEN'); -databases.fetchAll(); +databases.list(); ``` ## A few examples @@ -70,7 +73,7 @@ _To see code to create the page above or see more examples [go here](https://git ### Append blocks children ```dart // Create children instance: -Children children = Children.withBlocks([ +Children children = Children(blocks: [ Heading(text: Text('Test')), Paragraph(texts: [ Text('Lorem ipsum (A)'), @@ -85,7 +88,7 @@ Children children = Children.withBlocks([ // Send the instance to Notion notion.blocks.append( - to: 'YOUR_BLOCK_ID', + block_id: 'YOUR_BLOCK_ID', children: children, ); ``` @@ -104,7 +107,7 @@ notion.pages.create(page); # Errors Some errors that I have encounter and still don't know how to solve because are errors that also occur on Postman are: -## Create page with chidren +## Create page with children When the parent is a page the error is: ```json "body failed validation: body.properties.title.type should be anΒ array, instead was `\"array\"`." @@ -146,27 +149,16 @@ TEST_BLOCK_ID=c8hac4bb32af48889228bf483d938e34 TEST_BLOCK_HEADING_ID=c8hac4bb32af48889228bf483d938e34 ``` -# Next release -## v1.2.1: -> Release date: 02/Aug/2021 -* Add constructors with only single text content with default style for: - * `Paragraph.text('some text here...')` - * `ToDo.text('some text here...', checked: true)` - * `Heading.text('some text here...', type: 2)` - * `BulletedListItem.text('some text here...')` - * `NumberedListItem.text('some text here...')` - * `Toggle.text('some text here...', children: [])` -* Add more constructors for `Heading` class: - * `one`: Heading with type 1 by default. - * `two`: Heading with type 2 by default. - * `three`: Heading with type 3 by default. -* Add more constructors for `Text` class: - * `code`: Text with code style by default. - * `italic`: Text with italic style by default. - * `bold`: Text with bold style by default. - * [**Opt**] `list`: List of words separated by comma (by default). - * Example: `Text.list()` will receive a list of Text and will be concatenated separated with comma by default. **It may be unnecessary**. Can help to make the code more readable. - * [**Opt**] `sep`: Text separator. - * Example: `Text.sep()`, by default, will insert " " in a list of `Text` instances, but it will be able to do more things that I don't know yet, hehe. **It may be unnecessary**. Can help to make the code more readable. +# Releases +## Next +### v2.0.0 +> Release date: 13/Aug/2021 +* πŸ”§ Fix any error on beta + +## Last +### v2.0.0-beta2 +> Release date: 06/Aug/2021 +* πŸ— Add more suggestions on issue [#11](https://github.com/jonathangomz/notion_api/issues/11): + * Copy some terminologies from [`notion-sdk-js`](https://github.com/makenotion/notion-sdk-js) [1]:https://pub.dev/documentation/notion_api/1.0.0-beta1/responses_notion_response/NotionResponse-class.html \ No newline at end of file diff --git a/ROADMAP.md b/ROADMAP.md index 9f48218..b687aec 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -3,36 +3,75 @@ ## More coming soon... If you have suggestions feel free to create an Issue or to create a PR with the feature. -## v1.3.0 -> Release date: 06/Aug/2021 -* Maybe fix errors creating page with children. I don't know if is an error with Notion API. -* Add query a database endpoint: +## v2.1.0 +||| +|:---------------|:------------| +| Release date | ? | +| Notion-Version | 2021-08-16 | +* 🐣 Add `Retrieve a block` endpoint: + * https://developers.notion.com/reference/retrieve-a-block +* 🐣 Add `Update a block` endpoint: + * https://developers.notion.com/reference/update-a-block +* 🐣 Add `Query a database` endpoint: * https://developers.notion.com/reference/post-database-query -* Add more properties for pages and databases: + +## v2.0.1 +||| +|:---------------|:------------| +| Release date | ? | +| Notion-Version | 2021-08-16 | +* πŸ”§ Maybe fix errors creating page with children. I don't know if is an error with Notion API. +* 🐣 Add more properties for pages and databases: * Page properties: https://developers.notion.com/reference/page#page-property-value - * Database properties: https://developers.notion.com/reference/database#database-property - -## v1.2.1: -> Release date: 02/Aug/2021 -* Add constructors with only single text content with default style for: - * `Paragraph.text('some text here...')` - * `ToDo.text('some text here...', checked: true)` - * `Heading.text('some text here...', type: 2)` - * `BulletedListItem.text('some text here...')` - * `NumberedListItem.text('some text here...')` - * `Toggle.text('some text here...', children: [])` -* Add more constructors for `Heading` class: - * `one`: Heading with type 1 by default. - * `two`: Heading with type 2 by default. - * `three`: Heading with type 3 by default. -* Add more constructors for `Text` class: + * Database properties: https://develop ers.notion.com/reference/database#database-property + +## v2.0.0 +||| +|:---------------|:------------| +| Release date | 31/Mar/2022 | +| Notion-Version | 2021-08-16 | +* **Full refactorization to all the project** + +## v2.0.0-beta2 βœ… +> Release date: 06/Aug/2021 +* πŸ— Add more suggestions on issue [#11](https://github.com/jonathangomz/notion_api/issues/11): + * Copy some terminologies from [`notion-sdk-js`](https://github.com/makenotion/notion-sdk-js) + +## v2.0.0-beta1 βœ… +> Release date: 30/Jun/2021 +* 🐣 Add constructor for empty `Database`. +* 🐣 Add parameter `blocks` for `Children` constructor. +* πŸ— Remove deprecated code: + * `textSeparation` + * Parameter constructors for `Children`: + * `heading` + * `paragraph` + * `toDo` +* πŸ— Add suggestions on issue [#11](https://github.com/jonathangomz/notion_api/issues/11): + * Update exports to improve usage + * Add private folder (`src/`) +* 🐣 Add constructors with only single text content with default style for: + * `Paragraph`: `Paragraph.text('some text here...')` + * `ToDo`: `ToDo.text('some text here...', checked: true)` + * `Heading`: `Heading.text('some text here...', type: 2)` + * `BulletedListItem`: `BulletedListItem.text('some text here...')` + * `NumberedListItem`: `NumberedListItem.text('some text here...')` + * `Toggle`: `Toggle.text('some text here...', children: [])` +* (**CANCELED**) ~~🐣 Add more constructors for `Heading` class:~~ + * ~~`one`: Heading with type 1 by default.~~ + * ~~`two`: Heading with type 2 by default.~~ + * ~~`three`: Heading with type 3 by default.~~ +* 🐣 Add more constructors for `Text` class: * `code`: Text with code style by default. * `italic`: Text with italic style by default. * `bold`: Text with bold style by default. - * [**Opt**] `list`: List of words separated by comma (by default). - * Example: `Text.list()` will receive a list of Text and will be concatenated separated with comma by default. **It may be unnecessary**. Can help to make the code more readable. - * [**Opt**] `sep`: Text separator. - * Example: `Text.sep()`, by default, will insert " " in a list of `Text` instances, but it will be able to do more things that I don't know yet, hehe. **It may be unnecessary**. Can help to make the code more readable. + * `underline`: Text with underline style by default. + * `color`: Text with different color of default. + * [**CANCELED**] ~~`sep`: Text separator.~~ + * ~~Example: `Text.sep()`, by default, will insert " " in a list of `Text` instances, but it will be able to do more things that I don't know yet, hehe. **It may be unnecessary**. Can help to make the code more readable.~~ +* 🐣 Add `list(List texts, String separator, String lastSeparator)`: + * **A static method** + * Generate a textual list of texts separated by comma (by default). ## v1.2.0: βœ… > Release date: 27/Jul/2021 diff --git a/example/example.md b/example/example.md index 23a0e01..b569fad 100644 --- a/example/example.md +++ b/example/example.md @@ -23,9 +23,9 @@ # Initialization ## Full instance -You only have to create a new instance of the `NotionClient` class and all the API requests will be available as class properties methods. +You only have to create a new instance of the `Client` class and all the API requests will be available as class properties methods. ```dart -NotionClient notion = NotionClient(token: 'YOUR SECRET TOKEN FROM INTEGRATIONS PAGE'); +Client notion = Client(auth: 'YOUR SECRET TOKEN FROM INTEGRATIONS PAGE'); ``` ## Individual classes @@ -34,12 +34,12 @@ You can also use individual request group class like `NotionPagesClient` or `Not **Example** ```dart // With main class -NotionClient notion = NotionClient(token: 'YOUR_TOKEN'); -notion.databases.fetchAll(); +Client notion = Client(auth: 'YOUR_TOKEN'); +notion.databases.list(); // With individual class -NotionDatabasesClient databases = NotionDatabasesClient(token: 'YOUR_TOKEN'); -databases.fetchAll(); +NotionDatabasesClient databases = NotionDatabasesClient(auth: 'YOUR_TOKEN'); +databases.list(); ``` # Pages @@ -74,7 +74,8 @@ If you want to update the content of the page itself the use the block children **Note:** If the parent is a database, the new property values in the properties parameter must conform to the parent database's property schema. ```dart -notion.pages.update('YOUR_PAGE_ID', +notion.pages.update( + page_id: 'YOUR_PAGE_ID', properties: Properties(map: { 'Description': RichTextProp(content: [ Text('New value for Description property'), @@ -89,7 +90,8 @@ Archive or delete a page is a subaction of update it according to the API refere You can archive or un-archive the page just by toggling the boolean value of the `archived` parameter. ```dart -notion.pages.update('YOUR_PAGE_ID', +notion.pages.update( + page_id: 'YOUR_PAGE_ID', archived: true, ); ``` @@ -97,7 +99,7 @@ _See more at https://developers.notion.com/reference/patch-page#archive-delete-a ## Retrieve a page ```dart -notion.pages.fetch('YOUR_PAGE_ID'); +notion.pages.retrieve(page_id: 'YOUR_PAGE_ID'); ``` _See more at https://developers.notion.com/reference/get-page_ @@ -106,13 +108,9 @@ _See more at https://developers.notion.com/reference/get-page_ You have to define the parent of the page. It can be: - `page` - `workspace` - -There is a constructor for each one (_see example below_) but the main constructor can be used as well but the `ParentType` will be required. - -**Note:** The `newDatabase` constructor is a temporary solution while the main constructor is deprecating their fields until it became the sustitute for this constructor. ```dart // With page parent -Page page = Database.newDatabase( +Database database = Database( parent: Parent.page(id: 'YOUR_PAGE_ID'), title: [ Text('Database from examples'), @@ -126,13 +124,13 @@ Page page = Database.newDatabase( }), ); -notion.databases.create(page); +notion.databases.create(database); ``` _See more at https://developers.notion.com/reference/create-a-database_ ## Retrieve a database ```dart -notion.databases.fetch('YOUR_DATABASE_ID'); +notion.databases.retrieve(database_id: 'YOUR_DATABASE_ID'); ``` _See more at https://developers.notion.com/reference/get-database_ @@ -143,14 +141,14 @@ _Parameters:_ - _startCursor: If supplied, this endpoint will return a page of results starting after the cursor provided. If not supplied, this endpoint will return the first page of results._ - _pageSize: The number of items from the full list desired in the response. Maximum: 100, otherwise will be ignored._ ```dart -notion.databases.fetchAll(); +notion.databases.list(); ``` _See more at https://developers.notion.com/reference/get-databases_ # Block children ## Retrieve block children ```dart -notion.blocks.fetch('YOUR_BLOCK_ID'); +notion.blocks.list(block_id: 'YOUR_BLOCK_ID'); ``` _See more at https://developers.notion.com/reference/get-block-children_ @@ -165,26 +163,7 @@ _Parameters:_ ### Heading & Paragraph **Code** ```dart -// Create children instance: -// * Deprecated way -// Children oldWay = Children( -// heading: Heading('Test'), -// paragraph: Paragraph( -// content: [ -// Text('Lorem ipsum (A)'), -// Text( -// 'Lorem ipsum (B)', -// annotations: TextAnnotations( -// bold: true, -// underline: true, -// color: ColorsTypes.orange, -// ), -// ), -// ], -// ), -//); -// -// * New way using `addAll()` +// Option A: using `addAll(List)` Children childrenA = Children().addAll([ Heading(text: Text('Test')), Paragraph(texts: [ @@ -202,7 +181,7 @@ Children childrenA = Children().addAll([ ]), ]); -// * New way using single `add()` +// Option B: using `add(Block)` Children childrenB = Children().add(Heading(text: Text('Test'))).add(Paragraph(texts: [ Text('Lorem ipsum (A)'), @@ -218,7 +197,7 @@ Children childrenB = ], )); -// * New way using `withBlocks()` constructor +// Option C: using `withBlocks(List)` constructor Children childrenC = Children.withBlocks([ Heading(text: Text('Test')), Paragraph(texts: [ @@ -236,10 +215,28 @@ Children childrenC = Children.withBlocks([ ]), ]); +// Option D (Recommended): using main constructor +Children childrenD = Children(blocks: [ + Heading(text: Text('Test')), + Paragraph(texts: [ + Text('Lorem ipsum (A)'), + Text( + 'Lorem ipsum (B)', + annotations: TextAnnotations( + bold: true, + underline: true, + color: ColorsTypes.Orange, + ), + ), + ], children: [ + Heading(text: Text('Subtitle'), type: 3), + ]), +]); + // Send the instance to Notion notion.blocks.append( - to: 'YOUR_BLOCK_ID', - children: childrenA, // or `childrenB` or `childrenC`, any of these will produce the same result. + block_id: 'YOUR_BLOCK_ID', + children: childrenA, // or `childrenB`, `childrenC` or `childrenD`, any of these will produce the same result. ); ``` @@ -250,27 +247,8 @@ notion.blocks.append( ### To do **Code** ```dart -// Create children instance: -// * Deprecated way -// Children children = -// Children( -// toDo: [ -// ToDo(text: Text('This is a todo item A')), -// ToDo( -// texts: [ -// Text('This is a todo item'), -// Text( -// 'B', -// annotations: TextAnnotations(bold: true), -// ), -// ], -// ), -// ], -// ); -// -// * New way Children children = - Children.withBlocks([ + Children(blocks: [ ToDo(text: Text('This is a todo item A')), ToDo( texts: [ @@ -290,7 +268,7 @@ Children children = // Send the instance to Notion notion.blocks.append( - to: 'YOUR_BLOCK_ID', + block_id: 'YOUR_BLOCK_ID', children: children, ); ``` @@ -303,7 +281,7 @@ notion.blocks.append( **Code** ```dart Children children = - Children.withBlocks([ + Children(blocks: [ Toggle( text: Text('This is a toggle block'), children: [ @@ -323,7 +301,7 @@ Children children = // Send the instance to Notion notion.blocks.append( - to: 'YOUR_BLOCK_ID', + block_id: 'YOUR_BLOCK_ID', children: children, ); ``` @@ -336,7 +314,7 @@ notion.blocks.append( **Code** ```dart Children children = - Children.withBlocks([ + Children(blocks: [ BulletedListItem(text: Text('This is a bulleted list item A')), BulletedListItem(text: Text('This is a bulleted list item B')), BulletedListItem( @@ -354,7 +332,7 @@ Children children = // Send the instance to Notion notion.blocks.append( - to: 'YOUR_BLOCK_ID', + block_id: 'YOUR_BLOCK_ID', children: children, ); ``` @@ -367,7 +345,7 @@ notion.blocks.append( **Code** ```dart Children children = - Children.withBlocks([ + Children(blocks: [ NumberedListItem(text: Text('This is a numbered list item A')), NumberedListItem(text: Text('This is a numbered list item B')), NumberedListItem( @@ -385,7 +363,7 @@ Children children = // Send the instance to Notion notion.blocks.append( - to: 'YOUR_BLOCK_ID', + block_id: 'YOUR_BLOCK_ID', children: children, ); ``` @@ -399,11 +377,11 @@ _See more at https://developers.notion.com/reference/patch-block-children_ [1]: https://developers.notion.com/reference/get-databases # Full example -Go to see the result https://jonathangomz.notion.site/notion_api-example-0893dd2cb38a413d90165cb810b3c019 +Go to see the result https://jonathangomz.notion.site/notion_api-example-81cb020a620b4db19f8cf85e82be3d53 ```dart // Initialize the main Notion client -final NotionClient notion = NotionClient(token: 'YOUR_SECRET'); +final Client notion = Client(auth: 'YOUR_SECRET'); // Create the instance of the page final Page page = Page( @@ -418,97 +396,103 @@ var newPage = await notion.pages.create(page); String newPageId = newPage.page!.id; // Create the instance of the content of the page -Children fullContent = Children.withBlocks([ - Heading(text: Text('This the title')), - Paragraph(texts: [ - Text( - 'Here you can write all the content of the paragraph but if you want to have another style for a single word you will have to do ', - ), - Text( - 'this. ', - annotations: TextAnnotations( - color: ColorsTypes.Green, - bold: true, - italic: true, - ), - ), - Text( - 'Then you can continue writing all your content. See that if you separate the paragraph to stylized some parts you have to take in count the spaces because the ', - ), - Text('textSeparator', annotations: TextAnnotations(code: true)), - Text( - ' will be deprecated. Maybe you will see this with extra spaces because the separator but soon will be remove.') - ], children: [ - Heading( - text: Text('This is a subtitle for the paragraph'), - type: 2, - ), +Children fullContent = Children( + blocks: [ + Heading(text: Text('Examples')), Paragraph(texts: [ Text( - 'You can also have children for some blocks like ', + 'Here you can write all the content of the paragraph but if you want to have another style for a single word you will have to do ', ), Text( - 'Paragraph', - annotations: TextAnnotations(code: true), + 'this. ', + annotations: TextAnnotations( + color: ColorsTypes.Green, + bold: true, + italic: true, + ), ), - Text(', '), Text( - 'ToDo', - annotations: TextAnnotations(code: true), + 'Then you can continue writing all your content. See that if you separate the paragraph to stylized some parts you have to take in count the spaces.', ), - Text(', '), - Text( - 'BulletedListItems', - annotations: TextAnnotations(code: true), + ], children: [ + Heading.text('Children subtitle', type: 2), + Paragraph(texts: [ + Text( + 'You can also have children for some blocks like ', + ), + ...Text.list(texts: [ + Text.code('Paragraph'), + Text.code('ToDo'), + Text.code('BulletedListItems'), + Text.code('NumberedListItems'), + ], lastSeparator: ' or '), + Text('.'), + ]), + Paragraph.text( + 'Also, if your paragraph will have the same style you can write all your text directly like this to avoid using a list.', ), - Text(' or '), - Text( - 'NumberedListItems', - annotations: TextAnnotations(code: true), + Paragraph( + texts: [ + Text('You can use styles for texts like: '), + ...Text.list(texts: [ + Text.color('green text', color: ColorsTypes.Green), + Text.color('blue text', color: ColorsTypes.Blue), + Text.color('red text', color: ColorsTypes.Red), + Text.color('purple text', color: ColorsTypes.Purple), + Text.underline('underline text'), + Text.code('code format text'), + Text.italic('italic text'), + Text('strikethrough text', + annotations: TextAnnotations(strikethrough: true)), + Text( + 'mix styles', + annotations: TextAnnotations( + bold: true, + italic: true, + underline: true, + color: ColorsTypes.Orange, + ), + ), + ], lastSeparator: ' or '), + Text('!') + ], ), - Text('.'), ]), - Paragraph( - text: Text( - 'Also, if your paragraph will have the same style you can write all your text directly like this to avoid using a list.', - ), - ), - ]), - Heading(text: Text('Blocks'), type: 2), - Heading(text: Text('ToDo'), type: 3), - ToDo(text: Text('Daily meeting'), checked: true), - ToDo(text: Text('Clean the house')), - ToDo(text: Text('Do the laundry')), - ToDo(text: Text('Call mom'), children: [ - Paragraph(texts: [ - Text('Note: ', annotations: TextAnnotations(bold: true)), - Text('Remember to call her before 20:00'), + Heading.text('Blocks', type: 2), + Heading.text('ToDo', type: 3), + ToDo.text('Daily meeting', checked: true), + ToDo.text('Clean the house'), + ToDo.text('Do the laundry'), + ToDo.text('Call mom', children: [ + Paragraph(texts: [ + Text.bold('Note: '), + Text('Remember to call her before 20:00'), + ]), ]), - ]), - Heading(text: Text('Lists'), type: 3), - BulletedListItem(text: Text('Milk')), - BulletedListItem(text: Text('Cereal')), - BulletedListItem(text: Text('Eggs')), - BulletedListItem(text: Text('Tortillas of course')), - Paragraph( - text: Text('The numbered list are ordered by default by notion.'), - ), - NumberedListItem(text: Text('Notion')), - NumberedListItem(text: Text('Keep by Google')), - NumberedListItem(text: Text('Evernote')), - Heading(text: Text('Toggle'), type: 3), - Toggle(text: Text('Toogle items'), children: [ - Paragraph( - text: Text( - 'Toogle items are blocks that can show or hide their children, and their children can be any other block.', - ), + Heading.text('Lists', type: 3), + BulletedListItem.text('Milk'), + BulletedListItem.text('Cereal'), + BulletedListItem.text('Eggs'), + BulletedListItem.text('Tortillas of course'), + Paragraph.text('The numbered list are ordered by default by notion.'), + NumberedListItem.text('Notion'), + NumberedListItem.text('Keep by Google'), + NumberedListItem.text('Evernote'), + Heading.text('Toggle', type: 3), + Toggle.text( + 'Toogle items', + children: [ + Paragraph.text( + 'Toogle items are blocks that can show or hide their children, and their children can be any other block.', + ), + ], ), - ]) -]); + ], +); // Append the content to the page var res = await notion.blocks.append( - to: newPageId, + block_id: newPageId, children: fullContent, ); ``` \ No newline at end of file diff --git a/lib/base_client.dart b/lib/base_client.dart deleted file mode 100644 index 4b8afe2..0000000 --- a/lib/base_client.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:notion_api/statics.dart'; - -abstract class BaseClient { - /// The API integration secret token. - String token; - - /// The API version. - String v; - - /// The API date version. - /// - /// It's not the same as the API version. - String dateVersion; - - /// The path of the requests group. - String path = ''; - - BaseClient({ - required String token, - String version: latestVersion, - String dateVersion: latestDateVersion, - }) : this.token = token, - this.v = version, - this.dateVersion = dateVersion; -} diff --git a/lib/notion.dart b/lib/notion.dart deleted file mode 100644 index a5b58d9..0000000 --- a/lib/notion.dart +++ /dev/null @@ -1,41 +0,0 @@ -/// A wrapper for the public beta Notion API to manage it like a Notion SDK package for dart. -/// -/// To see code examples you can go to https://pub.dev/packages/notion_api/example. -/// -/// Documentation for Notion API here https://developers.notion.com. -/// API Reference here https://developers.notion.com/reference/intro. -library notion_api; - -import 'package:notion_api/statics.dart'; - -import 'notion_pages.dart'; -import 'notion_blocks.dart'; -import 'notion_databases.dart'; - -/// A Notion API client. -class NotionClient { - /// The Notion API client for pages requests. - NotionPagesClient pages; - - /// The Notion API client for databases requests. - NotionBlockClient blocks; - - /// The Notion API client for databases requests. - NotionDatabasesClient databases; - - /// Main Notion client constructor. - /// - /// Require the [token] to authenticate the requests. Also can receive the API [version] where to make the calls, which is the latests by default (v1); and the [dateVersion] which is by default "2021-05-13" (the latest at 04/07/2021). - /// - /// This class is used as the main entry point for all clients. From the instances of this class any other client can be used. - NotionClient({ - required String token, - String version: latestVersion, - String dateVersion: latestDateVersion, - }) : this.pages = NotionPagesClient( - token: token, version: version, dateVersion: dateVersion), - this.databases = NotionDatabasesClient( - token: token, version: version, dateVersion: dateVersion), - this.blocks = NotionBlockClient( - token: token, version: version, dateVersion: dateVersion); -} diff --git a/lib/notion/general/rich_text.dart b/lib/notion/general/rich_text.dart deleted file mode 100644 index 47201b9..0000000 --- a/lib/notion/general/rich_text.dart +++ /dev/null @@ -1,164 +0,0 @@ -import 'types/notion_types.dart'; -import '../../utils/utils.dart'; - -/// A representation of the Rich Text Notion. -class Text { - String _type = 'text'; - - /// The text intself. - String text; - - /// The annotations of the text. - TextAnnotations? annotations = TextAnnotations(); - - /// The url of the text when the text is a link. - Uri? url; - - /// Main text constructor. - /// - /// Required the [text] itself. Also can receive the [annotations] and/or the [url] of the text. - Text(this.text, {this.annotations, this.url}); - - /// Create a new Text instance from json. - /// - /// Receive a [json] from where the information is extracted. - Text.fromJson(Map json) - : this.text = json['text']['content'] ?? '', - this.annotations = TextAnnotations.fromJson(json['annotations'] ?? {}), - this.url = json['href'] != null ? Uri.parse(json['href']) : null; - - /// Convert this to a json representation valid for the Notion API. - /// - ///_Deprecated:_ [textSeparator] will be removed and the separation will be by your own. This because that's the same way that `Text` & `RichText` works on Flutter. In this way you can add annotations for a part of a word instead of only full words or phrases. - /// - /// If a [textSeparator] is given, then it's value (by default a space) is append - /// at the end of the string to allow be at the same level of other Text objects without - /// being all together. For example: - /// - /// ```dart - /// // using default (space) - /// blocks.append( - /// to: 'some_block_id', - /// children: Children( - /// paragraph: Paragraph([ - /// Text('A'), - /// Text('B',) - /// ]))); - /// // append => "A B " - /// - /// // using custom - /// blocks.append( - /// to: 'some_block_id', - /// children: Children( - /// paragraph: Paragraph([ - /// Text('A'), - /// Text('B',) - /// ], - /// textSeparator: '-'))); - /// // append => "A-B-" - /// ``` - Map toJson( - {@Deprecated('Will not have replacement') String textSeparator: ''}) { - Map json = { - 'type': _type, - 'text': { - 'content': '$text$textSeparator', - }, - }; - - // Null values on fields can break the API call. - if (annotations != null) { - json['annotations'] = annotations?.toJson(); - } - - if (url != null) { - json['link'] = { - 'url': url.toString(), - }; - } - - return json; - } - - /// Map a list of texts from a [json] list with dynamics. - static List fromListJson(List json) { - List texts = []; - json.forEach((e) => texts.add(Text.fromJson(e))); - return texts; - } -} - -/// The text style. -class TextAnnotations { - /// A marker for bold text. - bool bold; - - /// A marker for italic text. - bool italic; - - /// A marker for strikethrough text. - bool strikethrough; - - /// A marker for underline text. - bool underline; - - /// A marker for code text. - bool code; - - /// The color of the text. Default by... by default hehe. - ColorsTypes color; - - /// The string value of the color type. - String get strColor => colorTypeToString(color); - - /// Main text annotations constructor. - /// - /// Can receive if the text will be [bold], [italic], [strikethrough], [underline] and/or [code]. Also the [color] of the text. - /// - /// Valid colors are defined by the Colors enum. By default the color type is... Default dah. - TextAnnotations({ - this.bold: false, - this.italic: false, - this.strikethrough: false, - this.underline: false, - this.code: false, - this.color: ColorsTypes.Default, - }); - - /// Create a new text annotation instance from json. - /// - /// Receive a [json] from where the information is extracted. - TextAnnotations.fromJson(Map json) - : this.bold = json['bold'] ?? false, - this.italic = json['italic'] ?? false, - this.strikethrough = json['strikethrough'] ?? false, - this.underline = json['underline'] ?? false, - this.code = json['code'] ?? false, - this.color = stringToColorType(json['color'] ?? ''); - - /// Convert this to a json representation valid for the Notion API. - Map toJson() { - Map json = { - 'color': strColor, - }; - - // Null & false values on fields can break the API call. - if (bold) { - json['bold'] = bold; - } - if (italic) { - json['italic'] = italic; - } - if (strikethrough) { - json['strikethrough'] = strikethrough; - } - if (underline) { - json['underline'] = underline; - } - if (code) { - json['code'] = code; - } - - return json; - } -} diff --git a/lib/notion/objects/database.dart b/lib/notion/objects/database.dart deleted file mode 100644 index f28a25f..0000000 --- a/lib/notion/objects/database.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'package:notion_api/notion/general/base_fields.dart'; -import 'package:notion_api/notion/general/lists/properties.dart'; -import 'package:notion_api/notion/general/property.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/utils/utils.dart'; - -import 'parent.dart'; - -/// A representation of the Databse Notion object. -class Database extends BaseFields { - /// The type of this object. Always Database for this. - @override - final ObjectTypes object = ObjectTypes.Database; - - /// The information of the page parent. - Parent parent = Parent.none(); - - /// The title of this database. - List title = []; - - /// The properties of this database. - Properties properties = Properties(); - - /// Main database constructor. - /// - /// **Important:** This main constructor will become like the `newDatabase`. If you need to create an instance with this parameters then use `withDefaults` constructor. - /// - /// Can receive the [title], the [createdTime], the [lastEditedTime] and the database [id]. - /// - Database({ - this.title: const [], - @deprecated String createdTime: '', - @deprecated String lastEditedTime: '', - @deprecated String id: '', - }) { - this.id = id; - this.setBaseProperties( - createdTime: createdTime, - lastEditedTime: lastEditedTime, - ); - } - - /// Database constructor with defaults parameters. - /// - /// Can receive the [parent] (none parent), [title] (empty), the [createdTime] (""), the [lastEditedTime] ("") and the database [id] ("") but every parameter is optional. - Database.withDefaults({ - this.parent: const Parent.none(), - this.title: const [], - String createdTime: '', - String lastEditedTime: '', - String id: '', - }) { - this.id = id; - this.setBaseProperties( - createdTime: createdTime, - lastEditedTime: lastEditedTime, - ); - } - - /// Database constructor with properties for new Database. - /// - /// **Important:** This is a temporary solution. In a future this constructor will be remove and the main constructor will be update to work like this one. - /// - /// Can receive the [parent] (required), the [title] (empty) and the [pagesColumnName] ("Name"). - /// - /// The [pagesColumnName] is the value of the page column header. - /// - Database.newDatabase({ - required this.parent, - this.title: const [], - String pagesColumnName: 'Name', - Properties? properties, - }) : this.properties = Properties(map: { - pagesColumnName: TitleProp(), - if (properties != null) ...properties.entries, - }) { - this.id = id; - this.setBaseProperties( - createdTime: createdTime, - lastEditedTime: lastEditedTime, - ); - } - - /// Map a new database instance from a [json] map. - factory Database.fromJson(Map json) => Database.withDefaults( - id: json['id'] ?? '', - parent: Parent.fromJson(json['parent'] ?? {}), - title: Text.fromListJson(json['title'] ?? []), - createdTime: json['created_time'] ?? '', - lastEditedTime: json['last_edited_time'] ?? '', - ).addPropertiesFromJson(json['properties'] ?? {}); - - /// Add a new database [property] with an specific [name]. - /// - /// Example: - /// ```dart - /// // For the title of a database - /// this.add( - /// name: 'title', - /// property: TitleProp(content: [ - /// Text('Title'), - /// ]), - /// ); - /// ``` - Database addProperty({required String name, required Property property}) { - this.properties.add(name: name, property: property); - return this; - } - - /// Add a group of properties from a [json] map and return this instance. - Database addPropertiesFromJson(Map json) { - this.properties.addAllFromJson(json); - return this; - } - - /// Convert this to a valid json representation for the Notion API. - Map toJson() => { - 'object': objectTypeToString(this.object), - 'title': title.map((e) => e.toJson()).toList(), - 'parent': parent.toJson(), - 'properties': properties.toJson(), - }; -} diff --git a/lib/notion_api.dart b/lib/notion_api.dart new file mode 100644 index 0000000..1e2c4c4 --- /dev/null +++ b/lib/notion_api.dart @@ -0,0 +1,51 @@ +/// A wrapper for the public beta Notion API to manage it like a Notion SDK package for dart. +/// +/// To see code examples you can go to https://pub.dev/packages/notion_api/example. +/// +/// Documentation for Notion API here https://developers.notion.com. +/// API Reference here https://developers.notion.com/reference/intro. +library notion_api; + +import 'package:notion_api/src/statics.dart'; + +import 'package:notion_api/src/notion_pages.dart'; +import 'package:notion_api/src/notion_blocks.dart'; +import 'package:notion_api/src/notion_databases.dart'; + +// export clients +export 'package:notion_api/src/notion_blocks.dart'; +export 'package:notion_api/src/notion_databases.dart'; +export 'package:notion_api/src/notion_pages.dart'; + +// export all other components +export 'package:notion_api/src/notion/exports.dart'; +export 'package:notion_api/src/responses/notion_response.dart'; +export 'package:notion_api/src/utils/utils.dart'; + +/// A Notion API client. +class Client { + /// The Notion API client for pages requests. + NotionPagesClient pages; + + /// The Notion API client for databases requests. + NotionBlockClient blocks; + + /// The Notion API client for databases requests. + NotionDatabasesClient databases; + + /// Main Notion client constructor. + /// + /// Require the [auth] to authenticate the requests. Also can receive the API [version] where to make the calls, which is the latests by default (v1); and the [dateVersion] which is by default "2021-05-13" (the latest at 04/07/2021). + /// + /// This class is used as the main entry point for all clients. From the instances of this class any other client can be used. + Client({ + required String auth, + String version = latestVersion, + String dateVersion = latestDateVersion, + }) : this.pages = NotionPagesClient( + auth: auth, version: version, dateVersion: dateVersion), + this.databases = NotionDatabasesClient( + auth: auth, version: version, dateVersion: dateVersion), + this.blocks = NotionBlockClient( + auth: auth, version: version, dateVersion: dateVersion); +} diff --git a/lib/notion_databases.dart b/lib/notion_databases.dart deleted file mode 100644 index 8763765..0000000 --- a/lib/notion_databases.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'dart:convert'; - -import 'package:http/http.dart' as http; -import 'package:notion_api/base_client.dart'; -import 'package:notion_api/notion/objects/database.dart'; - -import 'responses/notion_response.dart'; -import 'statics.dart'; - -/// A client for Notion API databases requests. -class NotionDatabasesClient extends BaseClient { - /// The path of the requests group. - @override - final String path = 'databases'; - - /// Main Notion database client constructor. - /// - /// Require the [token] to authenticate the requests, and the API [version] where to make the calls, which is the latests by default (v1). - NotionDatabasesClient({ - required String token, - String version: latestVersion, - String dateVersion: latestDateVersion, - }) : super(token: token, version: version, dateVersion: dateVersion); - - /// Retrieve the database with [id]. - /// - /// _See more at https://developers.notion.com/reference/get-database_ - Future fetch(String id) async { - http.Response res = - await http.get(Uri.https(host, '/$v/$path/$id'), headers: { - 'Authorization': 'Bearer $token', - 'Notion-Version': dateVersion, - }); - - return NotionResponse.fromResponse(res); - } - - /// Retrieve all databases. - /// - /// A [startCursor] can be defined to specify the page where to start. - /// Also a [pageSize] can be defined to limit the result. The max value is 100. - /// - /// _See more at https://developers.notion.com/reference/get-databases_ - Future fetchAll({String? startCursor, int? pageSize}) async { - Map query = {}; - if (startCursor != null) { - query['start_cursor'] = startCursor; - } - if (pageSize != null && pageSize >= 0 && pageSize <= 100) { - query['page_size'] = pageSize.toString(); - } - - http.Response res = - await http.get(Uri.https(host, '/$v/$path', query), headers: { - 'Authorization': 'Bearer $token', - 'Notion-Version': dateVersion, - }); - - return NotionResponse.fromResponse(res); - } - - /// Create a database. - /// - /// _See more at https://developers.notion.com/reference/create-a-database_ - Future create(Database database) async { - http.Response res = await http.post( - Uri.https(host, '/$v/$path'), - body: jsonEncode(database.toJson()), - headers: { - 'Authorization': 'Bearer $token', - 'Notion-Version': dateVersion, - 'Content-Type': 'application/json', - }, - ); - - return NotionResponse.fromResponse(res); - } -} diff --git a/lib/notion_pages.dart b/lib/notion_pages.dart deleted file mode 100644 index b14d597..0000000 --- a/lib/notion_pages.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'dart:convert'; - -import 'package:http/http.dart' as http; -import 'package:notion_api/base_client.dart'; -import 'package:notion_api/notion/general/lists/properties.dart'; - -import 'notion/objects/pages.dart'; -import 'responses/notion_response.dart'; -import 'statics.dart'; - -/// A client for Notion API pages requests. -class NotionPagesClient extends BaseClient { - /// The path of the requests group. - @override - final String path = 'pages'; - - /// Main Notion page client constructor. - /// - /// Require the [token] to authenticate the requests, and the API [version] where to make the calls, which is the latests by default (v1). - NotionPagesClient({ - required String token, - String version: latestVersion, - String dateVersion: latestDateVersion, - }) : super(token: token, version: version, dateVersion: dateVersion); - - /// Retrieve the page with [id]. - /// - /// _See more at https://developers.notion.com/reference/get-page_ - Future fetch(String id) async { - http.Response res = - await http.get(Uri.https(host, '/$v/$path/$id'), headers: { - 'Authorization': 'Bearer $token', - 'Notion-Version': dateVersion, - }); - - return NotionResponse.fromResponse(res); - } - - /// Create a new [page]. - /// - /// _See more at https://developers.notion.com/reference/post-page_ - Future create(Page page) async { - http.Response res = await http.post(Uri.https(host, '/$v/$path'), - body: jsonEncode(page.toJson()), - headers: { - 'Authorization': 'Bearer $token', - 'Content-Type': 'application/json; charset=UTF-8', - 'Notion-Version': dateVersion, - }); - - return NotionResponse.fromResponse(res); - } - - /// Update the [properties] of the page with an specified [id]. Can also mark the page as [archived]. - /// - /// The page should contain the property to update. - /// - /// Archive a page is the equivalent to delete it according to API reference. - /// - /// _See more at https://developers.notion.com/reference/patch-page_ - Future update( - String id, { - Properties? properties, - bool? archived, - }) async { - Properties _properties = properties ?? Properties.empty(); - http.Response res = await http.patch(Uri.https(host, '/$v/$path/$id'), - body: jsonEncode({ - 'properties': _properties.toJson(), - if (archived != null) 'archived': archived, - }), - headers: { - 'Authorization': 'Bearer $token', - 'Content-Type': 'application/json', - 'Notion-Version': dateVersion, - }); - - return NotionResponse.fromResponse(res); - } -} diff --git a/lib/src/notion/exports.dart b/lib/src/notion/exports.dart new file mode 100644 index 0000000..39b3676 --- /dev/null +++ b/lib/src/notion/exports.dart @@ -0,0 +1,5 @@ +export 'objects/exports.dart'; +export 'general/exports.dart'; +export 'lists/exports.dart'; + +export 'rich_text.dart'; diff --git a/lib/src/notion/general/exports.dart b/lib/src/notion/general/exports.dart new file mode 100644 index 0000000..f1dffe7 --- /dev/null +++ b/lib/src/notion/general/exports.dart @@ -0,0 +1,2 @@ +export 'property.dart'; +export 'notion_types.dart'; diff --git a/lib/notion/general/types/notion_types.dart b/lib/src/notion/general/notion_types.dart similarity index 100% rename from lib/notion/general/types/notion_types.dart rename to lib/src/notion/general/notion_types.dart diff --git a/lib/notion/general/property.dart b/lib/src/notion/general/property.dart similarity index 92% rename from lib/notion/general/property.dart rename to lib/src/notion/general/property.dart index e1cb3f5..7bea6ef 100644 --- a/lib/notion/general/property.dart +++ b/lib/src/notion/general/property.dart @@ -1,6 +1,6 @@ -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/utils/utils.dart'; +import '../../utils/utils.dart'; +import '../rich_text.dart'; +import 'notion_types.dart'; /// A representation of a single property for any Notion object. class Property { @@ -10,6 +10,9 @@ class Property { /// The property id. String? id; + /// The property name. + String? name; + /// The base getter for the content of any property. dynamic get value => false; @@ -30,8 +33,8 @@ class Property { /// Main property constructor. /// - /// Can receive the property [id]. - Property({this.id}); + /// Can receive the property [id] and [name]. + Property({this.id, this.name}); /// Constructor for empty property. Property.empty(); @@ -112,7 +115,7 @@ class TitleProp extends Property { /// Main title property constructor. /// /// Can receive a list ot texts as the title [content]. - TitleProp({this.content: const [], this.name}); + TitleProp({this.content = const [], this.name}); /// Create a new property instance from json. /// @@ -161,7 +164,7 @@ class RichTextProp extends Property { /// Main RichText constructor. /// /// Can receive the [content] as a list of texts. - RichTextProp({this.content: const []}); + RichTextProp({this.content = const []}); /// Create a new rich text instance from json. /// @@ -203,7 +206,10 @@ class MultiSelectProp extends Property { /// Main multi select constructor. /// /// Can receive the list6 of the options. - MultiSelectProp({this.options: const []}); + MultiSelectProp({ + this.options = const [], + String? name, + }) : super(name: name); MultiSelectProp.fromJson(Map json, {String? subfield}) : this.options = MultiSelectOption.fromListJson((subfield != null @@ -220,11 +226,11 @@ class MultiSelectProp extends Property { /// Convert this to a valid json representation for the Notion API. @override Map toJson() { - Map json = {'type': strType}; - - if (id != null) { - json['id'] = id; - } + Map json = { + 'type': strType, + if (id != null) 'id': id, + if (name != null) 'name': name, + }; json[strType] = {'options': options.map((e) => e.toJson()).toList()}; @@ -251,7 +257,7 @@ class MultiSelectOption { /// /// Required the [name] field to display a text for the option. Also can receive the [id] and the [color] of the option. MultiSelectOption( - {required this.name, this.id, this.color: ColorsTypes.Default}); + {required this.name, this.id, this.color = ColorsTypes.Default}); /// Create a new multi select instance from json. /// diff --git a/lib/notion/general/lists/children.dart b/lib/src/notion/lists/children.dart similarity index 70% rename from lib/notion/general/lists/children.dart rename to lib/src/notion/lists/children.dart index d8417bb..354d7c1 100644 --- a/lib/notion/general/lists/children.dart +++ b/lib/src/notion/lists/children.dart @@ -1,8 +1,5 @@ -import 'package:notion_api/notion/blocks/block.dart'; -import 'package:notion_api/notion/blocks/heading.dart'; -import 'package:notion_api/notion/blocks/paragraph.dart'; -import 'package:notion_api/notion/blocks/todo.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; +import '../general/notion_types.dart'; +import '../objects/block.dart'; /// A representation of the children Notion object. Also list of blocks. class Children { @@ -19,23 +16,9 @@ class Children { /// Main children constructor. /// - /// _Parameters deprecated:_ Do not use the parameters, soon will be removed. - /// - /// Can receive a single [heading], a single [paragraph], and a list of [toDo] blocks. If all three are included then the three fields are added to the blocks list adding first the [heading] field, then the [paragraph], and the list of [toDo] at the end. - Children({ - @deprecated Heading? heading, - @deprecated Paragraph? paragraph, - @deprecated List? toDo, - }) { - if (heading != null) { - _blocks.add(heading); - } - if (paragraph != null) { - _blocks.add(paragraph); - } - if (toDo != null) { - _blocks.addAll(toDo.toList()); - } + /// Can receive a list of [blocks]. + Children({List blocks = const []}) { + this._blocks.addAll(blocks); } /// Constructor that initialize a Children instance with a list of blocks. @@ -72,8 +55,8 @@ class Children { /// /// If all fields are included then all filters are applied as a chain following the next order: [exclude], [include], [onlyLeft], and the [id]. List filterBlocks({ - List exclude: const [], - List include: const [], + List exclude = const [], + List include = const [], BlockTypes? onlyLeft, String? id, }) { diff --git a/lib/src/notion/lists/exports.dart b/lib/src/notion/lists/exports.dart new file mode 100644 index 0000000..715d876 --- /dev/null +++ b/lib/src/notion/lists/exports.dart @@ -0,0 +1,3 @@ +export 'children.dart'; +export 'pagination.dart'; +export 'properties.dart'; diff --git a/lib/notion/general/lists/pagination.dart b/lib/src/notion/lists/pagination.dart similarity index 90% rename from lib/notion/general/lists/pagination.dart rename to lib/src/notion/lists/pagination.dart index 0e6a9e3..09a91a9 100644 --- a/lib/notion/general/lists/pagination.dart +++ b/lib/src/notion/lists/pagination.dart @@ -1,7 +1,7 @@ -import 'package:notion_api/notion/blocks/block.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/objects/database.dart'; -import 'package:notion_api/utils/utils.dart'; +import '../../utils/utils.dart'; +import '../general/notion_types.dart'; +import '../objects/block.dart'; +import '../objects/database.dart'; /// A representation of the pagination response from the Notion API. Also list of objects. class Pagination { @@ -47,8 +47,8 @@ class Pagination { /// Can receive the [nextCursor], if [hasMore] pages, if [isEmpty] and the corresponding list: [blocks], [databases] or [pages]. Pagination({ this.nextCursor, - this.hasMore: false, - this.isEmpty: false, + this.hasMore = false, + this.isEmpty = false, List? blocks, List? databases, }); @@ -90,8 +90,8 @@ class Pagination { /// /// If all fields are included then all filters are applied as a chain following the next order: [exclude], [include], [onlyLeft], and the [id]. List filterBlocks({ - List exclude: const [], - List include: const [], + List exclude = const [], + List include = const [], BlockTypes? onlyLeft, String? id, }) { @@ -118,8 +118,8 @@ class Pagination { /// /// If all fields are included then all filters are applied as a chain following the next order: [exclude], [include], and the [id]. List filterDatabases({ - List exclude: const [], - List include: const [], + List exclude = const [], + List include = const [], String? id, }) { List filetered = []; diff --git a/lib/notion/general/lists/properties.dart b/lib/src/notion/lists/properties.dart similarity index 84% rename from lib/notion/general/lists/properties.dart rename to lib/src/notion/lists/properties.dart index c636383..79d4a07 100644 --- a/lib/notion/general/lists/properties.dart +++ b/lib/src/notion/lists/properties.dart @@ -1,4 +1,4 @@ -import 'package:notion_api/notion/general/property.dart'; +import '../general/property.dart'; /// A representation of the properties for any Notion object. class Properties { @@ -13,10 +13,21 @@ class Properties { /// Main properties constructor. /// /// Can receive a properties [map]. - Properties({Map map: const {}}) { + Properties({Map map = const {}}) { this._map.addAll(map); } + /// Main properties constructor. + /// + /// Can receive a properties [map]. + Properties.forDatabase({ + required String title, + Map map = const {}, + }) { + _map[title] = TitleProp(); + _map.addAll(map); + } + /// Constructor for an empty instance. Properties.empty(); diff --git a/lib/src/notion/new/database/database_property.dart b/lib/src/notion/new/database/database_property.dart new file mode 100644 index 0000000..0ad9225 --- /dev/null +++ b/lib/src/notion/new/database/database_property.dart @@ -0,0 +1,282 @@ +import 'package:notion_api/src/notion/new/properties.dart'; +import 'package:notion_api/src/notion/new/property.dart'; +import 'package:notion_api/src/utils/utils.dart'; +import 'package:notion_api/src/notion/general/notion_types.dart'; + +class DatabaseProperties extends Properties { + /// Main properties constructor. + /// + /// Should receive the default [mainColumnName] for the permanent title column. Also can receive a properties [properties]. + DatabaseProperties({ + String? mainColumnName, + Map? properties, + }) : super(map: properties) { + if (mainColumnName != null) + super.add(name: mainColumnName, property: DatabaseProperty.Title()); + } + + /// Map a new properties instance from a [json] map. + DatabaseProperties.fromJson(Map json) { + json.entries.forEach((entry) { + entries[entry.key] = DatabaseProperty.fromJson(entry.value); + }); + } + + /// Convert this to a valid json representation for the Notion API. + Map toJson() { + Map json = {}; + + entries.entries.forEach((element) { + json[element.key] = element.value.toJson(); + }); + + return json; + } +} + +/// A representation of a single property for any Notion object. +class DatabaseProperty extends Property { + static TitleDbProp Title({ + String? name, + }) => + TitleDbProp(name: name); + + static RichTextDbProp RichText({ + String? name, + }) => + RichTextDbProp(name: name); + + static MultiSelectDbProp MultiSelect({ + List options = const [], + String? name, + }) => + MultiSelectDbProp(name: name, options: options); + + static CheckboxtDbProp Checkbox({ + String? name, + }) => + CheckboxtDbProp(name: name); + + /// The property name. + String name; + + /// Main property constructor. + /// + /// Can receive the property [id] and [name]. + DatabaseProperty({String? id, String? name}) + : name = name ?? '', + super(id: id); + + /// Get this as a valid partial json representation for the Notion API. + Map toJson() => { + 'type': strType, + if (id.isNotEmpty) 'id': id, + if (name.isNotEmpty) 'name': name, + }; + + /// Create a new Property instance from json. + /// + /// Receive a [json] from where the information is extracted. + factory DatabaseProperty.fromJson(Map json) { + PropertiesTypes type = extractPropertyType(json); + if (type == PropertiesTypes.Title) { + return TitleDbProp.fromJson(json); + } else if (type == PropertiesTypes.RichText) { + return RichTextDbProp.fromJson(json); + } else if (type == PropertiesTypes.MultiSelect) { + return MultiSelectDbProp.fromJson(json); + } else if (type == PropertiesTypes.Checkbox) { + return CheckboxtDbProp.fromJson(json); + } else { + return DatabaseProperty(); + } + } + + /// Returns the property as a Title property. + /// + /// Throws an exception if the property is not of that type or if it has not been implemented from the corresponding property. + TitleDbProp get asTitle { + if (type != PropertiesTypes.Title) + throw 'Cannot convert as Title because property is a ${type}'; + throw 'Not Implemented'; + } + + /// Returns the property as a RichText property. + /// + /// Throws an exception if the property is not of that type or if it has not been implemented from the corresponding property. + RichTextDbProp get asRichText { + if (type != PropertiesTypes.RichText) + throw 'Cannot convert as RichText because property is a ${type}'; + throw 'Not Implemented'; + } + + /// Returns the property as a MultiSelect property. + /// + /// Throws an exception if the property is not of that type or if it has not been implemented from the corresponding property. + MultiSelectDbProp get asMultiSelect { + if (type != PropertiesTypes.MultiSelect) + throw 'Cannot convert as MultiSelect because property is a ${type}'; + throw 'Not Implemented'; + } + + /// Returns the property as a Checkbox property. + /// + /// Throws an exception if the property is not of that type or if it has not been implemented from the corresponding property. + CheckboxtDbProp get asCheckbox { + if (type != PropertiesTypes.Checkbox) + throw 'Cannot convert as Checkbox because property is a ${type}'; + throw 'Not Implemented'; + } +} + +class TitleDbProp extends DatabaseProperty { + @override + final PropertiesTypes type = PropertiesTypes.Title; + + TitleDbProp({String? name}) : super(name: name); + + TitleDbProp.fromJson(Map json) + : super( + id: json['id'], + name: json['name'], + ); + + /// Override `asTitle` method from class parent to format specifically to Title. + @override + TitleDbProp get asTitle => this; + + /// Get this as a valid json representation for the Notion API. + @override + Map toJson() => { + 'type': strType, + if (id.isNotEmpty) 'id': id, + if (name.isNotEmpty) 'name': name, + strType: {}, + }; +} + +class RichTextDbProp extends DatabaseProperty { + @override + final PropertiesTypes type = PropertiesTypes.RichText; + + RichTextDbProp({String? name}) : super(name: name); + + RichTextDbProp.fromJson(Map json) + : super( + id: json['id'], + name: json['name'], + ); + + /// Override `asRichText` method from class parent to format specifically to RichText. + @override + RichTextDbProp get asRichText => this; + + /// Get this as a valid json representation for the Notion API. + @override + Map toJson() => { + 'type': strType, + if (id.isNotEmpty) 'id': id, + if (name.isNotEmpty) 'name': name, + strType: {}, + }; +} + +class CheckboxtDbProp extends DatabaseProperty { + @override + final PropertiesTypes type = PropertiesTypes.Checkbox; + + CheckboxtDbProp({String? name}) : super(name: name); + + CheckboxtDbProp.fromJson(Map json) + : super( + id: json['id'], + name: json['name'], + ); + + /// Override `asCheckbox` method from class parent to format specifically to Checkbox. + @override + CheckboxtDbProp get asCheckbox => this; + + /// Get this as a valid json representation for the Notion API. + @override + Map toJson() => { + 'type': strType, + if (id.isNotEmpty) 'id': id, + if (name.isNotEmpty) 'name': name, + strType: {}, + }; +} + +class MultiSelectDbProp extends DatabaseProperty { + @override + PropertiesTypes type = PropertiesTypes.MultiSelect; + + List options; + + MultiSelectDbProp({ + this.options = const [], + String? name, + }) : super(name: name); + + MultiSelectDbProp.fromJson(Map json) + : options = List.from( + json[propertyTypeToString(PropertiesTypes.MultiSelect)] + ['options']) + .map((e) => MultiSelectOptionDbProp.fromJson(e)) + .toList(), + super( + id: json['id'], + name: json['name'], + ); + + /// Override `asMultiSelect` method from class parent to format specifically to MultiSelect. + @override + MultiSelectDbProp get asMultiSelect => this; + + /// Get this as a valid json representation for the Notion API. + @override + Map toJson() => { + 'type': strType, + if (id.isNotEmpty) 'id': id, + if (name.isNotEmpty) 'name': name, + strType: { + 'options': + options.isNotEmpty ? options.map((e) => e.toJson()).toList() : [], + }, + }; +} + +class MultiSelectOptionDbProp { + /// The option name. + String name; + + /// The option id. + String? id; + + /// The option color. + ColorsTypes color; + + /// Main multi select option property constructor. + /// + /// Required the [name] field to display a text for the option. Also can receive the [id] and the [color] of the option. + MultiSelectOptionDbProp({ + required this.name, + this.id, + this.color = ColorsTypes.Default, + }); + + /// Create a new multi select instance from json. + /// + /// Receive a [json] from where the information is extracted. + MultiSelectOptionDbProp.fromJson(Map json) + : this.id = json['id'], + this.name = json['name'], + this.color = stringToColorType(json['color']); + + /// Convert this to a valid json representation for the Notion API. + Map toJson() => { + if (id != null) 'id': id, + 'name': name, + 'color': colorTypeToString(color), + }; +} diff --git a/lib/src/notion/new/properties.dart b/lib/src/notion/new/properties.dart new file mode 100644 index 0000000..f89afba --- /dev/null +++ b/lib/src/notion/new/properties.dart @@ -0,0 +1,52 @@ +class Properties { + final Map entries = {}; + + /// Returns true if the properties map IS empty + bool get isEmpty => this.entries.isEmpty; + + /// Main properties constructor. + /// + /// Can receive a properties [map]. + Properties({Map? map}) { + this.entries.addAll(map ?? {}); + } + + /// Add a new [property] with a specific [name]. + /// + /// If there is a current entry with that [name], the new [property] will overwrite the other value. + void add({required String name, required T property}) { + this.entries[name] = property; + } + + /// Remove the property with the specific [name] and return the deleted property. + /// + /// Throws an Exception is any property is found with the [name]. + T remove(String name) { + if (contains(name)) { + return entries.remove(name)!; + } else { + throw 'No property found with "$name" name'; + } + } + + /// Get the property with the specific [name]. + /// + /// Throws an Exception is any property is found with the [name]. + T getByName(String name) { + if (contains(name)) { + return entries[name]!; + } else { + throw 'No property found with "$name" name'; + } + } + + /// Returns true if the property with the specific [name] is contained. + bool contains(String name) { + return this.entries.containsKey(name); + } + + /// Convert this to a valid json representation for the Notion API. + Map toJson() { + throw 'Not implemented'; + } +} diff --git a/lib/src/notion/new/property.dart b/lib/src/notion/new/property.dart new file mode 100644 index 0000000..b8d907d --- /dev/null +++ b/lib/src/notion/new/property.dart @@ -0,0 +1,18 @@ +import 'package:notion_api/notion_api.dart'; + +/// A representation of a single property for any Notion object. +class Property { + /// The property type. + final PropertiesTypes type = PropertiesTypes.None; + + /// The property id. + String id; + + /// The string value for this property type. + String get strType => propertyTypeToString(type); + + /// Main property constructor. + /// + /// Can receive the property [id] and [name]. + Property({String? id}) : id = id ?? ''; +} diff --git a/lib/notion/blocks/block.dart b/lib/src/notion/objects/block.dart similarity index 83% rename from lib/notion/blocks/block.dart rename to lib/src/notion/objects/block.dart index 152f90f..5a13342 100644 --- a/lib/notion/blocks/block.dart +++ b/lib/src/notion/objects/block.dart @@ -1,9 +1,9 @@ -import 'package:notion_api/notion/general/base_fields.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/utils/utils.dart'; +import '../../utils/utils.dart'; +import '../general/notion_types.dart'; +import 'object.dart'; /// A base representation of any Notion block object. -class Block extends BaseFields { +class Block extends Object { /// The type of object. Always Block for this. @override final ObjectTypes object = ObjectTypes.Block; @@ -57,12 +57,12 @@ class Block extends BaseFields { /// /// Also can receive the [createdTime] and the [lastEditedTime] of the block in case that the information is filled from response. Block({ - this.id: '', - this.hasChildren: false, - this.jsonContent: const {}, - this.type: BlockTypes.None, - String createdTime: '', - String lastEditedTime: '', + this.id = '', + this.hasChildren = false, + this.jsonContent = const {}, + this.type = BlockTypes.None, + DateTime? createdTime, + DateTime? lastEditedTime, }) { this.setBaseProperties( createdTime: createdTime, @@ -79,8 +79,12 @@ class Block extends BaseFields { this.jsonContent = json['type'] != null ? json[json['type']] ?? {} : {}, this.type = stringToBlockType(json['type'] ?? ''), super( - createdTime: json['created_time'] ?? '', - lastEditedTime: json['last_edited_time'] ?? '', + createdTime: json['created_time'] != null + ? DateTime.parse(json['created_time']) + : null, + lastEditedTime: json['last_edited_time'] != null + ? DateTime.parse(json['last_edited_time']) + : null, ); /// Convert this to a valid json representation for the Notion API. diff --git a/lib/notion/blocks/bulleted_list_item.dart b/lib/src/notion/objects/blocks/bulleted_list_item.dart similarity index 68% rename from lib/notion/blocks/bulleted_list_item.dart rename to lib/src/notion/objects/blocks/bulleted_list_item.dart index 5288aad..faa90e9 100644 --- a/lib/notion/blocks/bulleted_list_item.dart +++ b/lib/src/notion/objects/blocks/bulleted_list_item.dart @@ -1,6 +1,6 @@ -import 'package:notion_api/notion/blocks/block.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; +import '../../general/notion_types.dart'; +import '../../rich_text.dart'; +import '../block.dart'; /// A representation of the Bulleted List Item Notion block object; class BulletedListItem extends Block { @@ -22,16 +22,28 @@ class BulletedListItem extends Block { /// Can receive a single [text] or a list of [texts]. If both are included also both fields are added to the heading content adding first the [text] field. Also can receive the [children] of the block. BulletedListItem({ Text? text, - List texts: const [], - List children: const [], + List texts = const [], + List children = const [], }) { - if (text != null) { - _content.add(text); - } - _content.addAll(texts); - _children.addAll(children); + this._content.addAll([ + if (text != null) text, + ...texts, + ]); + this._children.addAll(children); } + /// BulletedListItem constructor with a single `Text` instance as content. + /// + /// This constructor should receive the [content] as a single String. + /// + /// Can also receive the [annotations] of the single `Text` element and the [children] of this block. + BulletedListItem.text( + String content, { + TextAnnotations? annotations, + List children = const [], + }) : this._content = [Text(content, annotations: annotations)], + this._children = children; + /// Add a [text] to the rich text array and returns this instance. Also can receive the [annotations] of the text. BulletedListItem addText(String text, {TextAnnotations? annotations}) { this._content.add(Text(text, annotations: annotations)); diff --git a/lib/src/notion/objects/blocks/exports.dart b/lib/src/notion/objects/blocks/exports.dart new file mode 100644 index 0000000..5f57599 --- /dev/null +++ b/lib/src/notion/objects/blocks/exports.dart @@ -0,0 +1,6 @@ +export 'bulleted_list_item.dart'; +export 'heading.dart'; +export 'numbered_list_item.dart'; +export 'paragraph.dart'; +export 'todo.dart'; +export 'toggle.dart'; diff --git a/lib/notion/blocks/heading.dart b/lib/src/notion/objects/blocks/heading.dart similarity index 60% rename from lib/notion/blocks/heading.dart rename to lib/src/notion/objects/blocks/heading.dart index 03fa8dd..c859042 100644 --- a/lib/notion/blocks/heading.dart +++ b/lib/src/notion/objects/blocks/heading.dart @@ -1,6 +1,6 @@ -import 'package:notion_api/notion/blocks/block.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; +import '../../general/notion_types.dart'; +import '../../rich_text.dart'; +import '../block.dart'; /// A representation of the Heading Notion block object. class Heading extends Block { @@ -20,35 +20,26 @@ class Heading extends Block { /// Also can receive the [type] of the heading as an integer between 1 and 3. Set to 1 by default when no specified or when is out of the range declared before. Heading({ Text? text, - List? texts, - int type: 1, - }) { - if (text != null) { - _content.add(text); - } - if (texts != null) { - _content.addAll(texts); - } - - switch (type) { - case 3: - this.type = BlockTypes.H3; - break; - case 2: - this.type = BlockTypes.H2; - break; - case 1: - default: - this.type = BlockTypes.H1; - } + List texts = const [], + int type = 1, + }) : this.type = headingTypeFromInt(type) { + this._content.addAll([ + if (text != null) text, + ...texts, + ]); } - /// Add a new [text] to the paragraph content and returns this instance. - @Deprecated('Use `addText(Block)` instead') - Heading add(Text text) { - this._content.add(text); - return this; - } + /// Heading constructor with a single `Text` instance as content. + /// + /// This constructor should receive the [content] as a single String. + /// + /// Can also receive the [annotations] of the single `Text` element and the [children] of this block. + Heading.text( + String content, { + TextAnnotations? annotations, + int type = 1, + }) : this._content = [Text(content, annotations: annotations)], + this.type = headingTypeFromInt(type); /// Add a [text] to the rich text array and returns this instance. Also can receive the [annotations] of the text. Heading addText(String text, {TextAnnotations? annotations}) { @@ -65,4 +56,16 @@ class Heading extends Block { 'text': _content.map((e) => e.toJson()).toList(), }, }; + + static BlockTypes headingTypeFromInt(int type) { + switch (type) { + case 3: + return BlockTypes.H3; + case 2: + return BlockTypes.H2; + case 1: + default: + return BlockTypes.H1; + } + } } diff --git a/lib/notion/blocks/numbered_list_item.dart b/lib/src/notion/objects/blocks/numbered_list_item.dart similarity index 68% rename from lib/notion/blocks/numbered_list_item.dart rename to lib/src/notion/objects/blocks/numbered_list_item.dart index 7dc93c8..f5aa2c6 100644 --- a/lib/notion/blocks/numbered_list_item.dart +++ b/lib/src/notion/objects/blocks/numbered_list_item.dart @@ -1,6 +1,6 @@ -import 'package:notion_api/notion/blocks/block.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; +import '../../general/notion_types.dart'; +import '../../rich_text.dart'; +import '../block.dart'; /// A representation of the Bulleted List Item Notion block object; class NumberedListItem extends Block { @@ -22,16 +22,28 @@ class NumberedListItem extends Block { /// Can receive a single [text] or a list of [texts]. If both are included also both fields are added to the heading content adding first the [text] field. Also can receive the [children] of the block. NumberedListItem({ Text? text, - List texts: const [], - List children: const [], + List texts = const [], + List children = const [], }) { - if (text != null) { - _content.add(text); - } - _content.addAll(texts); - _children.addAll(children); + this._content.addAll([ + if (text != null) text, + ...texts, + ]); + this._children.addAll(children); } + /// NumberedListItem constructor with a single `Text` instance as content. + /// + /// This constructor should receive the [content] as a single String. + /// + /// Can also receive the [annotations] of the single `Text` element and the [children] of this block. + NumberedListItem.text( + String content, { + TextAnnotations? annotations, + List children = const [], + }) : this._content = [Text(content, annotations: annotations)], + this._children = children; + /// Add a [text] to the rich text array and returns this instance. Also can receive the [annotations] of the text. NumberedListItem addText(String text, {TextAnnotations? annotations}) { this._content.add(Text(text, annotations: annotations)); diff --git a/lib/notion/blocks/paragraph.dart b/lib/src/notion/objects/blocks/paragraph.dart similarity index 51% rename from lib/notion/blocks/paragraph.dart rename to lib/src/notion/objects/blocks/paragraph.dart index a3c0e97..6fce98b 100644 --- a/lib/notion/blocks/paragraph.dart +++ b/lib/src/notion/objects/blocks/paragraph.dart @@ -1,6 +1,6 @@ -import 'package:notion_api/notion/blocks/block.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; +import '../../general/notion_types.dart'; +import '../../rich_text.dart'; +import '../block.dart'; /// A representation of the Paragraph Notion block object. class Paragraph extends Block { @@ -11,14 +11,6 @@ class Paragraph extends Block { List _content = []; List _children = []; - /// The separator for the Text objects. - @Deprecated('Text separation will be by your own') - String textSeparator; - - /// The content of this block. - @Deprecated('Instead use `content`') - List get texts => _content.toList(); - /// The content of this block. List get content => _content.toList(); @@ -28,29 +20,29 @@ class Paragraph extends Block { /// Main paragraph constructor. /// /// Can receive a single [text] or a list of [texts]. If both are included also both fields are added to the heading content adding first the [text] field. Also can receive the [children] of the block. - /// - /// _Deprecated:_ [textSeparator] will be removed and the separation will be by your own. This because that's the same way that `Text` & `RichText` works on Flutter. In this way you can add annotations for a part of a word instead of only full words or phrases. - /// - /// Also a [textSeparator] can be anexed to separate the texts on the json generated using the `toJson()` function. The separator is used because when the text is displayed is all together without any kind of separation and adding the separator that behavior is avoided. By default the [textSeparator] is an space (" "). Paragraph({ Text? text, - List texts: const [], - List children: const [], - @deprecated this.textSeparator: ' ', + List texts = const [], + List children = const [], }) { - if (text != null) { - this._content.add(text); - } - this._content.addAll(texts); + this._content.addAll([ + if (text != null) text, + ...texts, + ]); this._children.addAll(children); } - /// Add a new [text] to the paragraph content and returns this instance. - @Deprecated('Use `addText(Block)` instead') - Paragraph add(Text text) { - this._content.add(text); - return this; - } + /// Paragraph constructor with a single `Text` instance as content. + /// + /// This constructor should receive the [content] as a single String. + /// + /// Can also receive the [annotations] of the single `Text` element and the [children] of this block. + Paragraph.text( + String content, { + TextAnnotations? annotations, + List children = const [], + }) : this._content = [Text(content, annotations: annotations)], + this._children = children; /// Add a [text] to the rich text array and returns this instance. Also can receive the [annotations] of the text. Paragraph addText(String text, {TextAnnotations? annotations}) { @@ -76,9 +68,7 @@ class Paragraph extends Block { 'object': strObject, 'type': strType, strType: { - 'text': _content - .map((e) => e.toJson(textSeparator: textSeparator)) - .toList(), + 'text': _content.map((e) => e.toJson()).toList(), 'children': _children.map((e) => e.toJson()).toList(), } }; diff --git a/lib/notion/blocks/todo.dart b/lib/src/notion/objects/blocks/todo.dart similarity index 59% rename from lib/notion/blocks/todo.dart rename to lib/src/notion/objects/blocks/todo.dart index 056696e..fed5df2 100644 --- a/lib/notion/blocks/todo.dart +++ b/lib/src/notion/objects/blocks/todo.dart @@ -1,6 +1,6 @@ -import 'package:notion_api/notion/blocks/block.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; +import '../../general/notion_types.dart'; +import '../../rich_text.dart'; +import '../block.dart'; /// A representation of the Paragraph Notion block object. class ToDo extends Block { @@ -11,10 +11,6 @@ class ToDo extends Block { List _content = []; List _children = []; - /// The separator for the Text objects. - @Deprecated('Text separation will be by your own') - String textSeparator; - /// The checked value. bool checked; @@ -29,24 +25,32 @@ class ToDo extends Block { /// Can receive a single [text] or a list of [texts]. If both are included also both fields are added to the heading content adding first the [text] field. Also can receive the [children] of the block. /// /// The [checked] field define if the To do option is marked as done. By default is false. - /// - /// _Deprecated:_ [textSeparator] will be removed and the separation will be by your own. This because that's the same way that `Text` & `RichText` works on Flutter. In this way you can add annotations for a part of a word instead of only full words or phrases. - /// - /// Also a [textSeparator] can be anexed to separate the texts on the json generated using the `toJson()` function. The separator is used because when the text is displayed is all together without any kind of separation and adding the separator that behavior is avoided. By default the [textSeparator] is an space (" "). ToDo({ Text? text, - List texts: const [], - List children: const [], - this.checked: false, - @deprecated this.textSeparator: ' ', + List texts = const [], + List children = const [], + this.checked = false, }) { - if (text != null) { - this._content.add(text); - } - this._content.addAll(texts); + this._content.addAll([ + if (text != null) text, + ...texts, + ]); this._children.addAll(children); } + /// ToDo constructor with a single `Text` instance as content. + /// + /// This constructor should receive the [content] as a single String. + /// + /// Can also receive the [annotations] of the single `Text` element and the [children] of this block. + ToDo.text( + String content, { + TextAnnotations? annotations, + List children = const [], + this.checked = false, + }) : this._content = [Text(content, annotations: annotations)], + this._children = children; + // TODO: A function that create an instance of ToDo (or Paragraph or Heading) from a Block. // // factory ToDo.fromBlock(Block block) { @@ -56,13 +60,6 @@ class ToDo extends Block { // return todo; // } - /// Add a new [text] to the paragraph content and returns this instance. - @Deprecated('Use `addText(Block)` instead') - ToDo add(Text text) { - this._content.add(text); - return this; - } - /// Add a [text] to the rich text array and returns this instance. Also can receive the [annotations] of the text. ToDo addText(String text, {TextAnnotations? annotations}) { this._content.add(Text(text, annotations: annotations)); @@ -87,9 +84,7 @@ class ToDo extends Block { 'object': strObject, 'type': strType, strType: { - 'text': _content - .map((e) => e.toJson(textSeparator: textSeparator)) - .toList(), + 'text': _content.map((e) => e.toJson()).toList(), 'children': _children.map((e) => e.toJson()).toList(), 'checked': checked, } diff --git a/lib/notion/blocks/toggle.dart b/lib/src/notion/objects/blocks/toggle.dart similarity index 68% rename from lib/notion/blocks/toggle.dart rename to lib/src/notion/objects/blocks/toggle.dart index db15fd1..7ca32f0 100644 --- a/lib/notion/blocks/toggle.dart +++ b/lib/src/notion/objects/blocks/toggle.dart @@ -1,6 +1,6 @@ -import 'package:notion_api/notion/blocks/block.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; +import '../../general/notion_types.dart'; +import '../../rich_text.dart'; +import '../block.dart'; /// A representation of the Bulleted List Item Notion block object; class Toggle extends Block { @@ -22,16 +22,28 @@ class Toggle extends Block { /// Can receive a single [text] or a list of [texts]. If both are included also both fields are added to the heading content adding first the [text] field. Also can receive the [children] of the block. Toggle({ Text? text, - List texts: const [], - List children: const [], + List texts = const [], + List children = const [], }) { - if (text != null) { - _content.add(text); - } - _content.addAll(texts); - _children.addAll(children); + this._content.addAll([ + if (text != null) text, + ...texts, + ]); + this._children.addAll(children); } + /// Toggle constructor with a single `Text` instance as content. + /// + /// This constructor should receive the [content] as a single String. + /// + /// Can also receive the [annotations] of the single `Text` element and the [children] of this block. + Toggle.text( + String content, { + TextAnnotations? annotations, + List children = const [], + }) : this._content = [Text(content, annotations: annotations)], + this._children = children; + /// Add a [text] to the rich text array and returns this instance. Also can receive the [annotations] of the text. Toggle addText(String text, {TextAnnotations? annotations}) { this._content.add(Text(text, annotations: annotations)); diff --git a/lib/src/notion/objects/database.dart b/lib/src/notion/objects/database.dart new file mode 100644 index 0000000..e563c75 --- /dev/null +++ b/lib/src/notion/objects/database.dart @@ -0,0 +1,137 @@ +import 'package:notion_api/src/notion/new/database/database_property.dart'; + +import '../../utils/utils.dart'; +import '../general/exports.dart'; +import '../rich_text.dart'; +import 'object.dart'; +import 'parent.dart'; + +/// A representation of the Databse Notion object. +class Database extends Object { + /// The type of this object. Always Database for this. + @override + final ObjectTypes object = ObjectTypes.Database; + + /// The title of this database. + final List _title = []; + + /// The properties of this database. + DatabaseProperties properties; + + /// The information of the page parent. + Parent parent; + + /// The URL of the Notion datanase. + String url; + + String get title => _title.first.text; + + /// Map a new database instance for a new request. + /// + /// Receive the database [parent] and [properties], and also con receive the database [title] but is optional (by default will set to "Untitled"). + /// + /// We recommend to use `Properties.forDatabase` on the [properties] field to be sure that any required property is missing, like the Title property. + Database({ + required this.parent, + required this.properties, + String? title, + }) : url = '' { + if (parent.type != ParentType.Page) throw 'The parent should be a page'; + if (title != null) _title.add(RichText(title)); + } + + /// Main database constructor. + /// + /// Can receive all the available properties for a database which are the [id], [createdTime], [lastEditedTime], [title], [properties], [parent] and [url]. + Database.constructor({ + // Object properties + required String id, + DateTime? createdTime, + DateTime? lastEditedTime, + // Datebase properties + required String title, + // TODO: Add icon & cover + required this.properties, + required this.parent, + required this.url, + }) : super( + id: id, + createdTime: createdTime, + lastEditedTime: lastEditedTime, + ) { + _title.add(RichText(title)); + } + + /// Database constructor with defaults parameters. + /// + /// Can receive the database [parent], [title] and the [mainColumnName] but in a simplified way. + /// + /// The [title] parameter is a string that will be the content at the only `RichtText` element in the `title` attribute. And the [mainColumnName] is the name of the default title column for the database that is mandatory. + Database.simple({ + required this.parent, + required String mainColumnName, + String? title, + }) : url = '', + properties = DatabaseProperties(mainColumnName: mainColumnName) { + if (title != null) _title.add(RichText(title)); + } + + /// Map a new database instance from a [json] map. + factory Database.fromJson(Map json) { + throwIfAnyNull(json, ['id', 'properties', 'parent', 'url']); + + Database database = Database( + properties: DatabaseProperties.fromJson(json['properties']), + parent: Parent.fromJson(json['parent']), + ); + + database.id = json['id']; + database.url = json['url']; + database._title.addAll(RichText.fromListJson(json['title'] ?? [])); + + if (json['last_edited_time'] != null) { + database.lastEditedTime = DateTime.parse(json['last_edited_time']); + } + + if (json['created_time'] != null) { + database.createdTime = DateTime.parse(json['created_time']); + } + + return database; + } + + /// Add a new database [property] with an specific [name]. + /// + /// Example: + /// ```dart + /// // For the title of a database + /// this.add( + /// name: 'title', + /// property: TitleProp(content: [ + /// Text('Title'), + /// ]), + /// ); + /// ``` + void addProperty({required String name, required DatabaseProperty property}) { + properties.add(name: name, property: property); + } + + /// Convert this to a valid json representation for the Notion API. + Map toRequestJson() => { + 'parent': parent.toJson(), + 'title': _title.map((richText) => richText.toJson()).toList(), + 'properties': properties.toJson(), + }; + + /// Convert this to a valid json representation for the Notion API response. + Map toJson() => { + 'object': objectTypeToString(object), + 'id': id, + 'created_time': createdTime, + 'last_edited_by': lastEditedTime, + 'title': _title.map((e) => e.toJson()).toList(), + 'parent': parent.toJson(), + 'properties': properties.toJson(), + 'url': url, + }; +} diff --git a/lib/src/notion/objects/exports.dart b/lib/src/notion/objects/exports.dart new file mode 100644 index 0000000..09cc3a3 --- /dev/null +++ b/lib/src/notion/objects/exports.dart @@ -0,0 +1,6 @@ +export 'database.dart'; +export 'pages.dart'; +export 'parent.dart'; +export 'block.dart'; + +export 'blocks/exports.dart'; diff --git a/lib/notion/general/base_fields.dart b/lib/src/notion/objects/object.dart similarity index 61% rename from lib/notion/general/base_fields.dart rename to lib/src/notion/objects/object.dart index 24fed83..7798959 100644 --- a/lib/notion/general/base_fields.dart +++ b/lib/src/notion/objects/object.dart @@ -1,8 +1,8 @@ -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/utils/utils.dart'; +import '../../utils/utils.dart'; +import '../general/notion_types.dart'; /// A base representation of the base properties of almost any Notion object. -class BaseFields { +class Object { /// The object type. ObjectTypes object; @@ -10,10 +10,10 @@ class BaseFields { String id; /// The creation time of the object. - String createdTime; + DateTime? createdTime; /// The last edited time of the object. - String lastEditedTime; + DateTime? lastEditedTime; /// The string value of the object type. String get strObject => objectTypeToString(object); @@ -23,26 +23,30 @@ class BaseFields { /// Can receive the [object], the [id], the [createdTime] and the [lastEditedTime] of the object. /// /// **Note:** This class is mainly (if no only) used by extending it. - BaseFields({ - this.object: ObjectTypes.Object, - this.id: '', - this.createdTime: '', - this.lastEditedTime: '', + Object({ + this.object = ObjectTypes.Object, + this.id = '', + DateTime? this.createdTime, + DateTime? this.lastEditedTime, }); /// Map the properties from a [json] map. - BaseFields.fromJson(Map json) + Object.fromJson(Map json) : this.object = stringToObjectType(json['object']), this.id = json['id'] ?? '', - this.createdTime = json['created_time'] ?? '', - this.lastEditedTime = json['last_edited_time'] ?? ''; + this.createdTime = json['created_time'] != null + ? DateTime.parse(json['created_time']) + : null, + this.lastEditedTime = json['last_edited_time'] != null + ? DateTime.parse(json['last_edited_time']) + : null; /// Set the [createdTime] and the [lastEditedTime] properties. /// /// This function is used to set the base properties from the constructor of the class in which it is inherited. void setBaseProperties({ - required String createdTime, - required String lastEditedTime, + DateTime? createdTime, + DateTime? lastEditedTime, }) { this.createdTime = createdTime; this.lastEditedTime = lastEditedTime; diff --git a/lib/notion/objects/pages.dart b/lib/src/notion/objects/pages.dart similarity index 78% rename from lib/notion/objects/pages.dart rename to lib/src/notion/objects/pages.dart index 2f967f3..0954af7 100644 --- a/lib/notion/objects/pages.dart +++ b/lib/src/notion/objects/pages.dart @@ -1,14 +1,11 @@ -import 'package:notion_api/notion/general/base_fields.dart'; -import 'package:notion_api/notion/general/lists/properties.dart'; -import 'package:notion_api/notion/general/property.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/general/lists/children.dart'; - +import '../general/exports.dart'; +import '../lists/exports.dart'; +import '../rich_text.dart'; +import 'object.dart'; import 'parent.dart'; /// A representation of the Page Notion object. -class Page extends BaseFields { +class Page extends Object { /// The type of this object. Always Page for this. @override final ObjectTypes object = ObjectTypes.Page; @@ -35,9 +32,9 @@ class Page extends BaseFields { /// for when a new page is created. Page({ required this.parent, - this.archived: false, + this.archived = false, this.children, - String id: '', + String id = '', Text? title, }) { this.id = id; @@ -62,8 +59,12 @@ class Page extends BaseFields { archived: json['archived'] ?? false, ).addPropertiesFromJson(json['properties'] ?? {}); page.setBaseProperties( - createdTime: json['created_time'] ?? '', - lastEditedTime: json['last_edited_time'] ?? ''); + createdTime: json['created_time'] != null + ? DateTime.parse(json['created_time']) + : null, + lastEditedTime: json['last_edited_time'] != null + ? DateTime.parse(json['last_edited_time']) + : null); return page; } @@ -90,7 +91,7 @@ class Page extends BaseFields { } /// Convert this to a json representation valid for the Notion API. - Map toJson({bool isResponse: false}) { + Map toJson({bool isResponse = false}) { Map json = { 'parent': this.parent.toJson(), 'properties': this.properties.toJson(), @@ -100,8 +101,10 @@ class Page extends BaseFields { if (isResponse) { json['object'] = strObject; json['id'] = id; - json['created_time'] = createdTime; - json['last_edited_time'] = lastEditedTime; + if (createdTime != null) + json['created_time'] = createdTime!.toIso8601String(); + if (lastEditedTime != null) + json['last_edited_time'] = lastEditedTime!.toIso8601String(); json['archived'] = archived; } diff --git a/lib/notion/objects/parent.dart b/lib/src/notion/objects/parent.dart similarity index 93% rename from lib/notion/objects/parent.dart rename to lib/src/notion/objects/parent.dart index 562b09c..ca3e516 100644 --- a/lib/notion/objects/parent.dart +++ b/lib/src/notion/objects/parent.dart @@ -1,5 +1,5 @@ -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/utils/utils.dart'; +import '../../utils/utils.dart'; +import '../general/notion_types.dart'; /// A representation of the parent json field for the Notion API. class Parent { diff --git a/lib/src/notion/rich_text.dart b/lib/src/notion/rich_text.dart new file mode 100644 index 0000000..6b558c0 --- /dev/null +++ b/lib/src/notion/rich_text.dart @@ -0,0 +1,471 @@ +import 'general/notion_types.dart'; +import '../utils/utils.dart'; + +/// A representation of the Rich Text Notion. +class Text { + String _type = 'text'; + + /// The text intself. + String text; + + /// The annotations of the text. + TextAnnotations? annotations = TextAnnotations(); + + /// The url of the text when the text is a link. + Uri? url; + + /// Main text constructor. + /// + /// Required the [text] itself. Also can receive the [annotations] and/or the [url] of the text. + Text(this.text, {this.annotations, this.url}); + + /// Text constructor with **bold** content by default. + /// + /// Only can receive the [text] itself and the [color]. + Text.bold( + this.text, { + ColorsTypes color = ColorsTypes.Default, + }) : this.annotations = TextAnnotations(bold: true, color: color); + + /// Text constructor with _italic_ content by default. + /// + /// Only can receive the [text] itself and the [color]. + Text.italic( + this.text, { + ColorsTypes color = ColorsTypes.Default, + }) : this.annotations = TextAnnotations(italic: true, color: color); + + /// Text constructor with `code` content by default. + /// + /// Only can receive the [text] itself and the [color]. + Text.code( + this.text, { + ColorsTypes color = ColorsTypes.Default, + }) : this.annotations = TextAnnotations(code: true, color: color); + + /// Text constructor with underline content by default. + /// + /// Only can receive the [text] itself and the [color]. + Text.underline( + this.text, { + ColorsTypes color = ColorsTypes.Default, + }) : this.annotations = TextAnnotations(underline: true, color: color); + + /// Text constructor to change the default color of a text. + /// + /// Only can receive the [text] itself and the [color]. + Text.color( + this.text, { + ColorsTypes color = ColorsTypes.Default, + }) : this.annotations = TextAnnotations(color: color); + + /// Text mapper for lists. + /// + /// Can receive the list of [texts] the [separator] (by default ", ") and the [lastSeparator] (by default " and "). + /// + /// This static method generates a list of "Text" instances in the style of a textual list. + /// + /// Example: + /// ```dart + /// Paragraph( + /// texts: [ + /// Text('Some programming languages are '), + /// ...Text.list(texts: [ + /// Text.color('Java', color: ColorsTypes.Green), + /// Text.color('Javascript', color: ColorsTypes.Blue), + /// Text.color('PHP', color: ColorsTypes.Purple), + /// Text.color('Dart', color: ColorsTypes.Orange), + /// ]), + /// Text('.'), + /// ], + /// ); + /// ``` + /// Should print: _"Java, Javascript, PHP and Dart"_ but each element with his own style. + /// + /// Otherwise the code should be: + /// ```dart + /// Paragraph( + /// texts: [ + /// Text('Some programming languages are '), + /// Text.color('Java', color: ColorsTypes.Green), + /// Text(', '), + /// Text.color('Javascript', color: ColorsTypes.Blue), + /// Text(', '), + /// Text.color('PHP', color: ColorsTypes.Purple), + /// Text(' and '), + /// Text.color('Dart', color: ColorsTypes.Orange), + /// Text('.'), + /// ], + /// ); + /// ``` + static List list({ + required List texts, + String separator = ', ', + String lastSeparator = ' and ', + }) { + List list = []; + texts.asMap().forEach((index, element) { + if (index == texts.length - 1) { + // last element + list.add(element); + } else { + if (index == texts.length - 2) { + // penultimate element + list.addAll([element, Text(lastSeparator)]); + } else { + // any other element + list.addAll([element, Text(separator)]); + } + } + }); + return list; + } + + /// Create a new Text instance from json. + /// + /// Receive a [json] from where the information is extracted. + Text.fromJson(Map json) + : this.text = json['text']['content'] ?? '', + this.annotations = TextAnnotations.fromJson(json['annotations'] ?? {}), + this.url = json['href'] != null ? Uri.parse(json['href']) : null; + + /// Convert this to a json representation valid for the Notion API. + /// ``` + Map toJson() { + Map json = { + 'type': _type, + 'text': { + 'content': '$text', + }, + }; + + // Null values on fields can break the API call. + if (annotations != null) { + json['annotations'] = annotations?.toJson(); + } + + if (url != null) { + json['link'] = { + 'url': url.toString(), + }; + } + + return json; + } + + /// Map a list of texts from a [json] list with dynamics. + static List fromListJson(List json) { + List texts = []; + json.forEach((e) => texts.add(Text.fromJson(e))); + return texts; + } +} + +/// The text style. +class TextAnnotations { + /// A marker for bold text. + bool bold; + + /// A marker for italic text. + bool italic; + + /// A marker for strikethrough text. + bool strikethrough; + + /// A marker for underline text. + bool underline; + + /// A marker for code text. + bool code; + + /// The color of the text. Default by... by default hehe. + ColorsTypes color; + + /// The string value of the color type. + String get strColor => colorTypeToString(color); + + /// Main text annotations constructor. + /// + /// Can receive if the text will be [bold], [italic], [strikethrough], [underline] and/or [code]. Also the [color] of the text. + /// + /// Valid colors are defined by the Colors enum. By default the color type is... Default dah. + TextAnnotations({ + this.bold = false, + this.italic = false, + this.strikethrough = false, + this.underline = false, + this.code = false, + this.color = ColorsTypes.Default, + }); + + /// Create a new text annotation instance from json. + /// + /// Receive a [json] from where the information is extracted. + TextAnnotations.fromJson(Map json) + : this.bold = json['bold'] ?? false, + this.italic = json['italic'] ?? false, + this.strikethrough = json['strikethrough'] ?? false, + this.underline = json['underline'] ?? false, + this.code = json['code'] ?? false, + this.color = stringToColorType(json['color'] ?? ''); + + /// Convert this to a json representation valid for the Notion API. + Map toJson() { + Map json = { + 'color': strColor, + }; + + // Null & false values on fields can break the API call. + if (bold) { + json['bold'] = bold; + } + if (italic) { + json['italic'] = italic; + } + if (strikethrough) { + json['strikethrough'] = strikethrough; + } + if (underline) { + json['underline'] = underline; + } + if (code) { + json['code'] = code; + } + + return json; + } +} + +class RichText { + String _type = 'text'; + + /// The text intself. + String text; + + /// The annotations of the text. + TextAnnotations? annotations = TextAnnotations(); + + /// The url of the text when the text is a link. + Uri? url; + + /// Main rich text constructor. + /// + /// Required the [text] itself. Also can receive the [annotations] and/or the [url] of the text. + RichText(this.text, {this.annotations, this.url}); + + /// Text constructor with **bold** content by default. + /// + /// Only can receive the [text] itself and the [color]. + RichText.bold( + this.text, { + ColorsTypes color = ColorsTypes.Default, + }) : this.annotations = TextAnnotations(bold: true, color: color); + + /// Text constructor with _italic_ content by default. + /// + /// Only can receive the [text] itself and the [color]. + RichText.italic( + this.text, { + ColorsTypes color = ColorsTypes.Default, + }) : this.annotations = TextAnnotations(italic: true, color: color); + + /// Text constructor with `code` content by default. + /// + /// Only can receive the [text] itself and the [color]. + RichText.code( + this.text, { + ColorsTypes color = ColorsTypes.Default, + }) : this.annotations = TextAnnotations(code: true, color: color); + + /// Text constructor with underline content by default. + /// + /// Only can receive the [text] itself and the [color]. + RichText.underline( + this.text, { + ColorsTypes color = ColorsTypes.Default, + }) : this.annotations = TextAnnotations(underline: true, color: color); + + /// Text constructor to change the default color of a text. + /// + /// Only can receive the [text] itself and the [color]. + RichText.color( + this.text, { + ColorsTypes color = ColorsTypes.Default, + }) : this.annotations = TextAnnotations(color: color); + + /// Text mapper for lists. + /// + /// Can receive the list of [texts] the [separator] (by default ", ") and the [lastSeparator] (by default " and "). + /// + /// This static method generates a list of "Text" instances in the style of a textual list. + /// + /// Example: + /// ```dart + /// Paragraph( + /// texts: [ + /// Text('Some programming languages are '), + /// ...Text.list(texts: [ + /// Text.color('Java', color: ColorsTypes.Green), + /// Text.color('Javascript', color: ColorsTypes.Blue), + /// Text.color('PHP', color: ColorsTypes.Purple), + /// Text.color('Dart', color: ColorsTypes.Orange), + /// ]), + /// Text('.'), + /// ], + /// ); + /// ``` + /// Should print: _"Java, Javascript, PHP and Dart"_ but each element with his own style. + /// + /// Otherwise the code should be: + /// ```dart + /// Paragraph( + /// texts: [ + /// Text('Some programming languages are '), + /// Text.color('Java', color: ColorsTypes.Green), + /// Text(', '), + /// Text.color('Javascript', color: ColorsTypes.Blue), + /// Text(', '), + /// Text.color('PHP', color: ColorsTypes.Purple), + /// Text(' and '), + /// Text.color('Dart', color: ColorsTypes.Orange), + /// Text('.'), + /// ], + /// ); + /// ``` + static List list({ + required List texts, + String separator = ', ', + String lastSeparator = ' and ', + }) { + List list = []; + texts.asMap().forEach((index, element) { + if (index == texts.length - 1) { + // last element + list.add(element); + } else { + if (index == texts.length - 2) { + // penultimate element + list.addAll([element, RichText(lastSeparator)]); + } else { + // any other element + list.addAll([element, RichText(separator)]); + } + } + }); + return list; + } + + /// Create a new Text instance from json. + /// + /// Receive a [json] from where the information is extracted. + RichText.fromJson(Map json) + : this.text = json['text']['content'] ?? '', + this.annotations = TextAnnotations.fromJson(json['annotations'] ?? {}), + this.url = json['href'] != null ? Uri.parse(json['href']) : null; + + /// Convert this to a json representation valid for the Notion API. + /// ``` + Map toJson() { + Map json = { + 'type': _type, + 'text': { + 'content': '$text', + }, + }; + + // Null values on fields can break the API call. + if (annotations != null) { + json['annotations'] = annotations?.toJson(); + } + + if (url != null) { + json['link'] = { + 'url': url.toString(), + }; + } + + return json; + } + + /// Map a list of texts from a [json] list with dynamics. + static List fromListJson(List json) { + List texts = []; + json.forEach((e) => texts.add(RichText.fromJson(e))); + return texts; + } +} + +/// The text style. +class RichTextAnnotations { + /// A marker for bold text. + bool bold; + + /// A marker for italic text. + bool italic; + + /// A marker for strikethrough text. + bool strikethrough; + + /// A marker for underline text. + bool underline; + + /// A marker for code text. + bool code; + + /// The color of the text. Default by... by default hehe. + ColorsTypes color; + + /// The string value of the color type. + String get strColor => colorTypeToString(color); + + /// Main rich text annotations constructor. + /// + /// Can receive if the text will be [bold], [italic], [strikethrough], [underline] and/or [code]. Also the [color] of the text. + /// + /// Valid colors are defined by the Colors enum. By default the color type is... Default dah. + RichTextAnnotations({ + this.bold = false, + this.italic = false, + this.strikethrough = false, + this.underline = false, + this.code = false, + this.color = ColorsTypes.Default, + }); + + /// Create a new text annotation instance from json. + /// + /// Receive a [json] from where the information is extracted. + RichTextAnnotations.fromJson(Map json) + : this.bold = json['bold'] ?? false, + this.italic = json['italic'] ?? false, + this.strikethrough = json['strikethrough'] ?? false, + this.underline = json['underline'] ?? false, + this.code = json['code'] ?? false, + this.color = stringToColorType(json['color'] ?? ''); + + /// Convert this to a json representation valid for the Notion API. + Map toJson() { + Map json = { + 'color': strColor, + }; + + // Null & false values on fields can break the API call. + if (bold) { + json['bold'] = bold; + } + if (italic) { + json['italic'] = italic; + } + if (strikethrough) { + json['strikethrough'] = strikethrough; + } + if (underline) { + json['underline'] = underline; + } + if (code) { + json['code'] = code; + } + + return json; + } +} diff --git a/lib/notion_blocks.dart b/lib/src/notion_blocks.dart similarity index 53% rename from lib/notion_blocks.dart rename to lib/src/notion_blocks.dart index 3848792..d424c3d 100644 --- a/lib/notion_blocks.dart +++ b/lib/src/notion_blocks.dart @@ -1,35 +1,46 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:notion_api/base_client.dart'; -import 'notion/general/lists/children.dart'; +import 'notion/exports.dart'; import 'responses/notion_response.dart'; import 'statics.dart'; /// A client for Notion API block children requests. -class NotionBlockClient extends BaseClient { +class NotionBlockClient { + /// The API integration secret token. + String _token; + + /// The API version. + String _v; + + /// The API date version. + /// + /// It's not the same as the API version. + String _dateVersion; + /// The path of the requests group. - @override final String path = 'blocks'; /// Main Notion block client constructor. /// - /// Require the [token] to authenticate the requests, and the API [version] where to make the calls, which is the latests by default (v1). + /// Require the [auth] token to authenticate the requests, and the API [version] where to make the calls, which is the latests by default (v1). Also can receive the [dateVersion], which is by default "2021-05-13". NotionBlockClient({ - required String token, - String version: latestVersion, - String dateVersion: latestDateVersion, - }) : super(token: token, version: version, dateVersion: dateVersion); + required String auth, + String version = latestVersion, + String dateVersion = latestDateVersion, + }) : this._token = auth, + this._v = version, + this._dateVersion = dateVersion; - /// Retrieve the block children from block with [id]. + /// Retrieve the block children from block specified by the [block_id]. /// /// A [startCursor] can be defined to specify the page where to start. /// Also a [pageSize] can be defined to limit the result. The max value is 100. /// /// _See more at https://developers.notion.com/reference/get-block-children_ - Future fetch( - String id, { + Future list({ + required String block_id, String? startCursor, int? pageSize, }) async { @@ -42,28 +53,28 @@ class NotionBlockClient extends BaseClient { } http.Response response = await http - .get(Uri.https(host, '/$v/$path/$id/children', query), headers: { - 'Authorization': 'Bearer $token', - 'Notion-Version': dateVersion, + .get(Uri.https(host, '/$_v/$path/$block_id/children', query), headers: { + 'Authorization': 'Bearer $_token', + 'Notion-Version': _dateVersion, }); return NotionResponse.fromResponse(response); } - /// Append a block [children] [to] a specific block. + /// Append [children] to a block specified by the [block_id]. /// /// _See more at https://developers.notion.com/reference/patch-block-children_ Future append({ - required String to, + required String block_id, required Children children, }) async { http.Response res = await http.patch( - Uri.https(host, '/$v/$path/$to/children'), + Uri.https(host, '/$_v/$path/$block_id/children'), body: jsonEncode(children.toJson()), headers: { - 'Authorization': 'Bearer $token', + 'Authorization': 'Bearer $_token', 'Content-Type': 'application/json; charset=UTF-8', - 'Notion-Version': dateVersion, + 'Notion-Version': _dateVersion, }); return NotionResponse.fromResponse(res); diff --git a/lib/src/notion_databases.dart b/lib/src/notion_databases.dart new file mode 100644 index 0000000..1b1d67b --- /dev/null +++ b/lib/src/notion_databases.dart @@ -0,0 +1,176 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:notion_api/src/notion/new/database/database_property.dart'; + +import 'notion/exports.dart'; +import 'responses/notion_response.dart'; +import 'statics.dart'; + +/// A client for Notion API databases requests. +class NotionDatabasesClient { + /// The API integration secret token. + String _token; + + /// The API version. + String _v; + + /// The API date version. + /// + /// It's not the same as the API version. + String _dateVersion; + + /// The path of the requests group. + final String path = 'databases'; + + /// Main Notion database client constructor. + /// + /// Require the [auth] token to authenticate the requests, and the API [version] where to make the calls, which is the latests by default (v1). Also can receive the [dateVersion], which is by default "2021-05-13". + NotionDatabasesClient({ + required String auth, + String version = latestVersion, + String dateVersion = latestDateVersion, + }) : this._token = auth, + this._v = version, + this._dateVersion = dateVersion; + + /// Retrieve the database specified by the [databaseId]. + /// + /// _See more at https://developers.notion.com/reference/get-database_ + Future retrieve({required String databaseId}) async { + http.Response res = await http.get( + Uri.https(host, '/$_v/$path/$databaseId'), + headers: { + 'Authorization': 'Bearer $_token', + 'Notion-Version': _dateVersion, + }, + ); + + return NotionResponse.fromResponse(res); + } + + /// Create a [database]. + /// + /// _See more at https://developers.notion.com/reference/create-a-database_ + @deprecated + Future oldCreate(Database database) async { + http.Response res = await http.post( + Uri.https(host, '/$_v/$path'), + body: jsonEncode(database.toRequestJson()), + headers: { + 'Authorization': 'Bearer $_token', + 'Notion-Version': _dateVersion, + 'Content-Type': 'application/json', + }, + ); + + return NotionResponse.fromResponse(res); + } + + /// Create a [database]. + /// + /// _See more at https://developers.notion.com/reference/create-a-database_ + Future create({ + required String pageId, + required DatabaseProperties properties, + String? title, + }) async { + Database database = Database( + parent: Parent.page(id: pageId), + properties: properties, + title: title, + ); + + http.Response res = await http.post( + Uri.https(host, '/$_v/$path'), + body: jsonEncode(database.toRequestJson()), + headers: { + 'Authorization': 'Bearer $_token', + 'Notion-Version': _dateVersion, + 'Content-Type': 'application/json', + }, + ); + + return NotionResponse.fromResponse(res); + } + + /// Update the database [title] and [properties] with the [databaseId]. + /// + /// To **add a new property** set the key and the property type. Example: + /// ``` + /// NotionResponse res = await databases.update( + /// databaseId: 'some_existing_database_id', + /// properties: Properties(map: { + /// 'Tag': DatabaseProperties.Checkbox() // "Tag" will be created as a checkbox property + /// })); + /// ``` + /// + /// To **update an existing property** set the key as the current property name. Example: + /// ``` + /// NotionResponse res = await databases.update( + /// databaseId: 'some_existing_database_id', + /// properties: Properties(map: { + /// 'Tag': DatabaseProperties.RichText() // If "Tag" was Checkbox will be turn into RichText property + /// })); + /// ``` + /// + /// To **rename an existing property** set the key as the current property name and set the name parameters to the new name. Example: + /// ``` + /// NotionResponse res = await databases.update( + /// databaseId: 'some_existing_database_id', + /// properties: Properties(map: { + /// 'Tag': DatabaseProperties.RichText(name: 'Details') // "Tag" will be renamed to "Details" + /// })); + /// ``` + /// + /// **Warning:** Cannot change a title property to a different property type. + /// + /// _https://developers.notion.com/reference/update-a-database_ + Future update({ + required String databaseId, + List? title, + Properties? properties, + }) async { + Map toUpdate = {}; + + if (title != null) toUpdate['title'] = title; + if (properties != null) toUpdate['properties'] = properties; + + http.Response res = await http.patch( + Uri.https(host, '/$_v/$path/${databaseId}'), + body: jsonEncode(toUpdate), + headers: { + 'Authorization': 'Bearer $_token', + 'Notion-Version': _dateVersion, + 'Content-Type': 'application/json', + }, + ); + + return NotionResponse.fromResponse(res); + } + + /// Retrieve all databases. + /// + /// A [startCursor] can be defined to specify the page where to start. + /// Also a [pageSize] can be defined to limit the result. The max value is 100. + /// + /// _See more at https://developers.notion.com/reference/get-databases_ + @deprecated + Future list({String? startCursor, int? pageSize}) async { + Map query = {}; + if (startCursor != null) { + query['start_cursor'] = startCursor; + } + if (pageSize != null && pageSize >= 0 && pageSize <= 100) { + query['page_size'] = pageSize.toString(); + } + + http.Response res = + await http.get(Uri.https(host, '/$_v/$path', query), headers: { + 'Authorization': 'Bearer $_token', + 'Notion-Version': _dateVersion, + }); + + return NotionResponse.fromResponse(res); + } +} diff --git a/lib/src/notion_pages.dart b/lib/src/notion_pages.dart new file mode 100644 index 0000000..d0fb88e --- /dev/null +++ b/lib/src/notion_pages.dart @@ -0,0 +1,92 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; + +import 'notion/exports.dart'; +import 'responses/notion_response.dart'; +import 'statics.dart'; + +/// A client for Notion API pages requests. +class NotionPagesClient { + /// The API integration secret token. + String _token; + + /// The API version. + String _v; + + /// The API date version. + /// + /// It's not the same as the API version. + String _dateVersion; + + /// The path of the requests group. + final String path = 'pages'; + + /// Main Notion page client constructor. + /// + /// Require the [auth] token to authenticate the requests, and the API [version] where to make the calls, which is the latests by default (v1). Also can receive the [dateVersion], which is by default "2021-05-13". + NotionPagesClient({ + required String auth, + String version = latestVersion, + String dateVersion = latestDateVersion, + }) : this._token = auth, + this._v = version, + this._dateVersion = dateVersion; + + /// Retrieve the page specified by the [page_id]. + /// + /// _See more at https://developers.notion.com/reference/get-page_ + Future retrieve({required String page_id}) async { + http.Response res = + await http.get(Uri.https(host, '/$_v/$path/$page_id'), headers: { + 'Authorization': 'Bearer $_token', + 'Notion-Version': _dateVersion, + }); + + return NotionResponse.fromResponse(res); + } + + /// Create a new [page]. + /// + /// _See more at https://developers.notion.com/reference/post-page_ + Future create(Page page) async { + http.Response res = await http.post( + Uri.https(host, '/$_v/$path'), + body: jsonEncode(page.toJson()), + headers: { + 'Authorization': 'Bearer $_token', + 'Content-Type': 'application/json; charset=UTF-8', + 'Notion-Version': _dateVersion, + }, + ); + + return NotionResponse.fromResponse(res); + } + + /// Update the [properties] of the page specified by the [page_id]. Can also mark the page as [archived]. + /// + /// The page should contain the property to update. + /// + /// Archive a page is the equivalent to delete it according to API reference. + /// + /// _See more at https://developers.notion.com/reference/patch-page_ + Future update({ + required String page_id, + Properties? properties, + bool? archived, + }) async { + Properties _properties = properties ?? Properties.empty(); + http.Response res = await http.patch(Uri.https(host, '/$_v/$path/$page_id'), + body: jsonEncode({ + 'properties': _properties.toJson(), + if (archived != null) 'archived': archived, + }), + headers: { + 'Authorization': 'Bearer $_token', + 'Content-Type': 'application/json', + 'Notion-Version': _dateVersion, + }); + + return NotionResponse.fromResponse(res); + } +} diff --git a/lib/responses/notion_response.dart b/lib/src/responses/notion_response.dart similarity index 89% rename from lib/responses/notion_response.dart rename to lib/src/responses/notion_response.dart index 6f697d9..74d1ab6 100644 --- a/lib/responses/notion_response.dart +++ b/lib/src/responses/notion_response.dart @@ -1,11 +1,9 @@ import 'dart:convert'; import 'package:http/http.dart' show Response; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/objects/database.dart'; -import 'package:notion_api/notion/general/lists/pagination.dart'; -import 'package:notion_api/notion/objects/pages.dart'; -import 'package:notion_api/utils/utils.dart'; + +import '../notion/exports.dart'; +import '../utils/utils.dart'; /// A representation of the Response from the Notion API. class NotionResponse { @@ -72,8 +70,8 @@ class NotionResponse { /// /// By default the [object] type is None and the [status] is zero. NotionResponse({ - this.object: ObjectTypes.None, - this.status: 0, + this.object = ObjectTypes.None, + this.status = 0, this.code, this.message, }); diff --git a/lib/statics.dart b/lib/src/statics.dart similarity index 88% rename from lib/statics.dart rename to lib/src/statics.dart index 038a287..36ac349 100644 --- a/lib/statics.dart +++ b/lib/src/statics.dart @@ -9,4 +9,4 @@ const String latestVersion = 'v1'; /// The latest date version of Notion API /// /// See [Notion versioning](https://developers.notion.com/reference/versioning) for more information. -const String latestDateVersion = '2021-05-13'; +const String latestDateVersion = '2021-08-16'; diff --git a/lib/utils/utils.dart b/lib/src/utils/utils.dart similarity index 96% rename from lib/utils/utils.dart rename to lib/src/utils/utils.dart index 26346d9..1891684 100644 --- a/lib/utils/utils.dart +++ b/lib/src/utils/utils.dart @@ -1,4 +1,4 @@ -import 'package:notion_api/notion/general/types/notion_types.dart'; +import '../notion/exports.dart'; // Lists of types const List objects = ObjectTypes.values; @@ -327,3 +327,12 @@ PropertiesTypes extractPropertyType(Map json) { /// Find if a json [field] is a List bool fieldIsList(dynamic field) => field is List; + +/// Find is a list of properties are not null +void throwIfAnyNull(dynamic json, List properties) { + bool anyNull = properties.any((property) => json[property] == null); + + if (anyNull) + throw FormatException( + 'Any required field is null. Required fields are ${properties}'); +} diff --git a/pubspec.lock b/pubspec.lock index 9101701..2714d64 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,351 +5,401 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - url: "https://pub.dartlang.org" + sha256: d93b0378aadce9c1388108067946276582c2ae89426c64c17920c74988508fed + url: "https://pub.dev" source: hosted version: "22.0.0" analyzer: dependency: transitive description: name: analyzer - url: "https://pub.dartlang.org" + sha256: "90d0daecd576ee4afe1a67d7fe0fef70cc08cb4cb87423e7a5a8770eabd3ee9f" + url: "https://pub.dev" source: hosted version: "1.7.1" args: dependency: transitive description: name: args - url: "https://pub.dartlang.org" + sha256: ad180a697bf97dd54ab519a4665c57683bf4f70649a18671cea661cf0397c055 + url: "https://pub.dev" source: hosted version: "2.1.0" async: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "6eda8392a48ae1de7ea438c91a4ba3e77205f043e7013102a424863aa6db368f" + url: "https://pub.dev" source: hosted version: "2.5.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "5bbf32bc9e518d41ec49718e2931cd4527292c9b0c6d2dffcf7fe6b9a8a8cf72" + url: "https://pub.dev" source: hosted version: "2.1.0" charcode: dependency: transitive description: name: charcode - url: "https://pub.dartlang.org" + sha256: "8e36feea6de5ea69f2199f29cf42a450a855738c498b57c0b980e2d3cca9c362" + url: "https://pub.dev" source: hosted version: "1.2.0" cli_util: dependency: transitive description: name: cli_util - url: "https://pub.dartlang.org" + sha256: cf1c02840bbbcf8fcd13feb5933c62d643cc58ddf4f6088707cf48d1892cbc5d + url: "https://pub.dev" source: hosted version: "0.3.0" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: "6d4193120997ecfd09acf0e313f13dc122b119e5eca87ef57a7d065ec9183762" + url: "https://pub.dev" source: hosted version: "1.15.0" convert: dependency: transitive description: name: convert - url: "https://pub.dartlang.org" + sha256: df567b950053d83b4dba3e8c5799c411895d146f82b2147114b666a4fd9a80dd + url: "https://pub.dev" source: hosted version: "3.0.0" coverage: dependency: transitive description: name: coverage - url: "https://pub.dartlang.org" + sha256: ad538fa2e8f6b828d54c04a438af816ce814de404690136d3b9dfb3a436cd01c + url: "https://pub.dev" source: hosted version: "1.0.3" crypto: dependency: transitive description: name: crypto - url: "https://pub.dartlang.org" + sha256: cf75650c66c0316274e21d7c43d3dea246273af5955bd94e8184837cd577575c + url: "https://pub.dev" source: hosted version: "3.0.1" dotenv: dependency: "direct dev" description: name: dotenv - url: "https://pub.dartlang.org" + sha256: dc4c91d8d5e9e361803fc81e8ea7ba0f35be483b656837ea6b9a3c41df308c68 + url: "https://pub.dev" source: hosted version: "3.0.0" file: dependency: transitive description: name: file - url: "https://pub.dartlang.org" + sha256: bbbe4cbd826c19385c87dcb11d07af2b26c98ac9ed94c1cd65172818570e25b2 + url: "https://pub.dev" source: hosted version: "6.1.1" frontend_server_client: dependency: transitive description: name: frontend_server_client - url: "https://pub.dartlang.org" + sha256: "780784ec9e9362ed5278272d39b6590474dba495483ec97eba31df4d23622fa0" + url: "https://pub.dev" source: hosted version: "2.1.0" glob: dependency: transitive description: name: glob - url: "https://pub.dartlang.org" + sha256: dda85ce2aefce16f7e75586acbcb1e8320bf176f69fd94082e31945d6de67f3e + url: "https://pub.dev" source: hosted version: "2.0.1" http: dependency: "direct main" description: name: http - url: "https://pub.dartlang.org" + sha256: b6f1f143a71e1fe1b34670f1acd6f13960ade2557c96b87e127e0cf661969791 + url: "https://pub.dev" source: hosted version: "0.13.3" http_multi_server: dependency: transitive description: name: http_multi_server - url: "https://pub.dartlang.org" + sha256: bfb651625e251a88804ad6d596af01ea903544757906addcb2dcdf088b5ea185 + url: "https://pub.dev" source: hosted version: "3.0.1" http_parser: dependency: transitive description: name: http_parser - url: "https://pub.dartlang.org" + sha256: e362d639ba3bc07d5a71faebb98cde68c05bfbcfbbb444b60b6f60bb67719185 + url: "https://pub.dev" source: hosted version: "4.0.0" io: dependency: transitive description: name: io - url: "https://pub.dartlang.org" + sha256: "15a5436d2a02dc60e6dc2fb5d7dfaac08b7b137cff3d4bf3158d38ecab656b69" + url: "https://pub.dev" source: hosted version: "1.0.0" js: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: d9bdfd70d828eeb352390f81b18d6a354ef2044aa28ef25682079797fa7cd174 + url: "https://pub.dev" source: hosted version: "0.6.3" logging: dependency: transitive description: name: logging - url: "https://pub.dartlang.org" + sha256: "0520a4826042a8a5d09ddd4755623a50d37ee536d79a70452aff8c8ad7bb6c27" + url: "https://pub.dev" source: hosted version: "1.0.1" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "38c7be344ac5057e10161a5ecb00c9d9d67ed2f150001278601dd27d9fe64206" + url: "https://pub.dev" source: hosted version: "0.12.10" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "98a7492d10d7049ea129fd4e50f7cdd2d5008522b1dfa1148bbbc542b9dd21f7" + url: "https://pub.dev" source: hosted version: "1.3.0" mime: dependency: transitive description: name: mime - url: "https://pub.dartlang.org" + sha256: a7a98ea7f366e2cc9d2b20873815aebec5e2bc124fe0da9d3f7f59b0625ea180 + url: "https://pub.dev" source: hosted version: "1.0.0" node_preamble: dependency: transitive description: name: node_preamble - url: "https://pub.dartlang.org" + sha256: c133f761a6a790d0b000efa4f74eae9700bb6e9e9f5e996f0e8d6fe92703ced6 + url: "https://pub.dev" source: hosted version: "2.0.0" package_config: dependency: transitive description: name: package_config - url: "https://pub.dartlang.org" + sha256: "20e7154d701fedaeb219dad732815ecb66677667871127998a9a6581c2aba4ba" + url: "https://pub.dev" source: hosted version: "2.0.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "2ad4cddff7f5cc0e2d13069f2a3f7a73ca18f66abd6f5ecf215219cdb3638edb" + url: "https://pub.dev" source: hosted version: "1.8.0" pedantic: dependency: transitive description: name: pedantic - url: "https://pub.dartlang.org" + sha256: "8f6460c77a98ad2807cd3b98c67096db4286f56166852d0ce5951bb600a63594" + url: "https://pub.dev" source: hosted version: "1.11.0" pool: dependency: transitive description: name: pool - url: "https://pub.dartlang.org" + sha256: "05955e3de2683e1746222efd14b775df7131139e07695dc8e24650f6b4204504" + url: "https://pub.dev" source: hosted version: "1.5.0" pub_semver: dependency: transitive description: name: pub_semver - url: "https://pub.dartlang.org" + sha256: "59ed538734419e81f7fc18c98249ae72c3c7188bdd9dceff2840585227f79843" + url: "https://pub.dev" source: hosted version: "2.0.0" shelf: dependency: transitive description: name: shelf - url: "https://pub.dartlang.org" + sha256: "26d22c68cf9c349fb1adb4a830da3e014cb5f7ed9cc57f721d9d9e20cf8d6de5" + url: "https://pub.dev" source: hosted version: "1.1.4" shelf_packages_handler: dependency: transitive description: name: shelf_packages_handler - url: "https://pub.dartlang.org" + sha256: e0b44ebddec91e70a713e13adf93c1b2100821303b86a18e1ef1d082bd8bd9b8 + url: "https://pub.dev" source: hosted version: "3.0.0" shelf_static: dependency: transitive description: name: shelf_static - url: "https://pub.dartlang.org" + sha256: "8584c0aa0f5756a61519b1a2fc2cd22ddbc518e9396bd33ebf06b9836bb23d13" + url: "https://pub.dev" source: hosted version: "1.0.0" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - url: "https://pub.dartlang.org" + sha256: fd84910bf7d58db109082edf7326b75322b8f186162028482f53dc892f00332d + url: "https://pub.dev" source: hosted version: "1.0.1" source_map_stack_trace: dependency: transitive description: name: source_map_stack_trace - url: "https://pub.dartlang.org" + sha256: "8c463326277f68a628abab20580047b419c2ff66756fd0affd451f73f9508c11" + url: "https://pub.dev" source: hosted version: "2.1.0" source_maps: dependency: transitive description: name: source_maps - url: "https://pub.dartlang.org" + sha256: "52de2200bb098de739794c82d09c41ac27b2e42fd7e23cce7b9c74bf653c7296" + url: "https://pub.dev" source: hosted version: "0.10.10" source_span: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: f4b22294de9a549967d0033d4f30fcad4f0afc200f4bf58e82d815725feec70c + url: "https://pub.dev" source: hosted version: "1.8.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: f8d9f247e2f9f90e32d1495ff32dac7e4ae34ffa7194c5ff8fcc0fd0e52df774 + url: "https://pub.dev" source: hosted version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: db47e4797198ee601990820437179bb90219f918962318d494ada2b4b11e6f6d + url: "https://pub.dev" source: hosted version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: dd11571b8a03f7cadcf91ec26a77e02bfbd6bbba2a512924d3116646b4198fc4 + url: "https://pub.dev" source: hosted version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a88162591b02c1f3a3db3af8ce1ea2b374bd75a7bb8d5e353bcfbdc79d719830 + url: "https://pub.dev" source: hosted version: "1.2.0" test: dependency: "direct dev" description: name: test - url: "https://pub.dartlang.org" + sha256: da03cbb1dd2537def0ea0d7f134cdfae2c97b4df95687827ffaac9a54eeb20db + url: "https://pub.dev" source: hosted version: "1.17.5" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: f27b321e23d18b58891d880ed565be7f517cd9dcce2652873101a7c742a9c8ca + url: "https://pub.dev" source: hosted version: "0.4.0" test_core: dependency: transitive description: name: test_core - url: "https://pub.dartlang.org" + sha256: "382f73c8fdc29bec39fdb23347204b2341ce56a90b6479ec2b893dfd56351168" + url: "https://pub.dev" source: hosted version: "0.3.25" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee" + url: "https://pub.dev" source: hosted version: "1.3.0" vm_service: dependency: transitive description: name: vm_service - url: "https://pub.dartlang.org" + sha256: f6c7c62374647d36cc0c67908a934f99ef17eb603cf678db090f01e55fa3e34d + url: "https://pub.dev" source: hosted version: "7.1.0" watcher: dependency: transitive description: name: watcher - url: "https://pub.dartlang.org" + sha256: "68173f2fa67d241323a4123be7ed4e43424c54befa5505d71c8ad4b7baf8f71d" + url: "https://pub.dev" source: hosted version: "1.0.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - url: "https://pub.dartlang.org" + sha256: "0c2ada1b1aeb2ad031ca81872add6be049b8cb479262c6ad3c4b0f9c24eaab2f" + url: "https://pub.dev" source: hosted version: "2.1.0" webkit_inspection_protocol: dependency: transitive description: name: webkit_inspection_protocol - url: "https://pub.dartlang.org" + sha256: "5adb6ab8ed14e22bb907aae7338f0c206ea21e7a27004e97664b16c120306f00" + url: "https://pub.dev" source: hosted version: "1.0.0" yaml: dependency: transitive description: name: yaml - url: "https://pub.dartlang.org" + sha256: "3cee79b1715110341012d27756d9bae38e650588acd38d3f3c610822e1337ace" + url: "https://pub.dev" source: hosted version: "3.1.0" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.12.0 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index c38c707..da79f3d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: notion_api description: A wrapper for the public beta Notion API to manage it like a Notion SDK package for dart. -version: 1.2.0 +version: 2.0.0-beta2 homepage: https://github.com/jonathangomz/notion_api environment: diff --git a/test/blocks/block.dart b/test/blocks/block.dart index 4f0fff8..6f9d28c 100644 --- a/test/blocks/block.dart +++ b/test/blocks/block.dart @@ -1,5 +1,4 @@ -import 'package:notion_api/notion/blocks/block.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; void main() { @@ -35,8 +34,8 @@ void main() { }, ] }, - createdTime: '2021-05-20T21:01:00.000Z', - lastEditedTime: '2021-05-26T19:10:00.000Z', + createdTime: DateTime.parse('2021-05-20T21:01:00.000Z'), + lastEditedTime: DateTime.parse('2021-05-26T19:10:00.000Z'), ); expect(block.strType, 'paragraph'); @@ -66,8 +65,8 @@ void main() { }, ] }, - createdTime: '2021-05-20T21:01:00.000Z', - lastEditedTime: '2021-05-26T19:10:00.000Z', + createdTime: DateTime.parse('2021-05-20T21:01:00.000Z'), + lastEditedTime: DateTime.parse('2021-05-26T19:10:00.000Z'), ).toJson(); expect(json['type'], 'paragraph'); diff --git a/test/blocks/bulleted_list_item_test.dart b/test/blocks/bulleted_list_item_test.dart index 30199ea..7b1acb5 100644 --- a/test/blocks/bulleted_list_item_test.dart +++ b/test/blocks/bulleted_list_item_test.dart @@ -1,9 +1,4 @@ -import 'package:notion_api/notion/blocks/bulleted_list_item.dart'; -import 'package:notion_api/notion/blocks/heading.dart'; -import 'package:notion_api/notion/blocks/paragraph.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/utils/utils.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; void main() { @@ -19,6 +14,34 @@ void main() { expect(block.type, BlockTypes.BulletedListItem); }); + test('Create an instance with text constructor', () { + BulletedListItem block = BulletedListItem.text( + 'This is a paragraph with a single text', + annotations: TextAnnotations(bold: true), + children: [ + Paragraph.text('This is a children'), + ], + ); + + expect( + block.content, + allOf([ + isList, + isNotEmpty, + hasLength( + 1, + ) + ])); + expect(block.content.first.annotations!.bold, isTrue); + expect( + block.children, + allOf([ + isList, + isNotEmpty, + hasLength(1), + ])); + }); + test('Create an instance with information', () { BulletedListItem block = BulletedListItem(text: Text('A')).addText('B'); diff --git a/test/blocks/heading_test.dart b/test/blocks/heading_test.dart index 5955ce0..6a77e67 100644 --- a/test/blocks/heading_test.dart +++ b/test/blocks/heading_test.dart @@ -1,7 +1,4 @@ -import 'package:notion_api/notion/blocks/heading.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/utils/utils.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; void main() { @@ -16,6 +13,26 @@ void main() { expect(heading.type, BlockTypes.H1); }); + test('Create an instance with text constructor', () { + Heading heading = Heading.text( + 'This is a paragraph with a single text', + annotations: TextAnnotations(bold: true), + type: 2, + ); + + expect( + heading.content, + allOf([ + isList, + isNotEmpty, + hasLength( + 1, + ) + ])); + expect(heading.content.first.annotations!.bold, isTrue); + expect(heading.type, BlockTypes.H2); + }); + test('Create an instance of every heading type', () { Heading h1 = Heading(type: 1); expect(h1.strType, blockTypeToString(BlockTypes.H1)); diff --git a/test/blocks/numbered_list_item_test.dart b/test/blocks/numbered_list_item_test.dart index 1ce3b57..e0ecc79 100644 --- a/test/blocks/numbered_list_item_test.dart +++ b/test/blocks/numbered_list_item_test.dart @@ -1,9 +1,4 @@ -import 'package:notion_api/notion/blocks/heading.dart'; -import 'package:notion_api/notion/blocks/numbered_list_item.dart'; -import 'package:notion_api/notion/blocks/paragraph.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/utils/utils.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; void main() { @@ -18,6 +13,34 @@ void main() { expect(block.type, BlockTypes.NumberedListItem); }); + test('Create an instance with text constructor', () { + NumberedListItem block = NumberedListItem.text( + 'This is a paragraph with a single text', + annotations: TextAnnotations(bold: true), + children: [ + Paragraph.text('This is a children'), + ], + ); + + expect( + block.content, + allOf([ + isList, + isNotEmpty, + hasLength( + 1, + ) + ])); + expect(block.content.first.annotations!.bold, isTrue); + expect( + block.children, + allOf([ + isList, + isNotEmpty, + hasLength(1), + ])); + }); + test('Create an instance with information', () { NumberedListItem block = NumberedListItem(text: Text('A')).addText('B'); diff --git a/test/blocks/paragraph_test.dart b/test/blocks/paragraph_test.dart index 52e97c4..c34d673 100644 --- a/test/blocks/paragraph_test.dart +++ b/test/blocks/paragraph_test.dart @@ -1,8 +1,4 @@ -import 'package:notion_api/notion/blocks/heading.dart'; -import 'package:notion_api/notion/blocks/paragraph.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/utils/utils.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; void main() { @@ -17,6 +13,34 @@ void main() { expect(paragraph.type, BlockTypes.Paragraph); }); + test('Create an instance with text constructor', () { + Paragraph paragraph = Paragraph.text( + 'This is a paragraph with a single text', + annotations: TextAnnotations(bold: true), + children: [ + Paragraph.text('This is a children'), + ], + ); + + expect( + paragraph.content, + allOf([ + isList, + isNotEmpty, + hasLength( + 1, + ) + ])); + expect(paragraph.content.first.annotations!.bold, isTrue); + expect( + paragraph.children, + allOf([ + isList, + isNotEmpty, + hasLength(1), + ])); + }); + test('Create an instance with information', () { Paragraph paragraph = Paragraph().addText('A').addText('B'); @@ -100,20 +124,5 @@ void main() { expect(json[blockTypeToString(BlockTypes.Paragraph)]['children'], allOf([isList, isEmpty])); }); - - test('Create json with separator', () { - String char = 'A'; - String separator = '-'; - - Map json = - Paragraph(textSeparator: separator).addText(char).toJson(); - - List jsonTexts = json[blockTypeToString(BlockTypes.Paragraph)]['text']; - - List texts = Text.fromListJson(jsonTexts); - - expect(texts, isList); - expect(texts.first.text, char + separator); - }); }); } diff --git a/test/blocks/todo_test.dart b/test/blocks/todo_test.dart index d01765f..5dca746 100644 --- a/test/blocks/todo_test.dart +++ b/test/blocks/todo_test.dart @@ -1,9 +1,4 @@ -import 'package:notion_api/notion/blocks/heading.dart'; -import 'package:notion_api/notion/blocks/paragraph.dart'; -import 'package:notion_api/notion/blocks/todo.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/utils/utils.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; void main() { @@ -19,6 +14,36 @@ void main() { expect(todo.type, BlockTypes.ToDo); }); + test('Create an instance with text constructor', () { + ToDo toDo = ToDo.text( + 'This is a ToDo with a single text', + annotations: TextAnnotations(bold: true), + checked: true, + children: [ + Paragraph.text('This is a children'), + ], + ); + + expect( + toDo.content, + allOf([ + isList, + isNotEmpty, + hasLength( + 1, + ) + ])); + expect(toDo.content.first.annotations!.bold, isTrue); + expect(toDo.checked, isTrue); + expect( + toDo.children, + allOf([ + isList, + isNotEmpty, + hasLength(1), + ])); + }); + test('Create an instance with information', () { ToDo todo = ToDo(text: Text('A'), checked: true).addText('B'); diff --git a/test/blocks/toggle_test.dart b/test/blocks/toggle_test.dart index 9cb07a4..4c1955d 100644 --- a/test/blocks/toggle_test.dart +++ b/test/blocks/toggle_test.dart @@ -1,10 +1,4 @@ -import 'package:notion_api/notion/blocks/bulleted_list_item.dart'; -import 'package:notion_api/notion/blocks/numbered_list_item.dart'; -import 'package:notion_api/notion/blocks/paragraph.dart'; -import 'package:notion_api/notion/blocks/toggle.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/utils/utils.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; void main() { @@ -20,6 +14,34 @@ void main() { expect(block.type, BlockTypes.Toggle); }); + test('Create an instance with text constructor', () { + Toggle block = Toggle.text( + 'This is a paragraph with a single text', + annotations: TextAnnotations(bold: true), + children: [ + Paragraph.text('This is a children'), + ], + ); + + expect( + block.content, + allOf([ + isList, + isNotEmpty, + hasLength( + 1, + ) + ])); + expect(block.content.first.annotations!.bold, isTrue); + expect( + block.children, + allOf([ + isList, + isNotEmpty, + hasLength(1), + ])); + }); + test('Create an instance with information', () { Toggle block = Toggle(text: Text('A')) .addText('B') diff --git a/test/general_test.dart b/test/general_test.dart index 236db4a..90b7d9e 100644 --- a/test/general_test.dart +++ b/test/general_test.dart @@ -1,11 +1,11 @@ -import 'package:notion_api/notion/general/base_fields.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; +import 'package:notion_api/notion_api.dart'; +import 'package:notion_api/src/notion/objects/object.dart'; import 'package:test/test.dart'; void main() { group('Base fields tests =>', () { test('Create an instance from json', () { - BaseFields base = BaseFields.fromJson({ + Object base = Object.fromJson({ "object": "database", "id": "386da3c6-46bb-4581-8807-1fdb2fbbf447", "created_time": "2021-05-19T20:21:11.420Z", @@ -15,8 +15,9 @@ void main() { expect(base.object, ObjectTypes.Database); expect(base.id, "386da3c6-46bb-4581-8807-1fdb2fbbf447"); - expect(base.createdTime, "2021-05-19T20:21:11.420Z"); - expect(base.lastEditedTime, "2021-06-07T23:02:00.000Z"); + expect(base.createdTime!.toIso8601String(), "2021-05-19T20:21:11.420Z"); + expect( + base.lastEditedTime!.toIso8601String(), "2021-06-07T23:02:00.000Z"); }); }); } diff --git a/test/lists/children_test.dart b/test/lists/children_test.dart index a1f7b79..8ad09e2 100644 --- a/test/lists/children_test.dart +++ b/test/lists/children_test.dart @@ -1,10 +1,4 @@ -import 'package:notion_api/notion/blocks/block.dart'; -import 'package:notion_api/notion/blocks/heading.dart'; -import 'package:notion_api/notion/blocks/paragraph.dart'; -import 'package:notion_api/notion/blocks/todo.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/general/lists/children.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; import '../long_data.dart'; @@ -86,21 +80,23 @@ void main() { }); test('Add blocks in distinct ways', () { - Children deprecated = Children( - heading: Heading(text: Text('Test')), - paragraph: Paragraph( - texts: [ - Text('Lorem ipsum (A)'), - Text( - 'Lorem ipsum (B)', - annotations: TextAnnotations( - bold: true, - underline: true, - color: ColorsTypes.Orange, + Children children = Children( + blocks: [ + Heading(text: Text('Test')), + Paragraph( + texts: [ + Text('Lorem ipsum (A)'), + Text( + 'Lorem ipsum (B)', + annotations: TextAnnotations( + bold: true, + underline: true, + color: ColorsTypes.Orange, + ), ), - ), - ], - ), + ], + ), + ], ); Children children1 = Children.withBlocks([ @@ -144,7 +140,7 @@ void main() { ]) ]); - var json0 = deprecated.toJson(); + var json0 = children.toJson(); var json1 = children1.toJson(); var json2 = children2.toJson(); var json3 = children3.toJson(); diff --git a/test/lists/pagination_test.dart b/test/lists/pagination_test.dart index 71a8054..aa318c0 100644 --- a/test/lists/pagination_test.dart +++ b/test/lists/pagination_test.dart @@ -1,7 +1,4 @@ -import 'package:notion_api/notion/blocks/block.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/general/lists/pagination.dart'; -import 'package:notion_api/notion/objects/database.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; import '../long_data.dart'; @@ -26,13 +23,23 @@ void main() { "results": [ { "object": "database", - "id": "386da3c6-46bb-4581-8807-1fdb2fbbf447", - "created_time": "2021-05-19T20:21:11.420Z", - "last_edited_time": "2021-06-07T23:02:00.000Z", + "id": "386da3c6-46bb-4581-8877-1fdb2fbbf447", + "cover": null, + "icon": null, + "created_time": "2021-05-19T20:21:00.000Z", + "created_by": { + "object": "user", + "id": "cfea802c-a071-46ff-ac1f-81713e43d15b" + }, + "last_edited_by": { + "object": "user", + "id": "59457011-46b2-4542-8a0b-2ee2602ee02c" + }, + "last_edited_time": "2022-02-24T06:18:00.000Z", "title": [ { "type": "text", - "text": {"content": "test", "link": null}, + "text": {"content": "New Title", "link": null}, "annotations": { "bold": false, "italic": false, @@ -41,18 +48,24 @@ void main() { "code": false, "color": "default" }, - "plain_text": "test", + "plain_text": "New Title", "href": null } ], "properties": { - "Tags": { - "id": ">cp;", - "type": "multi_select", - "multi_select": {"options": []} - }, - "This is a test": {"id": "title", "type": "title", "title": {}} - } + "Title": { + "id": "title", + "name": "Title", + "type": "title", + "title": {} + } + }, + "parent": { + "type": "page_id", + "page_id": "ba3e9659-1de0-4c93-b3ad-78b9b1de5007" + }, + "url": "https://www.notion.so/386da3c646bb458188071fdb1fbbf447", + "archived": false } ], "next_cursor": null, @@ -70,13 +83,23 @@ void main() { "results": [ { "object": "database", - "id": "386da3c6-46bb-4581-8807-1fdb2fbbf447", - "created_time": "2021-05-19T20:21:11.420Z", - "last_edited_time": "2021-06-07T23:02:00.000Z", + "id": "386da3c6-46bb-4581-8807-1fdb2fbbf446", + "cover": null, + "icon": null, + "created_time": "2021-05-19T20:21:00.000Z", + "created_by": { + "object": "user", + "id": "cfea802c-a071-46ff-ac1f-81713e43d15b" + }, + "last_edited_by": { + "object": "user", + "id": "59457011-46b2-4542-8a0b-2ee2602ee02c" + }, + "last_edited_time": "2022-02-24T06:18:00.000Z", "title": [ { "type": "text", - "text": {"content": "test", "link": null}, + "text": {"content": "New Title", "link": null}, "annotations": { "bold": false, "italic": false, @@ -85,28 +108,44 @@ void main() { "code": false, "color": "default" }, - "plain_text": "test", + "plain_text": "New Title", "href": null } ], "properties": { - "Tags": { - "id": ">cp;", - "type": "multi_select", - "multi_select": {"options": []} - }, - "This is a test": {"id": "title", "type": "title", "title": {}} - } + "Title": { + "id": "title", + "name": "Title", + "type": "title", + "title": {} + } + }, + "parent": { + "type": "page_id", + "page_id": "ba3e9659-1de0-4c93-b3ad-78b9b1de5007" + }, + "url": "https://www.notion.so/386da3c646bb458188071fdb1fbbf448", + "archived": false }, { "object": "database", - "id": "386da3c6-46bb-4581-8807-1fdb2fbbf446", - "created_time": "2021-05-19T20:21:11.420Z", - "last_edited_time": "2021-06-07T23:02:00.000Z", + "id": "386da3c6-46bb-4581-8807-1fdb2fbbf448", + "cover": null, + "icon": null, + "created_time": "2021-05-19T20:21:00.000Z", + "created_by": { + "object": "user", + "id": "cfea802c-a071-46ff-ac1f-81713e43d15b" + }, + "last_edited_by": { + "object": "user", + "id": "59457011-46b2-4542-8a0b-2ee2602ee02c" + }, + "last_edited_time": "2022-02-24T06:18:00.000Z", "title": [ { "type": "text", - "text": {"content": "test", "link": null}, + "text": {"content": "New Title", "link": null}, "annotations": { "bold": false, "italic": false, @@ -115,18 +154,24 @@ void main() { "code": false, "color": "default" }, - "plain_text": "test", + "plain_text": "New Title", "href": null } ], "properties": { - "Tags": { - "id": ">cp;", - "type": "multi_select", - "multi_select": {"options": []} - }, - "This is a test": {"id": "title", "type": "title", "title": {}} - } + "Title": { + "id": "title", + "name": "Title", + "type": "title", + "title": {} + } + }, + "parent": { + "type": "page_id", + "page_id": "ba3e9659-1de0-4c93-b3ad-78b9b1de5007" + }, + "url": "https://www.notion.so/386da3c646bb458188071fdb1fbbf447", + "archived": false } ], "next_cursor": null, @@ -136,9 +181,9 @@ void main() { List exclude = pag .filterDatabases(exclude: ['386da3c6-46bb-4581-8807-1fdb2fbbf446']); List include = pag - .filterDatabases(include: ['386da3c6-46bb-4581-8807-1fdb2fbbf447']); + .filterDatabases(include: ['386da3c6-46bb-4581-8807-1fdb2fbbf448']); List id = - pag.filterDatabases(id: '386da3c6-46bb-4581-8807-1fdb2fbbf447'); + pag.filterDatabases(id: '386da3c6-46bb-4581-8807-1fdb2fbbf448'); expect(exclude.length, lessThan(pag.list.length)); expect(include.length, lessThan(pag.list.length)); @@ -160,9 +205,17 @@ void main() { test('Blocks list with filter', () async { Pagination pag = Pagination.fromJson(longBlocksJsonList); - List filtered = pag.filterBlocks(onlyLeft: BlockTypes.H1); + List exclude = pag.filterBlocks(exclude: [BlockTypes.H1]); + List onlyLeft = pag.filterBlocks(onlyLeft: BlockTypes.H1); + List include = pag.filterBlocks(include: [BlockTypes.H1]); + List single = + pag.filterBlocks(id: '71fa679a-f072-4e70-bf52-6b1e770f5c3c'); - expect(filtered.length, lessThan(pag.list.length)); + expect(exclude.length, lessThan(pag.list.length)); + expect(onlyLeft.length, lessThan(pag.list.length)); + expect(onlyLeft, include); + expect(single.length, 1); + expect(single.first.type, BlockTypes.Paragraph); }); test('Wrong json', () { diff --git a/test/lists/properties_test.dart b/test/lists/properties_test.dart index efb2e72..5a232ab 100644 --- a/test/lists/properties_test.dart +++ b/test/lists/properties_test.dart @@ -1,4 +1,4 @@ -import 'package:notion_api/notion/general/lists/properties.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; void main() { diff --git a/test/notion_api_test.dart b/test/notion_api_test.dart index 71999a8..10754e5 100644 --- a/test/notion_api_test.dart +++ b/test/notion_api_test.dart @@ -1,25 +1,8 @@ import 'dart:io' show Platform; import 'package:dotenv/dotenv.dart' show load, env, clean; -import 'package:notion_api/notion/blocks/bulleted_list_item.dart'; -import 'package:notion_api/notion/blocks/heading.dart'; -import 'package:notion_api/notion/blocks/numbered_list_item.dart'; -import 'package:notion_api/notion/blocks/paragraph.dart'; -import 'package:notion_api/notion/blocks/todo.dart'; -import 'package:notion_api/notion/blocks/toggle.dart'; -import 'package:notion_api/notion/general/lists/properties.dart'; -import 'package:notion_api/notion/general/property.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/general/lists/children.dart'; -import 'package:notion_api/notion/objects/database.dart'; -import 'package:notion_api/notion/objects/pages.dart'; -import 'package:notion_api/notion.dart'; -import 'package:notion_api/notion/objects/parent.dart'; -import 'package:notion_api/notion_blocks.dart'; -import 'package:notion_api/notion_databases.dart'; -import 'package:notion_api/notion_pages.dart'; -import 'package:notion_api/responses/notion_response.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; +import 'package:notion_api/notion_api.dart'; +import 'package:notion_api/src/notion/new/database/database_property.dart'; import 'package:test/test.dart'; void main() { @@ -46,8 +29,9 @@ void main() { group('Notion Client', () { test('Retrieve a page', () async { - final NotionClient notion = NotionClient(token: token ?? ''); - NotionResponse res = await notion.pages.fetch(testPageId ?? ''); + final Client notion = Client(auth: token ?? ''); + NotionResponse res = + await notion.pages.retrieve(page_id: testPageId ?? ''); expect(res.status, 200); expect(res.isPage, true); @@ -58,8 +42,20 @@ void main() { }); group('Notion Pages Client =>', () { + test('Retrieve a page content', () async { + final Client notion = Client(auth: token ?? ''); + NotionResponse res = + await notion.pages.retrieve(page_id: testPageId ?? ''); + + expect(res.status, 200); + expect(res.isPage, true); + expect(res.content, isNotNull); + expect(res.content, isA()); + expect(res.isOk, true); + }); + test('Create a page', () async { - final NotionPagesClient pages = NotionPagesClient(token: token ?? ''); + final NotionPagesClient pages = NotionPagesClient(auth: token ?? ''); final Page page = Page( parent: Parent.database(id: testDatabaseId ?? ''), @@ -72,7 +68,7 @@ void main() { }); test('Create a page with default title', () async { - final NotionPagesClient pages = NotionPagesClient(token: token ?? ''); + final NotionPagesClient pages = NotionPagesClient(auth: token ?? ''); final Page page = Page( parent: Parent.database(id: testDatabaseId ?? ''), @@ -84,95 +80,101 @@ void main() { }); test('Create a page with full example', () async { - Children fullContent = Children.withBlocks([ - Heading(text: Text('This the title')), - Paragraph(texts: [ - Text( - 'Here you can write all the content of the paragraph but if you want to have another style for a single word you will have to do ', - ), - Text( - 'this. ', - annotations: TextAnnotations( - color: ColorsTypes.Green, - bold: true, - italic: true, - ), - ), - Text( - 'Then you can continue writing all your content. See that if you separate the paragraph to stylized some parts you have to take in count the spaces because the ', - ), - Text('textSeparator', annotations: TextAnnotations(code: true)), - Text( - ' will be deprecated. Maybe you will see this with extra spaces because the separator but soon will be remove.') - ], children: [ - Heading( - text: Text('This is a subtitle for the paragraph'), - type: 2, - ), + Children fullContent = Children( + blocks: [ + Heading(text: Text('Examples')), Paragraph(texts: [ Text( - 'You can also have children for some blocks like ', + 'Here you can write all the content of the paragraph but if you want to have another style for a single word you will have to do ', ), Text( - 'Paragraph', - annotations: TextAnnotations(code: true), + 'this. ', + annotations: TextAnnotations( + color: ColorsTypes.Green, + bold: true, + italic: true, + ), ), - Text(', '), Text( - 'ToDo', - annotations: TextAnnotations(code: true), + 'Then you can continue writing all your content. See that if you separate the paragraph to stylized some parts you have to take in count the spaces.', ), - Text(', '), - Text( - 'BulletedListItems', - annotations: TextAnnotations(code: true), + ], children: [ + Heading.text('Children subtitle', type: 2), + Paragraph(texts: [ + Text( + 'You can also have children for some blocks like ', + ), + ...Text.list(texts: [ + Text.code('Paragraph'), + Text.code('ToDo'), + Text.code('BulletedListItems'), + Text.code('NumberedListItems'), + ], lastSeparator: ' or '), + Text('.'), + ]), + Paragraph.text( + 'Also, if your paragraph will have the same style you can write all your text directly like this to avoid using a list.', ), - Text(' or '), - Text( - 'NumberedListItems', - annotations: TextAnnotations(code: true), + Paragraph( + texts: [ + Text('You can use styles for texts like: '), + ...Text.list(texts: [ + Text.color('green text', color: ColorsTypes.Green), + Text.color('blue text', color: ColorsTypes.Blue), + Text.color('red text', color: ColorsTypes.Red), + Text.color('purple text', color: ColorsTypes.Purple), + Text.underline('underline text'), + Text.code('code format text'), + Text.italic('italic text'), + Text('strikethrough text', + annotations: TextAnnotations(strikethrough: true)), + Text( + 'mix styles', + annotations: TextAnnotations( + bold: true, + italic: true, + underline: true, + color: ColorsTypes.Orange, + ), + ), + ], lastSeparator: ' or '), + Text('!') + ], ), - Text('.'), ]), - Paragraph( - text: Text( - 'Also, if your paragraph will have the same style you can write all your text directly like this to avoid using a list.', - ), - ), - ]), - Heading(text: Text('Blocks'), type: 2), - Heading(text: Text('ToDo'), type: 3), - ToDo(text: Text('Daily meeting'), checked: true), - ToDo(text: Text('Clean the house')), - ToDo(text: Text('Do the laundry')), - ToDo(text: Text('Call mom'), children: [ - Paragraph(texts: [ - Text('Note: ', annotations: TextAnnotations(bold: true)), - Text('Remember to call her before 20:00'), + Heading.text('Blocks', type: 2), + Heading.text('ToDo', type: 3), + ToDo.text('Daily meeting', checked: true), + ToDo.text('Clean the house'), + ToDo.text('Do the laundry'), + ToDo.text('Call mom', children: [ + Paragraph(texts: [ + Text.bold('Note: '), + Text('Remember to call her before 20:00'), + ]), ]), - ]), - Heading(text: Text('Lists'), type: 3), - BulletedListItem(text: Text('Milk')), - BulletedListItem(text: Text('Cereal')), - BulletedListItem(text: Text('Eggs')), - BulletedListItem(text: Text('Tortillas of course')), - Paragraph( - text: Text('The numbered list are ordered by default by notion.'), - ), - NumberedListItem(text: Text('Notion')), - NumberedListItem(text: Text('Keep by Google')), - NumberedListItem(text: Text('Evernote')), - Heading(text: Text('Toggle'), type: 3), - Toggle(text: Text('Toogle items'), children: [ - Paragraph( - text: Text( - 'Toogle items are blocks that can show or hide their children, and their children can be any other block.', - ), + Heading.text('Lists', type: 3), + BulletedListItem.text('Milk'), + BulletedListItem.text('Cereal'), + BulletedListItem.text('Eggs'), + BulletedListItem.text('Tortillas of course'), + Paragraph.text('The numbered list are ordered by default by notion.'), + NumberedListItem.text('Notion'), + NumberedListItem.text('Keep by Google'), + NumberedListItem.text('Evernote'), + Heading.text('Toggle', type: 3), + Toggle.text( + 'Toogle items', + children: [ + Paragraph.text( + 'Toogle items are blocks that can show or hide their children, and their children can be any other block.', + ), + ], ), - ]) - ]); + ], + ); - final NotionClient notion = NotionClient(token: token ?? ''); + final Client notion = Client(auth: token ?? ''); final Page page = Page( parent: Parent.database(id: testDatabaseId ?? ''), @@ -184,7 +186,7 @@ void main() { String newPageId = newPage.page!.id; var res = await notion.blocks.append( - to: newPageId, + block_id: newPageId, children: fullContent, ); @@ -192,9 +194,10 @@ void main() { }); test('Update a page (properties)', () async { - final NotionPagesClient pages = NotionPagesClient(token: token ?? ''); + final NotionPagesClient pages = NotionPagesClient(auth: token ?? ''); - var res = await pages.update('15db928d5d2a43ada59e3136663d41f6', + var res = await pages.update( + page_id: '15db928d5d2a43ada59e3136663d41f6', properties: Properties(map: { 'Property': RichTextProp(content: [Text('A')]) })); @@ -203,31 +206,53 @@ void main() { }); test('Update a page (archived)', () async { - final NotionPagesClient pages = NotionPagesClient(token: token ?? ''); + final NotionPagesClient pages = NotionPagesClient(auth: token ?? ''); - var res = await pages.update('15db928d5d2a43ada59e3136663d41f6', - archived: false); + var res = await pages.update( + page_id: '15db928d5d2a43ada59e3136663d41f6', archived: false); expect(res.status, 200); }); }); group('Notion Databases Client =>', () { - test('Retrieve a database', () async { - final NotionDatabasesClient databases = - NotionDatabasesClient(token: token ?? ''); - - NotionResponse res = await databases.fetch(testDatabaseId ?? ''); - - expect(res.status, 200); - expect(res.isOk, true); + group('Retrieve', () { + test('Retrieve a database', () async { + final NotionDatabasesClient databases = + NotionDatabasesClient(auth: token ?? ''); + + NotionResponse res = + await databases.retrieve(databaseId: testDatabaseId ?? ''); + + /// TODO: Separate test and retrieve database on a `setUp`. + expect(res.status, 200); + expect(res.isOk, isTrue); + expect(res.isDatabase, isTrue); + expect(res.content, isA()); + + // Can read the title + expect(res.database!.title, 'New Title'); + + // Can read properties main column. + expect(res.database!.properties.getByName('This is a test'), + isA()); + + // Can read multiselect property options + expect( + res.database!.properties + .getByName('Tags') + .asMultiSelect + .options + .length, + 1); + }); }); test('Retrieve all databases', () async { final NotionDatabasesClient databases = - NotionDatabasesClient(token: token ?? ''); + NotionDatabasesClient(auth: token ?? ''); - NotionResponse res = await databases.fetchAll(); + NotionResponse res = await databases.list(); expect(res.status, 200); expect(res.isOk, true); @@ -235,9 +260,9 @@ void main() { test('Retrieve all databases with wrong query', () async { final NotionDatabasesClient databases = - NotionDatabasesClient(token: token ?? ''); + NotionDatabasesClient(auth: token ?? ''); - NotionResponse res = await databases.fetchAll(startCursor: ''); + NotionResponse res = await databases.list(startCursor: ''); expect(res.status, 400); expect(res.code, 'validation_error'); @@ -247,45 +272,67 @@ void main() { test('Retrieve all databases with query', () async { final NotionDatabasesClient databases = - NotionDatabasesClient(token: token ?? ''); + NotionDatabasesClient(auth: token ?? ''); const int limit = 2; - NotionResponse res = await databases.fetchAll(pageSize: limit); + NotionResponse res = await databases.list(pageSize: limit); expect(res.isOk, true); expect(res.isList, true); expect(res.content.length, lessThanOrEqualTo(limit)); }); - test('Create a database', () async { - final NotionDatabasesClient databases = - NotionDatabasesClient(token: token ?? ''); + group('Create', () { + test('Create a database with title', () async { + final NotionDatabasesClient databases = + NotionDatabasesClient(auth: token ?? ''); + + NotionResponse res = await databases.create( + pageId: testPageId ?? '', + title: 'Database from test', + properties: DatabaseProperties( + mainColumnName: 'ABC', + properties: { + 'Description': DatabaseProperty.RichText(), + 'Done': DatabaseProperty.Checkbox(), + }, + ), + ); - NotionResponse res = await databases.create(Database.newDatabase( - parent: Parent.page(id: testPageId ?? ''), - title: [ - Text('Database from test'), - ], - pagesColumnName: 'Custom pages column', - properties: Properties(map: { - 'Description': MultiSelectProp(options: [ - MultiSelectOption(name: 'Read', color: ColorsTypes.Blue), - MultiSelectOption(name: 'Sleep', color: ColorsTypes.Green), - ]) - }), - )); + expect(res.status, 200); + expect(res.isOk, true); + }); - expect(res.status, 200); - expect(res.isOk, true); + test('Create a database without title', () async { + final NotionDatabasesClient databases = + NotionDatabasesClient(auth: token ?? ''); + + NotionResponse res = await databases.create( + pageId: testPageId ?? '', + properties: DatabaseProperties(mainColumnName: 'Title Column'), + ); + + expect(res.status, 200); + expect(res.isOk, true); + }); }); - test('Create a database with default', () async { + test('Update a database', () async { final NotionDatabasesClient databases = - NotionDatabasesClient(token: token ?? ''); + NotionDatabasesClient(auth: token ?? ''); - NotionResponse res = await databases.create(Database.newDatabase( - parent: Parent.page(id: testPageId ?? ''), - )); + NotionResponse res = await databases.update( + databaseId: '8bd452157e1642dd8aad5734a2372518', + title: [RichText('New Title')], + properties: Properties(map: { + 'Test': MultiSelectProp( + options: [ + MultiSelectOption(name: 'Read', color: ColorsTypes.Blue), + MultiSelectOption(name: 'Sleep', color: ColorsTypes.Green), + ], + ), + 'Column': RichTextProp() + })); expect(res.status, 200); expect(res.isOk, true); @@ -294,19 +341,19 @@ void main() { group('Notion Block Client =>', () { test('Retrieve block children', () async { - final NotionBlockClient blocks = NotionBlockClient(token: token ?? ''); + final NotionBlockClient blocks = NotionBlockClient(auth: token ?? ''); - NotionResponse res = await blocks.fetch(testBlockId ?? ''); + NotionResponse res = await blocks.list(block_id: testBlockId ?? ''); expect(res.status, 200); expect(res.isOk, true); }); test('Retrieve block children with wrong query', () async { - final NotionBlockClient blocks = NotionBlockClient(token: token ?? ''); + final NotionBlockClient blocks = NotionBlockClient(auth: token ?? ''); NotionResponse res = - await blocks.fetch(testBlockId ?? '', startCursor: ''); + await blocks.list(block_id: testBlockId ?? '', startCursor: ''); expect(res.status, 400); expect(res.code, 'validation_error'); @@ -315,120 +362,22 @@ void main() { }); test('Retrieve block children with query', () async { - final NotionBlockClient blocks = NotionBlockClient(token: token ?? ''); + final NotionBlockClient blocks = NotionBlockClient(auth: token ?? ''); const int limit = 2; NotionResponse res = - await blocks.fetch(testBlockId ?? '', pageSize: limit); + await blocks.list(block_id: testBlockId ?? '', pageSize: limit); expect(res.isOk, true); expect(res.isList, true); expect(res.content.length, lessThanOrEqualTo(limit)); }); - test('Append complex stuff', () async { - final NotionBlockClient blocks = NotionBlockClient(token: token ?? ''); - - NotionResponse res = await blocks.append( - to: testBlockId as String, - children: Children.withBlocks([ - Heading(text: Text('This the title')), - Paragraph(texts: [ - Text( - 'Here you can write all the content of the paragraph but if you want to have another style for a single word you will have to do ', - ), - Text( - 'this. ', - annotations: TextAnnotations( - color: ColorsTypes.Green, - bold: true, - italic: true, - ), - ), - Text( - 'Then you can continue writing all your content. See that if you separate the paragraph to stylized some parts you have to take in count the spaces because the ', - ), - Text('textSeparator', annotations: TextAnnotations(code: true)), - Text( - ' will be deprecated. Maybe you will see this with extra spaces because the separator but soon will be remove.') - ], children: [ - Heading( - text: Text('This is a subtitle for the paragraph'), - type: 2, - ), - Paragraph(texts: [ - Text( - 'You can also have children for some blocks like ', - ), - Text( - 'Paragraph', - annotations: TextAnnotations(code: true), - ), - Text(', '), - Text( - 'ToDo', - annotations: TextAnnotations(code: true), - ), - Text(', '), - Text( - 'BulletedListItems', - annotations: TextAnnotations(code: true), - ), - Text(' or '), - Text( - 'NumberedListItems', - annotations: TextAnnotations(code: true), - ), - Text('.'), - ]), - Paragraph( - text: Text( - 'Also, if your paragraph will have the same style you can write all your text directly like this to avoid using a list.', - ), - ), - ]), - Heading(text: Text('Blocks'), type: 2), - Heading(text: Text('ToDo'), type: 3), - ToDo(text: Text('Daily meeting'), checked: true), - ToDo(text: Text('Clean the house')), - ToDo(text: Text('Do the laundry')), - ToDo(text: Text('Call mom'), children: [ - Paragraph(texts: [ - Text('Note: ', annotations: TextAnnotations(bold: true)), - Text('Remember to call her before 20:00'), - ]), - ]), - Heading(text: Text('Lists'), type: 3), - BulletedListItem(text: Text('Milk')), - BulletedListItem(text: Text('Cereal')), - BulletedListItem(text: Text('Eggs')), - BulletedListItem(text: Text('Tortillas of course')), - Paragraph( - text: Text('The numbered list are ordered by default by notion.'), - ), - NumberedListItem(text: Text('Notion')), - NumberedListItem(text: Text('Keep by Google')), - NumberedListItem(text: Text('Evernote')), - Heading(text: Text('Toggle'), type: 3), - Toggle(text: Text('Toogle items'), children: [ - Paragraph( - text: Text( - 'Toogle items are blocks that can show or hide their children, and their children can be any other block.', - ), - ), - ]) - ]), - ); - - expect(res.status, 200); - expect(res.isOk, true); - }); - test('Append heading & text', () async { - final NotionBlockClient blocks = NotionBlockClient(token: token ?? ''); + final NotionBlockClient blocks = NotionBlockClient(auth: token ?? ''); NotionResponse res = await blocks.append( - to: testBlockId as String, + block_id: testBlockId as String, children: Children.withBlocks([ Heading(text: Text('Test')), Paragraph(texts: [ @@ -452,10 +401,10 @@ void main() { }); test('Append todo block', () async { - final NotionBlockClient blocks = NotionBlockClient(token: token ?? ''); + final NotionBlockClient blocks = NotionBlockClient(auth: token ?? ''); NotionResponse res = await blocks.append( - to: testBlockId as String, + block_id: testBlockId as String, children: Children.withBlocks([ ToDo(text: Text('This is a todo item A')), ToDo( @@ -479,10 +428,10 @@ void main() { }); test('Append bulleted list item block', () async { - final NotionBlockClient blocks = NotionBlockClient(token: token ?? ''); + final NotionBlockClient blocks = NotionBlockClient(auth: token ?? ''); NotionResponse res = await blocks.append( - to: testBlockId as String, + block_id: testBlockId as String, children: Children.withBlocks( [ BulletedListItem(text: Text('This is a bulleted list item A')), @@ -506,53 +455,55 @@ void main() { }); test('Colors', () async { - final NotionBlockClient blocks = NotionBlockClient(token: token ?? ''); + final NotionBlockClient blocks = NotionBlockClient(auth: token ?? ''); NotionResponse res = await blocks.append( - to: testBlockId as String, + block_id: testBlockId as String, children: Children.withBlocks( [ Paragraph( texts: [ - Text( - 'gray', - annotations: TextAnnotations(color: ColorsTypes.Gray), - ), - Text( - 'brown', - annotations: TextAnnotations(color: ColorsTypes.Brown), - ), - Text( - 'orange', - annotations: TextAnnotations(color: ColorsTypes.Orange), - ), - Text( - 'yellow', - annotations: TextAnnotations(color: ColorsTypes.Yellow), - ), - Text( - 'green', - annotations: TextAnnotations(color: ColorsTypes.Green), - ), - Text( - 'blue', - annotations: TextAnnotations(color: ColorsTypes.Blue), - ), - Text( - 'purple', - annotations: TextAnnotations(color: ColorsTypes.Purple), - ), - Text( - 'pink', - annotations: TextAnnotations(color: ColorsTypes.Pink), - ), - Text( - 'red', - annotations: TextAnnotations(color: ColorsTypes.Red), - ), - Text( - 'default', - annotations: TextAnnotations(color: ColorsTypes.Default), - ), + ...Text.list(texts: [ + Text( + 'gray', + annotations: TextAnnotations(color: ColorsTypes.Gray), + ), + Text( + 'brown', + annotations: TextAnnotations(color: ColorsTypes.Brown), + ), + Text( + 'orange', + annotations: TextAnnotations(color: ColorsTypes.Orange), + ), + Text( + 'yellow', + annotations: TextAnnotations(color: ColorsTypes.Yellow), + ), + Text( + 'green', + annotations: TextAnnotations(color: ColorsTypes.Green), + ), + Text( + 'blue', + annotations: TextAnnotations(color: ColorsTypes.Blue), + ), + Text( + 'purple', + annotations: TextAnnotations(color: ColorsTypes.Purple), + ), + Text( + 'pink', + annotations: TextAnnotations(color: ColorsTypes.Pink), + ), + Text( + 'red', + annotations: TextAnnotations(color: ColorsTypes.Red), + ), + Text( + 'default', + annotations: TextAnnotations(color: ColorsTypes.Default), + ), + ]), ], ), ], @@ -564,10 +515,10 @@ void main() { }); test('Append numbered list item block', () async { - final NotionBlockClient blocks = NotionBlockClient(token: token ?? ''); + final NotionBlockClient blocks = NotionBlockClient(auth: token ?? ''); NotionResponse res = await blocks.append( - to: testBlockId as String, + block_id: testBlockId as String, children: Children.withBlocks( [ NumberedListItem(text: Text('This is a numbered list item A')), @@ -596,10 +547,10 @@ void main() { }); test('Append toggle block', () async { - final NotionBlockClient blocks = NotionBlockClient(token: token ?? ''); + final NotionBlockClient blocks = NotionBlockClient(auth: token ?? ''); NotionResponse res = await blocks.append( - to: testBlockId as String, + block_id: testBlockId as String, children: Children.withBlocks( [ Toggle( diff --git a/test/objects/database_test.dart b/test/objects/database_test.dart index 36d74f1..56cf383 100644 --- a/test/objects/database_test.dart +++ b/test/objects/database_test.dart @@ -1,151 +1,227 @@ -import 'package:notion_api/notion/general/property.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/objects/database.dart'; +import 'package:notion_api/notion_api.dart' hide MultiSelectOption; +import 'package:notion_api/src/notion/new/database/database_property.dart'; import 'package:test/test.dart'; void main() { group('Database Instance =>', () { - test('Create new empty instance', () { - Database database = Database(); + group('Create Instance', () { + group('[Default:', () { + const String title = 'DatabaseTitle'; - expect(database, isNotNull); - expect(database.title, isEmpty); - expect(database.properties.entries, isEmpty); - expect(database.object, ObjectTypes.Database); - }); + test('new database instance]', () { + Database database = Database( + parent: Parent.page(id: 'someId'), + title: title, + properties: DatabaseProperties( + mainColumnName: 'Main', + properties: { + 'Details': DatabaseProperty.RichText(), + }, + ), + ); - test('Create new instance with data', () { - Database database = Database(title: [Text('Title')]) - .addProperty( - name: 'Tags', - property: MultiSelectProp(options: [ - MultiSelectOption(name: 'Option A'), - MultiSelectOption(name: 'Option B'), - ])) - .addProperty( - name: 'Details', - property: RichTextProp(content: [ - Text('Detail A'), - Text('Detail B'), - ])) - .addProperty( - name: 'Name', - property: TitleProp(content: [Text('Something here...')])); - - expect(database, isNotNull); - expect(database.title.length, 1); - expect(database.properties.entries, isNotEmpty); - expect(database.properties.getByName('Tags').isMultiSelect, true); - expect(database.properties.getByName('Details').isRichText, true); - expect(database.properties.getByName('Name').isTitle, true); - }); + expect(database.title, title); + expect(database.parent.type, ParentType.Page); + expect(database.properties.entries, + isNotEmpty); // pages column name and 'Details' + }); + }); + + group('[Usage:', () { + test('add individual properties]', () { + Database database = Database.simple( + parent: Parent.page(id: 'someId'), + title: 'DatabaseTitle', + mainColumnName: 'Main', + ); + expect(database.properties.getByName('Main'), isA()); + expect(database.properties.entries.length, 1); + + // add one more property + database.addProperty( + name: 'Tags', + property: DatabaseProperty.MultiSelect( + options: [ + MultiSelectOptionDbProp(name: 'Option A'), + MultiSelectOptionDbProp(name: 'Option B'), + ], + ), + ); + expect( + database.properties.getByName('Tags'), isA()); + expect(database.properties.entries.length, 2); + + // add one more property + database.addProperty( + name: 'Details', + property: DatabaseProperty.RichText(), + ); + expect( + database.properties.getByName('Details'), isA()); + + expect(database.properties.entries.length, 3); + }); + + test('access database properties with type]', () { + var multiSelectOptions = [ + MultiSelectOptionDbProp(name: 'TagA'), + MultiSelectOptionDbProp(name: 'TagB'), + MultiSelectOptionDbProp(name: 'TagC'), + ]; + + Database database = Database( + parent: Parent.page(id: 'someId'), + title: 'DatabaseTitle', + properties: DatabaseProperties( + mainColumnName: 'Main', + properties: { + 'Tags': DatabaseProperty.MultiSelect( + options: [...multiSelectOptions], + ), + }, + ), + ); + + expect(database.parent.type, ParentType.Page); + expect(database.properties.getByName('Tags').asMultiSelect.options, + equals(multiSelectOptions)); // pages column name and 'Details' + }); + + test('create json for request]', () { + Database database = Database( + parent: Parent.page(id: 'someId'), + title: 'DatabaseTitle', + properties: DatabaseProperties( + mainColumnName: 'Main', + properties: { + 'Details': DatabaseProperty.RichText(), + }, + ), + ); + + Map json = database.toRequestJson(); - test('Create json from instance', () { - Map database = Database(title: [Text('Title')]) - .addProperty( - name: 'Tags', - property: MultiSelectProp(options: [ - MultiSelectOption(name: 'Option A'), - MultiSelectOption(name: 'Option B'), - ])) - .addProperty( - name: 'Details', - property: RichTextProp(content: [ - Text('Detail A'), - Text('Detail B'), - ])) - .addProperty( - name: 'Name', - property: TitleProp(content: [Text('Something here...')])) - .toJson(); - - expect(database, isNotNull); - expect(database['object'], 'database'); - expect(database['title'], isList); - expect(database['properties'], isMap); + expect(json, isNotNull); + + // required + expect(json['parent'], allOf([isMap, isNotEmpty])); + expect(json['title'], allOf([isList, isNotEmpty])); + expect(json['properties'], allOf([isMap, isNotEmpty])); + + // not required + expect(json.containsKey('id'), isFalse); + expect(json.containsKey('url'), isFalse); + }); + + test('create full json]', () { + Database database = Database( + parent: Parent.page(id: 'someId'), + title: 'DatabaseTitle', + properties: DatabaseProperties( + mainColumnName: 'Main', + properties: { + 'Details': DatabaseProperty.RichText(), + }, + ), + ); + + Map json = database.toJson(); + + expect(json, isNotNull); + expect(json['object'], 'database'); + expect(json['title'], isList); + expect(json['properties'], isNotEmpty); + expect(json['id'], isEmpty); + expect(json['url'], isEmpty); + }); + }); }); - test('Map from json', () { - var jsonDatabase = { - "object": "database", - "id": "386da3c6-46bb-4581-8807-1fdb2fbbf447", - "created_time": "2021-05-19T20:21:11.420Z", - "last_edited_time": "2021-06-07T23:02:00.000Z", - "title": [ - { - "type": "text", - "text": {"content": "test", "link": null}, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" + group('Read from JSON', () { + group('[Mapping:', () { + String title = 'test'; + late Database database; + setUp(() { + database = Database.fromJson({ + "object": "database", + "id": "386da3c6-46bb-4581-8807-1fdb2fbbf447", + "created_time": "2021-05-19T20:21:11.420Z", + "last_edited_time": "2021-06-07T23:02:00.000Z", + "title": [ + { + "type": "text", + "text": {"content": title, "link": null}, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "test", + "href": null + } + ], + "properties": { + "Tags": { + "id": ">cp;", + "type": "multi_select", + "multi_select": {"options": []} + }, + "Details": {"id": "title", "type": "title", "title": {}} }, - "plain_text": "test", - "href": null - } - ], - "properties": { - "Tags": { - "id": ">cp;", - "type": "multi_select", - "multi_select": {"options": []} - }, - "Details": {"id": "title", "type": "title", "title": {}} - } - }; - - Database database = Database.fromJson(jsonDatabase); - - expect(database.title, isNotEmpty); - expect(database.id, isNotEmpty); - expect(database.properties.contains('Tags'), true); - expect(database.properties.getByName('Tags').isMultiSelect, true); - expect(database.properties.contains('Details'), true); - expect(database.properties.getByName('Details').isTitle, true); - }); + "parent": { + "type": "page_id", + "page_id": "ba3e9659-1de0-4c93-b3ad-78b9b16e5507" + }, + "url": "https://www.notion.so/8bd452157e1642dd8aad5734a2372518", + }); + }); - test('Map from wrong json', () { - Map wrongJsonDatabase = {}; + test('Map full json]', () { + expect(database.title, isNotEmpty); + expect(database.id, isNotEmpty); + expect(database.properties.entries, isNotEmpty); + expect(Uri.tryParse(database.url)?.hasAbsolutePath ?? false, isTrue); + expect(database.properties.contains('Tags'), isTrue); + expect(database.properties.contains('Details'), isTrue); + }); - Database database = Database.fromJson(wrongJsonDatabase); + test('get database title]', () { + expect(database.title, title); + }); - expect(database.title, isEmpty); - expect(database.id, isEmpty); - expect(database.properties.contains('Tags'), false); - expect(database.properties.contains('Details'), false); - }); + test('get database properties]', () { + expect( + database.properties.getByName('Tags'), isA()); + expect( + database.properties.getByName('Tags'), isA()); + expect(database.properties.getByName('Details'), isA()); + }); - test('Add properties from json', () { - Database database = Database().addPropertiesFromJson({ - "Tags": { - "id": ">cp;", - "type": "multi_select", - "multi_select": { - "options": [ - {"name": "A"}, - {"name": "B"} - ] - } - }, - "Details": {"id": "title", "type": "title", "title": {}} + test('access specific properties with type]', () { + expect(database.properties.getByName('Tags').asMultiSelect.options, + isList); + }); }); - expect(database.properties.contains('Tags'), true); - expect(database.properties.getByName('Tags').isMultiSelect, true); - expect( - database.properties.getByName('Tags').value, - allOf([ - isList, - hasLength(2), - isA>(), - ])); - expect(database.properties.contains('Details'), true); - expect(database.properties.getByName('Details').isTitle, true); - expect(database.properties.getByName('Details').value, isList); + group('[Throw exception:', () { + test('for empty json]', () { + expect(() => Database.fromJson({}), throwsA(isA())); + }); + + test('for null required value]', () { + expect( + () => Database.fromJson({ + "object": "database", + "id": "386da3c6-46bb-4581-8807-1fdb2fbbf447", + "created_time": "2021-05-19T20:21:11.420Z", + "last_edited_time": "2021-06-07T23:02:00.000Z", + }), + throwsA(isA())); + }); + }); }); }); } diff --git a/test/objects/page_test.dart b/test/objects/page_test.dart index 3ccfa65..2101cc3 100644 --- a/test/objects/page_test.dart +++ b/test/objects/page_test.dart @@ -1,11 +1,4 @@ -import 'package:notion_api/notion/blocks/heading.dart'; -import 'package:notion_api/notion/general/lists/children.dart'; -import 'package:notion_api/notion/general/property.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/objects/pages.dart'; -import 'package:notion_api/notion/objects/parent.dart'; -import 'package:notion_api/utils/utils.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; void main() { diff --git a/test/property_test.dart b/test/property_test.dart index e69b69e..c7ea7a9 100644 --- a/test/property_test.dart +++ b/test/property_test.dart @@ -1,7 +1,4 @@ -import 'package:notion_api/notion/general/property.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/utils/utils.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; void main() { diff --git a/test/response_test.dart b/test/response_test.dart index 30606cf..664df7a 100644 --- a/test/response_test.dart +++ b/test/response_test.dart @@ -1,18 +1,7 @@ import 'dart:io' show Platform; import 'package:dotenv/dotenv.dart' show load, env, clean; -import 'package:notion_api/notion.dart'; -import 'package:notion_api/notion/blocks/paragraph.dart'; -import 'package:notion_api/notion/general/lists/children.dart'; -import 'package:notion_api/notion/general/property.dart'; -import 'package:notion_api/notion/general/rich_text.dart'; -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/notion/objects/pages.dart'; -import 'package:notion_api/notion/objects/parent.dart'; -import 'package:notion_api/notion_blocks.dart'; -import 'package:notion_api/notion_databases.dart'; -import 'package:notion_api/notion_pages.dart'; -import 'package:notion_api/responses/notion_response.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; void main() { @@ -53,8 +42,7 @@ void main() { }); test('Create an instance from auth error response', () async { - final NotionResponse res = - await NotionClient(token: '').databases.fetchAll(); + final NotionResponse res = await Client(auth: '').databases.list(); expect(res.hasError, true); expect(res.isError, true); @@ -63,11 +51,11 @@ void main() { }); test('Invalid field (children) for block', () async { - final NotionBlockClient blocks = NotionBlockClient(token: token ?? ''); + final NotionBlockClient blocks = NotionBlockClient(auth: token ?? ''); // Heading block do not support children var res = await blocks.append( - to: testBlockHeadingId ?? '', + block_id: testBlockHeadingId ?? '', children: Children.withBlocks( [ Paragraph( @@ -86,7 +74,7 @@ void main() { }); test('Invalid property', () async { - final NotionPagesClient pages = NotionPagesClient(token: token ?? ''); + final NotionPagesClient pages = NotionPagesClient(auth: token ?? ''); final Page page = Page( parent: Parent.database(id: testDatabaseId ?? ''), @@ -103,10 +91,11 @@ void main() { }); test('Wrong uuid for block children', () async { - final NotionBlockClient blocks = NotionBlockClient(token: token ?? ''); + final NotionBlockClient blocks = NotionBlockClient(auth: token ?? ''); - NotionResponse res = await blocks.fetch( - testBlockId != null ? testBlockId!.replaceFirst('d', 'b') : ''); + NotionResponse res = await blocks.list( + block_id: + testBlockId != null ? testBlockId!.replaceFirst('d', 'b') : ''); expect(res.status, 404); expect(res.isOk, false); @@ -119,9 +108,9 @@ void main() { group('Response lists tests =>', () { test('Fetch a list', () async { NotionDatabasesClient databases = - NotionDatabasesClient(token: token ?? ''); + NotionDatabasesClient(auth: token ?? ''); - NotionResponse res = await databases.fetchAll(); + NotionResponse res = await databases.list(); expect(res.status, 200); expect(res.hasError, false); @@ -133,18 +122,18 @@ void main() { test('Fetch databases list', () async { NotionDatabasesClient databases = - NotionDatabasesClient(token: token ?? ''); + NotionDatabasesClient(auth: token ?? ''); - NotionResponse res = await databases.fetchAll(); + NotionResponse res = await databases.list(); expect(res.content.isEmpty, true); expect(res.content.list, isEmpty); }); test('Fetch blocks children', () async { - NotionBlockClient blocks = NotionBlockClient(token: token ?? ''); + NotionBlockClient blocks = NotionBlockClient(auth: token ?? ''); - NotionResponse res = await blocks.fetch(testBlockId ?? ''); + NotionResponse res = await blocks.list(block_id: testBlockId ?? ''); expect(res.content.isEmpty, false); expect(res.content.isBlocksList, true); @@ -156,13 +145,13 @@ void main() { group('Database response test =>', () { test('Instance from response', () async { - NotionDatabasesClient db = NotionDatabasesClient(token: token ?? ''); + NotionDatabasesClient db = NotionDatabasesClient(auth: token ?? ''); - NotionResponse response = await db.fetch(testDatabaseId ?? ''); + NotionResponse response = + await db.retrieve(databaseId: testDatabaseId ?? ''); expect(response.isDatabase, true); - expect(response.content.title, allOf([isList, isNotEmpty, hasLength(1)])); - expect(response.content.title.first.text, 'test'); + expect(response.content.title, 'New Title'); expect(response.content.properties.entries, allOf([isMap, isNotEmpty, hasLength(3)])); }); diff --git a/test/rich_text_test.dart b/test/rich_text_test.dart new file mode 100644 index 0000000..c576fa0 --- /dev/null +++ b/test/rich_text_test.dart @@ -0,0 +1,88 @@ +import 'package:notion_api/notion_api.dart'; +import 'package:test/test.dart'; + +void main() { + group('Rich text tests =>', () { + test('Create a json from stylized instance', () { + Map json = Text( + '', + annotations: TextAnnotations( + bold: true, + strikethrough: true, + ), + ).toJson(); + + expect(json.containsKey('annotations'), true); + expect(json['annotations'].containsKey('italic'), false); + expect(json['annotations'].containsKey('strikethrough'), true); + expect(json['annotations'].containsKey('bold'), true); + expect(json['annotations']['strikethrough'], true); + expect(json['annotations']['bold'], true); + }); + + test('Create instances of Text with default styles', () { + Text bold = + Text.bold('This text is bold and green', color: ColorsTypes.Green); + Text underline = Text.underline('This text is underline'); + Text code = Text.code('This text is code'); + Text italic = Text.italic('This text is italic'); + + Text green = Text.color('This text is green', color: ColorsTypes.Green); + Text blue = Text.color('This text is blue', color: ColorsTypes.Blue); + + expect(bold.annotations!.bold, isTrue); + expect(bold.annotations!.color, ColorsTypes.Green); + expect(underline.annotations!.underline, isTrue); + expect(code.annotations!.code, isTrue); + expect(italic.annotations!.italic, isTrue); + + expect(green.annotations!.color, ColorsTypes.Green); + expect(blue.annotations!.color, ColorsTypes.Blue); + }); + }); + + group('Rich text list test =>', () { + test('Create a list of instances of Text', () { + List list = Text.list(texts: [ + Text('A'), + Text('B'), + Text('C'), + ]); + + expect(list.length, 5); + }); + + test('Create a list of instances of Text (one element)', () { + List list = Text.list(texts: [Text('A')]); + + expect(list.length, 1); + }); + + test('Create a list of instances of Text (two element)', () { + List list = Text.list(texts: [Text('A'), Text('B')]); + + expect(list.length, 3); + expect(list[list.length - 2].text, ' and '); + }); + + test('Create a list of instances of Text (three element)', () { + List list = Text.list(texts: [Text('A'), Text('B'), Text('C')]); + + expect(list.length, 5); + expect(list[list.length - 2].text, ' and '); + expect(list[1].text, ', '); + }); + + test('Create a list of instances of Text (custom separators)', () { + List list = Text.list( + texts: [Text('A'), Text('B'), Text('C')], + separator: '; ', + lastSeparator: ' y ', + ); + + expect(list.length, 5); + expect(list[list.length - 2].text, ' y '); + expect(list[1].text, '; '); + }); + }); +} diff --git a/test/utils_test.dart b/test/utils_test.dart index c227db2..d996237 100644 --- a/test/utils_test.dart +++ b/test/utils_test.dart @@ -1,5 +1,4 @@ -import 'package:notion_api/notion/general/types/notion_types.dart'; -import 'package:notion_api/utils/utils.dart'; +import 'package:notion_api/notion_api.dart'; import 'package:test/test.dart'; void main() {