aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Shashank Pachava <spachava753@gmail.com> 2022-05-18 01:02:56 -0400
committerGravatar GitHub <noreply@github.com> 2022-05-17 22:02:56 -0700
commit8160fea0ffa1f074538ec5fd73b317bc9d023d6e (patch)
treeecd9288aa409051e57695b638f416540eb77cbe5
parenta0106b557ee1af347b7c422be3364dd6d0fac945 (diff)
downloadnotion-8160fea0ffa1f074538ec5fd73b317bc9d023d6e.tar.gz
notion-8160fea0ffa1f074538ec5fd73b317bc9d023d6e.tar.zst
notion-8160fea0ffa1f074538ec5fd73b317bc9d023d6e.zip
Upgrade notion version (#39)
* Upgrade constant * Fix lint * Change module file tree Changed around module file tree, but module structure hasn't changed. Converted models.rs to mod.rs under models folder * Minor refactoring * Add MentionObject for rich text mention types * Add new fields to BlockCommon. Modify existing field model Text. Add heading 1 test Add created_by and last_edited_by fields to BlockCommon. Change field text to rich_text for model Text to handle breaking change in API version 2022-02-22 * Differentiate between unsupported and unknown block types * Change text field to rich_text in paragraph block * Add callout block. Add file and emoji object * Fix as_id for unsupported block * Fix lint issues * Move quote block to follow documentation order. Add color field to TextAndChildren struct * Add color field to ToDoFields struct * Formatting * Add caption field to code block Add caption field to code block. Create enum CodeLanguage for code block. Reorder code block to reflect documentation * Add child database block * Create embed block * Refactor notion file object struct name * Create image block * Create video block * Create file block * Fix video block field * Create pdf block * Change text field to rich_text in TodoFields for Notion API version 2022-02-22 * Create bookmark block * Create divider block * Create table of contents block * Create breadcrumb block * Create column list and column block * Create link preview block * Create template block * Formatting * Create link to page block * Fix ColumnListFields struct * Create table and table row block * Fix AsIdentifier trait impl for Block * Create synced block
-rw-r--r--src/lib.rs8
-rw-r--r--src/models/mod.rs (renamed from src/models.rs)380
-rw-r--r--src/models/properties.rs1
-rw-r--r--src/models/tests.rs449
-rw-r--r--src/models/tests/callout.json43
-rw-r--r--src/models/tests/emoji_object.json4
-rw-r--r--src/models/tests/external_file_object.json6
-rw-r--r--src/models/tests/file_object.json7
-rw-r--r--src/models/tests/heading_1.json175
-rw-r--r--src/models/tests/rich_text_mention_date.json21
-rw-r--r--src/models/tests/rich_text_mention_date_with_end.json21
-rw-r--r--src/models/tests/rich_text_mention_date_with_end_and_time.json21
-rw-r--r--src/models/tests/rich_text_mention_date_with_time.json21
-rw-r--r--src/models/tests/rich_text_mention_user_person.json26
-rw-r--r--src/models/tests/rich_text_text.json19
-rw-r--r--src/models/text.rs31
16 files changed, 1189 insertions, 44 deletions
diff --git a/src/lib.rs b/src/lib.rs
index ac45a35..ae2ed46 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -9,19 +9,17 @@ use tracing::Instrument;
pub mod ids;
pub mod models;
-pub use chrono;
-#[cfg(test)]
-mod tests;
+pub use chrono;
-const NOTION_API_VERSION: &str = "2021-08-16";
+const NOTION_API_VERSION: &str = "2022-02-22";
/// An wrapper Error type for all errors produced by the [`NotionApi`](NotionApi) client.
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Invalid Notion API Token: {}", source)]
InvalidApiToken {
- source: reqwest::header::InvalidHeaderValue,
+ source: header::InvalidHeaderValue,
},
#[error("Unable to build reqwest HTTP client: {}", source)]
diff --git a/src/models.rs b/src/models/mod.rs
index 9eab369..175f3c5 100644
--- a/src/models.rs
+++ b/src/models/mod.rs
@@ -2,11 +2,13 @@ pub mod error;
pub mod paging;
pub mod properties;
pub mod search;
+#[cfg(test)]
+mod tests;
pub mod text;
pub mod users;
use crate::models::properties::{PropertyConfiguration, PropertyValue};
-use crate::models::text::RichText;
+use crate::models::text::{RichText, TextColor};
use crate::Error;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
@@ -14,7 +16,7 @@ use std::collections::HashMap;
use crate::ids::{AsIdentifier, BlockId, DatabaseId, PageId};
use crate::models::error::ErrorResponse;
use crate::models::paging::PagingCursor;
-use crate::models::users::User;
+use crate::models::users::{User, UserCommon};
pub use chrono::{DateTime, Utc};
pub use serde_json::value::Number;
@@ -203,24 +205,64 @@ pub struct BlockCommon {
pub created_time: DateTime<Utc>,
pub last_edited_time: DateTime<Utc>,
pub has_children: bool,
+ pub created_by: UserCommon,
+ pub last_edited_by: UserCommon,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct TextAndChildren {
- pub text: Vec<RichText>,
+ pub rich_text: Vec<RichText>,
pub children: Option<Vec<Block>>,
+ pub color: TextColor,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct Text {
- pub text: Vec<RichText>,
+ pub rich_text: Vec<RichText>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct InternalFileObject {
+ url: String,
+ expiry_time: DateTime<Utc>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct ExternalFileObject {
+ url: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[serde(tag = "type")]
+#[serde(rename_all = "snake_case")]
+pub enum FileOrEmojiObject {
+ Emoji { emoji: String },
+ File { file: InternalFileObject },
+ External { external: ExternalFileObject },
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[serde(tag = "type")]
+#[serde(rename_all = "snake_case")]
+pub enum FileObject {
+ File { file: InternalFileObject },
+ External { external: ExternalFileObject },
+}
+
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct Callout {
+ pub rich_text: Vec<RichText>,
+ pub icon: FileOrEmojiObject,
+ pub color: TextColor,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct ToDoFields {
- pub text: Vec<RichText>,
+ pub rich_text: Vec<RichText>,
pub checked: bool,
pub children: Option<Vec<Block>>,
+ pub color: TextColor,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
@@ -229,9 +271,111 @@ pub struct ChildPageFields {
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct ChildDatabaseFields {
+ pub title: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct EmbedFields {
+ pub url: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct BookmarkFields {
+ pub url: String,
+ pub caption: Vec<RichText>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[serde(rename_all = "lowercase")]
+pub enum CodeLanguage {
+ Abap,
+ Arduino,
+ Bash,
+ Basic,
+ C,
+ Clojure,
+ Coffeescript,
+ #[serde(rename = "c++")]
+ CPlusPlus,
+ #[serde(rename = "c#")]
+ CSharp,
+ Css,
+ Dart,
+ Diff,
+ Docker,
+ Elixir,
+ Elm,
+ Erlang,
+ Flow,
+ Fortran,
+ #[serde(rename = "f#")]
+ FSharp,
+ Gherkin,
+ Glsl,
+ Go,
+ Graphql,
+ Groovy,
+ Haskell,
+ Html,
+ Java,
+ Javascript,
+ Json,
+ Julia,
+ Kotlin,
+ Latex,
+ Less,
+ Lisp,
+ Livescript,
+ Lua,
+ Makefile,
+ Markdown,
+ Markup,
+ Matlab,
+ Mermaid,
+ Nix,
+ #[serde(rename = "objective-c")]
+ ObjectiveC,
+ Ocaml,
+ Pascal,
+ Perl,
+ Php,
+ #[serde(rename = "plain text")]
+ PlainText,
+ Powershell,
+ Prolog,
+ Protobuf,
+ Python,
+ R,
+ Reason,
+ Ruby,
+ Rust,
+ Sass,
+ Scala,
+ Scheme,
+ Scss,
+ Shell,
+ Sql,
+ Swift,
+ Typescript,
+ #[serde(rename = "vb.net")]
+ VbNet,
+ Verilog,
+ Vhdl,
+ #[serde(rename = "visual basic")]
+ VisualBasic,
+ Webassembly,
+ Xml,
+ Yaml,
+ #[serde(rename = "java/c/c++/c#")]
+ JavaCAndCPlusPlusAndCSharp,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct CodeFields {
- pub text: Vec<RichText>,
- pub language: String,
+ pub rich_text: Vec<RichText>,
+ pub caption: Vec<RichText>,
+ pub language: CodeLanguage,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
@@ -240,6 +384,68 @@ pub struct Equation {
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct TableOfContents {
+ pub color: TextColor,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct ColumnListFields {
+ pub children: Vec<Block>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct ColumnFields {
+ pub children: Vec<Block>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct LinkPreviewFields {
+ pub url: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct TemplateFields {
+ pub rich_text: Vec<RichText>,
+ pub children: Vec<Block>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[serde(tag = "type")]
+#[serde(rename_all = "snake_case")]
+pub enum LinkToPageFields {
+ PageId {
+ page_id: PageId
+ },
+ DatabaseId {
+ database_id: DatabaseId
+ },
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct SyncedFromObject {
+ pub block_id: BlockId,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct SyncedBlockFields {
+ pub synced_from: Option<SyncedFromObject>,
+ pub children: Vec<Block>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct TableFields {
+ pub table_width: u64,
+ pub has_column_header: bool,
+ pub has_row_header: bool,
+ pub children: Vec<Block>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct TableRowFields {
+ pub cells: Vec<RichText>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
pub enum Block {
@@ -266,6 +472,16 @@ pub enum Block {
common: BlockCommon,
heading_3: Text,
},
+ Callout {
+ #[serde(flatten)]
+ common: BlockCommon,
+ callout: Callout,
+ },
+ Quote {
+ #[serde(flatten)]
+ common: BlockCommon,
+ quote: TextAndChildren,
+ },
BulletedListItem {
#[serde(flatten)]
common: BlockCommon,
@@ -286,28 +502,116 @@ pub enum Block {
common: BlockCommon,
toggle: TextAndChildren,
},
+ Code {
+ #[serde(flatten)]
+ common: BlockCommon,
+ code: CodeFields,
+ },
ChildPage {
#[serde(flatten)]
common: BlockCommon,
child_page: ChildPageFields,
},
- Code {
+ ChildDatabase {
#[serde(flatten)]
common: BlockCommon,
- code: CodeFields,
+ child_page: ChildDatabaseFields,
},
- Quote {
+ Embed {
#[serde(flatten)]
common: BlockCommon,
- quote: TextAndChildren,
+ embed: EmbedFields,
+ },
+ Image {
+ #[serde(flatten)]
+ common: BlockCommon,
+ image: FileObject,
+ },
+ Video {
+ #[serde(flatten)]
+ common: BlockCommon,
+ video: FileObject,
+ },
+ File {
+ #[serde(flatten)]
+ common: BlockCommon,
+ file: FileObject,
+ caption: Text,
+ },
+ Pdf {
+ #[serde(flatten)]
+ common: BlockCommon,
+ pdf: FileObject,
+ },
+ Bookmark {
+ #[serde(flatten)]
+ common: BlockCommon,
+ bookmark: BookmarkFields,
},
Equation {
#[serde(flatten)]
common: BlockCommon,
equation: Equation,
},
+ Divider {
+ #[serde(flatten)]
+ common: BlockCommon,
+ },
+ TableOfContents {
+ #[serde(flatten)]
+ common: BlockCommon,
+ table_of_contents: TableOfContents,
+ },
+ Breadcrumb {
+ #[serde(flatten)]
+ common: BlockCommon,
+ },
+ ColumnList {
+ #[serde(flatten)]
+ common: BlockCommon,
+ column_list: ColumnListFields,
+ },
+ Column {
+ #[serde(flatten)]
+ common: BlockCommon,
+ column: ColumnFields,
+ },
+ LinkPreview {
+ #[serde(flatten)]
+ common: BlockCommon,
+ link_preview: LinkPreviewFields,
+ },
+ Template {
+ #[serde(flatten)]
+ common: BlockCommon,
+ template: TemplateFields,
+ },
+ LinkToPage {
+ #[serde(flatten)]
+ common: BlockCommon,
+ link_to_page: LinkToPageFields,
+ },
+ Table {
+ #[serde(flatten)]
+ common: BlockCommon,
+ table: TableFields,
+ },
+ SyncedBlock {
+ #[serde(flatten)]
+ common: BlockCommon,
+ synced_block: SyncedBlockFields,
+ },
+ TableRow {
+ #[serde(flatten)]
+ common: BlockCommon,
+ table_row: TableRowFields,
+ },
+ Unsupported {
+ #[serde(flatten)]
+ common: BlockCommon,
+ },
#[serde(other)]
- Unsupported,
+ Unknown,
}
impl AsIdentifier<BlockId> for Block {
@@ -318,16 +622,36 @@ impl AsIdentifier<BlockId> for Block {
| Heading1 { common, .. }
| Heading2 { common, .. }
| Heading3 { common, .. }
+ | Callout { common, .. }
+ | Quote { common, .. }
| BulletedListItem { common, .. }
| NumberedListItem { common, .. }
| ToDo { common, .. }
| Toggle { common, .. }
- | ChildPage { common, .. }
| Code { common, .. }
- | Quote { common, .. }
- | Equation { common, .. } => &common.id,
- Unsupported {} => {
- panic!("Trying to reference identifier for unsupported block!")
+ | ChildPage { common, .. }
+ | ChildDatabase { common, .. }
+ | Embed { common, .. }
+ | Image { common, .. }
+ | Video { common, .. }
+ | File { common, .. }
+ | Pdf { common, .. }
+ | Bookmark { common, .. }
+ | Equation { common, .. }
+ | Divider { common, .. }
+ | TableOfContents { common, .. }
+ | Breadcrumb { common, .. }
+ | ColumnList { common, .. }
+ | Column { common, .. }
+ | LinkPreview { common, .. }
+ | Template { common, .. }
+ | LinkToPage { common, .. }
+ | SyncedBlock { common, .. }
+ | Table { common, .. }
+ | TableRow { common, .. }
+ | Unsupported { common, .. } => { &common.id }
+ Unknown => {
+ panic!("Trying to reference identifier for unknown block!")
}
}
}
@@ -374,25 +698,3 @@ impl Object {
matches!(self, Object::Database { .. })
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::models::{ListResponse, Object, Page};
-
- #[test]
- fn deserialize_page() {
- let _page: Page = serde_json::from_str(include_str!("models/tests/page.json")).unwrap();
- }
-
- #[test]
- fn deserialize_query_result() {
- let _page: ListResponse<Page> =
- serde_json::from_str(include_str!("models/tests/query_result.json")).unwrap();
- }
-
- #[test]
- fn deserialize_number_format() {
- let _search_results: ListResponse<Object> =
- serde_json::from_str(include_str!("models/tests/issue_15.json")).unwrap();
- }
-}
diff --git a/src/models/properties.rs b/src/models/properties.rs
index 0a2ed97..948a0a0 100644
--- a/src/models/properties.rs
+++ b/src/models/properties.rs
@@ -206,6 +206,7 @@ pub enum DateOrDateTime {
pub struct DateValue {
pub start: DateOrDateTime,
pub end: Option<DateOrDateTime>,
+ pub time_zone: Option<String>
}
/// Formula property value objects represent the result of evaluating a formula
diff --git a/src/models/tests.rs b/src/models/tests.rs
new file mode 100644
index 0000000..fa58d54
--- /dev/null
+++ b/src/models/tests.rs
@@ -0,0 +1,449 @@
+use std::str::FromStr;
+use chrono::{DateTime, NaiveDate};
+use crate::{Block, BlockId, models};
+use crate::ids::UserId;
+use crate::models::text::{Annotations, Link, MentionObject, RichText, RichTextCommon, Text, TextColor};
+use crate::models::{BlockCommon, Callout, ExternalFileObject, InternalFileObject, FileOrEmojiObject, ListResponse, Object, Page};
+use crate::models::properties::{DateOrDateTime, DateValue};
+use crate::models::users::{Person, User, UserCommon};
+
+#[test]
+fn deserialize_page() {
+ let _page: Page = serde_json::from_str(include_str!("tests/page.json")).unwrap();
+}
+
+#[test]
+fn deserialize_query_result() {
+ let _page: ListResponse<Page> =
+ serde_json::from_str(include_str!("tests/query_result.json")).unwrap();
+}
+
+#[test]
+fn deserialize_number_format() {
+ let _search_results: ListResponse<Object> =
+ serde_json::from_str(include_str!("tests/issue_15.json")).unwrap();
+}
+
+#[test]
+fn rich_text() {
+ let rich_text_text: RichText = serde_json::from_str(include_str!("tests/rich_text_text.json")).unwrap();
+ assert_eq!(rich_text_text, RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "Rich".to_string(),
+ href: Some("https://github.com/jakeswenson/notion".to_string()),
+ annotations: Some(Annotations {
+ bold: Some(true),
+ code: Some(true),
+ color: Some(TextColor::Default),
+ italic: Some(true),
+ strikethrough: Some(true),
+ underline: Some(true),
+ }),
+ },
+ text: Text {
+ content: "Rich".to_string(),
+ link: Some(Link {
+ url: "https://github.com/jakeswenson/notion".to_string()
+ }),
+ },
+ })
+}
+
+#[test]
+fn rich_text_mention_user_person() {
+ let rich_text_mention_user_person: RichText = serde_json::from_str(include_str!("tests/rich_text_mention_user_person.json")).unwrap();
+ assert_eq!(rich_text_mention_user_person, RichText::Mention {
+ rich_text: RichTextCommon {
+ plain_text: "@John Doe".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ mention: MentionObject::User {
+ user: User::Person {
+ common: UserCommon {
+ id: UserId::from_str("1118608e-35e8-4fa3-aef7-a4ced85ce8e0").unwrap(),
+ name: Some("John Doe".to_string()),
+ avatar_url: Some("https://secure.notion-static.com/e6a352a8-8381-44d0-a1dc-9ed80e62b53d.jpg".to_string()),
+ },
+ person: Person { email: "john.doe@gmail.com".to_string() },
+ }
+ },
+ })
+}
+
+#[test]
+fn rich_text_mention_date() {
+ let rich_text_mention_date: RichText = serde_json::from_str(include_str!("tests/rich_text_mention_date.json")).unwrap();
+ assert_eq!(rich_text_mention_date, RichText::Mention {
+ rich_text: RichTextCommon {
+ plain_text: "2022-04-16 → ".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ mention: MentionObject::Date {
+ date: DateValue {
+ start: DateOrDateTime::Date(NaiveDate::from_str("2022-04-16").unwrap()),
+ end: None,
+ time_zone: None,
+ }
+ },
+ })
+}
+
+#[test]
+fn rich_text_mention_date_with_time() {
+ let rich_text_mention_date_with_time: RichText = serde_json::from_str(include_str!("tests/rich_text_mention_date_with_time.json")).unwrap();
+ assert_eq!(rich_text_mention_date_with_time, RichText::Mention {
+ rich_text: RichTextCommon {
+ plain_text: "2022-05-14T09:00:00.000-04:00 → ".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ mention: MentionObject::Date {
+ date: DateValue {
+ start: DateOrDateTime::DateTime(DateTime::from_str("2022-05-14T09:00:00.000-04:00").unwrap()),
+ end: None,
+ time_zone: None,
+ }
+ },
+ })
+}
+
+#[test]
+fn rich_text_mention_date_with_end() {
+ let rich_text_mention_date_with_end: RichText = serde_json::from_str(include_str!("tests/rich_text_mention_date_with_end.json")).unwrap();
+ assert_eq!(rich_text_mention_date_with_end, RichText::Mention {
+ rich_text: RichTextCommon {
+ plain_text: "2022-05-12 → 2022-05-13".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ mention: MentionObject::Date {
+ date: DateValue {
+ start: DateOrDateTime::Date(NaiveDate::from_str("2022-05-12").unwrap()),
+ end: Some(DateOrDateTime::Date(NaiveDate::from_str("2022-05-13").unwrap())),
+ time_zone: None,
+ }
+ },
+ })
+}
+
+#[test]
+fn rich_text_mention_date_with_end_and_time() {
+ let rich_text_mention_date_with_end_and_time: RichText = serde_json::from_str(include_str!("tests/rich_text_mention_date_with_end_and_time.json")).unwrap();
+ assert_eq!(rich_text_mention_date_with_end_and_time, RichText::Mention {
+ rich_text: RichTextCommon {
+ plain_text: "2022-04-16T12:00:00.000-04:00 → 2022-04-16T12:00:00.000-04:00".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ mention: MentionObject::Date {
+ date: DateValue {
+ start: DateOrDateTime::DateTime(DateTime::from_str("2022-04-16T12:00:00.000-04:00").unwrap()),
+ end: Some(DateOrDateTime::DateTime(DateTime::from_str("2022-04-16T12:00:00.000-04:00").unwrap())),
+ time_zone: None,
+ }
+ },
+ })
+}
+
+#[test]
+fn heading_1() {
+ let heading_1: Block = serde_json::from_str(include_str!("tests/heading_1.json")).unwrap();
+ assert_eq!(heading_1, Block::Heading1 {
+ common: BlockCommon {
+ id: BlockId::from_str("9e891834-6a03-475c-a2b8-421e17f0f3aa").unwrap(),
+ created_time: DateTime::from_str("2022-05-12T21:15:00.000Z").unwrap(),
+ last_edited_time: DateTime::from_str("2022-05-12T22:10:00.000Z").unwrap(),
+ has_children: false,
+ created_by: UserCommon {
+ id: UserId::from_str("6419f912-5293-4ea8-b2c8-9c3ce44f90e3").unwrap(),
+ name: None,
+ avatar_url: None,
+ },
+ last_edited_by: UserCommon {
+ id: UserId::from_str("6419f912-5293-4ea8-b2c8-9c3ce44f90e3").unwrap(),
+ name: None,
+ avatar_url: None,
+ },
+ },
+ heading_1: models::Text {
+ rich_text: vec![
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "This".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(true),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: "This".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: " ".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: " ".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "is".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(true),
+ }),
+ },
+ text: Text {
+ content: "is".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: " ".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: " ".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "a".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(true),
+ strikethrough: Some(false),
+ underline: Some(true),
+ }),
+ },
+ text: Text {
+ content: "a".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: " ".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: " ".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "Heading".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(true),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: "Heading".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: " ".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: " ".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "1".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(true),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: "1".to_string(),
+ link: None,
+ },
+ },
+ ]
+ },
+ })
+}
+
+#[test]
+fn emoji_object() {
+ let emoji_object: FileOrEmojiObject = serde_json::from_str(include_str!("tests/emoji_object.json")).unwrap();
+ assert_eq!(emoji_object, FileOrEmojiObject::Emoji {
+ emoji: "💡".to_string()
+ })
+}
+
+#[test]
+fn file_object() {
+ let file_object: FileOrEmojiObject = serde_json::from_str(include_str!("tests/file_object.json")).unwrap();
+ assert_eq!(file_object, FileOrEmojiObject::File {
+ file: InternalFileObject {
+ url: "https://s3.us-west-2.amazonaws.com/secure.notion-static.com/2703e742-ace5-428c-a74d-1c587ceddc32/DiRT_Rally.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20220513%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20220513T201035Z&X-Amz-Expires=3600&X-Amz-Signature=714b49bde0b499fb8f3aae1a88a8cbd374f2b09c1d128e91cac49e85ce0e00fb&X-Amz-SignedHeaders=host&x-id=GetObject".to_string(),
+ expiry_time: DateTime::from_str("2022-05-13T21:10:35.817Z").unwrap(),
+ }
+ })
+}
+
+#[test]
+fn external_file_object() {
+ let external_file_object: FileOrEmojiObject = serde_json::from_str(include_str!("tests/external_file_object.json")).unwrap();
+ assert_eq!(external_file_object, FileOrEmojiObject::External {
+ external: ExternalFileObject {
+ url: "https://nerdist.com/wp-content/uploads/2020/07/maxresdefault.jpg".to_string(),
+ }
+ })
+}
+
+#[test]
+fn callout() {
+ let callout: Object = serde_json::from_str(include_str!("tests/callout.json")).unwrap();
+ assert_eq!(callout, Object::Block {
+ block: Block::Callout {
+ common: BlockCommon {
+ id: BlockId::from_str("00e8829a-a7b8-4075-884a-8f53be145d2f").unwrap(),
+ created_time: DateTime::from_str("2022-05-13T20:08:00.000Z").unwrap(),
+ last_edited_time: DateTime::from_str("2022-05-13T20:08:00.000Z").unwrap(),
+ has_children: true,
+ created_by: UserCommon {
+ id: UserId::from_str("e2507360-468c-4e0f-a928-7bbcbbb45353").unwrap(),
+ name: None,
+ avatar_url: None,
+ },
+ last_edited_by: UserCommon {
+ id: UserId::from_str("e2507360-468c-4e0f-a928-7bbcbbb45353").unwrap(),
+ name: None,
+ avatar_url: None,
+ },
+ },
+ callout: Callout {
+ rich_text: vec![
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "Test callout".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text { content: "Test callout".to_string(), link: None },
+ }
+ ],
+ icon: FileOrEmojiObject::Emoji {
+ emoji: "💡".to_string()
+ },
+ color: TextColor::Green,
+ },
+ }
+ })
+} \ No newline at end of file
diff --git a/src/models/tests/callout.json b/src/models/tests/callout.json
new file mode 100644
index 0000000..e4d884d
--- /dev/null
+++ b/src/models/tests/callout.json
@@ -0,0 +1,43 @@
+{
+ "object": "block",
+ "id": "00e8829a-a7b8-4075-884a-8f53be145d2f",
+ "created_time": "2022-05-13T20:08:00.000Z",
+ "last_edited_time": "2022-05-13T20:08:00.000Z",
+ "created_by": {
+ "object": "user",
+ "id": "e2507360-468c-4e0f-a928-7bbcbbb45353"
+ },
+ "last_edited_by": {
+ "object": "user",
+ "id": "e2507360-468c-4e0f-a928-7bbcbbb45353"
+ },
+ "has_children": true,
+ "archived": false,
+ "type": "callout",
+ "callout": {
+ "rich_text": [
+ {
+ "type": "text",
+ "text": {
+ "content": "Test callout",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "Test callout",
+ "href": null
+ }
+ ],
+ "icon": {
+ "type": "emoji",
+ "emoji": "💡"
+ },
+ "color": "green"
+ }
+} \ No newline at end of file
diff --git a/src/models/tests/emoji_object.json b/src/models/tests/emoji_object.json
new file mode 100644
index 0000000..1fb3b56
--- /dev/null
+++ b/src/models/tests/emoji_object.json
@@ -0,0 +1,4 @@
+{
+ "type": "emoji",
+ "emoji": "💡"
+} \ No newline at end of file
diff --git a/src/models/tests/external_file_object.json b/src/models/tests/external_file_object.json
new file mode 100644
index 0000000..b5d4b85
--- /dev/null
+++ b/src/models/tests/external_file_object.json
@@ -0,0 +1,6 @@
+{
+ "type": "external",
+ "external": {
+ "url": "https://nerdist.com/wp-content/uploads/2020/07/maxresdefault.jpg"
+ }
+} \ No newline at end of file
diff --git a/src/models/tests/file_object.json b/src/models/tests/file_object.json
new file mode 100644
index 0000000..650cf9b
--- /dev/null
+++ b/src/models/tests/file_object.json
@@ -0,0 +1,7 @@
+{
+ "type": "file",
+ "file": {
+ "url": "https://s3.us-west-2.amazonaws.com/secure.notion-static.com/2703e742-ace5-428c-a74d-1c587ceddc32/DiRT_Rally.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20220513%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20220513T201035Z&X-Amz-Expires=3600&X-Amz-Signature=714b49bde0b499fb8f3aae1a88a8cbd374f2b09c1d128e91cac49e85ce0e00fb&X-Amz-SignedHeaders=host&x-id=GetObject",
+ "expiry_time": "2022-05-13T21:10:35.817Z"
+ }
+} \ No newline at end of file
diff --git a/src/models/tests/heading_1.json b/src/models/tests/heading_1.json
new file mode 100644
index 0000000..ab2d7e1
--- /dev/null
+++ b/src/models/tests/heading_1.json
@@ -0,0 +1,175 @@
+{
+ "object": "block",
+ "id": "9e891834-6a03-475c-a2b8-421e17f0f3aa",
+ "created_time": "2022-05-12T21:15:00.000Z",
+ "last_edited_time": "2022-05-12T22:10:00.000Z",
+ "created_by": {
+ "object": "user",
+ "id": "6419f912-5293-4ea8-b2c8-9c3ce44f90e3"
+ },
+ "last_edited_by": {
+ "object": "user",
+ "id": "6419f912-5293-4ea8-b2c8-9c3ce44f90e3"
+ },
+ "has_children": false,
+ "archived": false,
+ "type": "heading_1",
+ "heading_1": {
+ "rich_text": [
+ {
+ "type": "text",
+ "text": {
+ "content": "This",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": true,
+ "color": "default"
+ },
+ "plain_text": "This",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": " ",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": " ",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": "is",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": true,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "is",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": " ",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": " ",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": "a",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": true,
+ "strikethrough": false,
+ "underline": true,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "a",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": " ",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": " ",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": "Heading",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": true,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "Heading",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": " ",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": " ",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": "1",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": true,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "1",
+ "href": null
+ }
+ ],
+ "color": "default"
+ }
+} \ No newline at end of file
diff --git a/src/models/tests/rich_text_mention_date.json b/src/models/tests/rich_text_mention_date.json
new file mode 100644
index 0000000..687f6dc
--- /dev/null
+++ b/src/models/tests/rich_text_mention_date.json
@@ -0,0 +1,21 @@
+{
+ "type": "mention",
+ "mention": {
+ "type": "date",
+ "date": {
+ "start": "2022-04-16",
+ "end": null,
+ "time_zone": null
+ }
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "2022-04-16 → ",
+ "href": null
+} \ No newline at end of file
diff --git a/src/models/tests/rich_text_mention_date_with_end.json b/src/models/tests/rich_text_mention_date_with_end.json
new file mode 100644
index 0000000..b4953a0
--- /dev/null
+++ b/src/models/tests/rich_text_mention_date_with_end.json
@@ -0,0 +1,21 @@
+{
+ "type": "mention",
+ "mention": {
+ "type": "date",
+ "date": {
+ "start": "2022-05-12",
+ "end": "2022-05-13",
+ "time_zone": null
+ }
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "2022-05-12 → 2022-05-13",
+ "href": null
+} \ No newline at end of file
diff --git a/src/models/tests/rich_text_mention_date_with_end_and_time.json b/src/models/tests/rich_text_mention_date_with_end_and_time.json
new file mode 100644
index 0000000..2070207
--- /dev/null
+++ b/src/models/tests/rich_text_mention_date_with_end_and_time.json
@@ -0,0 +1,21 @@
+{
+ "type": "mention",
+ "mention": {
+ "type": "date",
+ "date": {
+ "start": "2022-04-16T12:00:00.000-04:00",
+ "end": "2022-04-16T12:00:00.000-04:00",
+ "time_zone": null
+ }
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "2022-04-16T12:00:00.000-04:00 → 2022-04-16T12:00:00.000-04:00",
+ "href": null
+} \ No newline at end of file
diff --git a/src/models/tests/rich_text_mention_date_with_time.json b/src/models/tests/rich_text_mention_date_with_time.json
new file mode 100644
index 0000000..127d9bd
--- /dev/null
+++ b/src/models/tests/rich_text_mention_date_with_time.json
@@ -0,0 +1,21 @@
+{
+ "type": "mention",
+ "mention": {
+ "type": "date",
+ "date": {
+ "start": "2022-05-14T09:00:00.000-04:00",
+ "end": null,
+ "time_zone": null
+ }
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "2022-05-14T09:00:00.000-04:00 → ",
+ "href": null
+} \ No newline at end of file
diff --git a/src/models/tests/rich_text_mention_user_person.json b/src/models/tests/rich_text_mention_user_person.json
new file mode 100644
index 0000000..8851266
--- /dev/null
+++ b/src/models/tests/rich_text_mention_user_person.json
@@ -0,0 +1,26 @@
+{
+ "type": "mention",
+ "mention": {
+ "type": "user",
+ "user": {
+ "object": "user",
+ "id": "1118608e-35e8-4fa3-aef7-a4ced85ce8e0",
+ "name": "John Doe",
+ "avatar_url": "https://secure.notion-static.com/e6a352a8-8381-44d0-a1dc-9ed80e62b53d.jpg",
+ "type": "person",
+ "person": {
+ "email": "john.doe@gmail.com"
+ }
+ }
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "@John Doe",
+ "href": null
+} \ No newline at end of file
diff --git a/src/models/tests/rich_text_text.json b/src/models/tests/rich_text_text.json
new file mode 100644
index 0000000..0089b47
--- /dev/null
+++ b/src/models/tests/rich_text_text.json
@@ -0,0 +1,19 @@
+{
+ "type": "text",
+ "text": {
+ "content": "Rich",
+ "link": {
+ "url": "https://github.com/jakeswenson/notion"
+ }
+ },
+ "annotations": {
+ "bold": true,
+ "italic": true,
+ "strikethrough": true,
+ "underline": true,
+ "code": true,
+ "color": "default"
+ },
+ "plain_text": "Rich",
+ "href": "https://github.com/jakeswenson/notion"
+} \ No newline at end of file
diff --git a/src/models/text.rs b/src/models/text.rs
index 16274ab..4169374 100644
--- a/src/models/text.rs
+++ b/src/models/text.rs
@@ -1,4 +1,7 @@
use serde::{Deserialize, Serialize};
+use crate::models::users::User;
+use crate::{Database, Page};
+use crate::models::properties::DateValue;
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Copy, Clone)]
#[serde(rename_all = "snake_case")]
@@ -56,6 +59,33 @@ pub struct Text {
pub link: Option<Link>,
}
+/// See https://developers.notion.com/reference/rich-text#mention-objects
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[serde(tag = "type")]
+#[serde(rename_all = "snake_case")]
+pub enum MentionObject {
+ User {
+ user: User
+ },
+ // TODO: need to add tests
+ Page {
+ page: Page
+ },
+ // TODO: need to add tests
+ Database {
+ database: Database
+ },
+ Date {
+ date: DateValue
+ },
+ // TODO: need to add LinkPreview
+ // LinkPreview {
+ //
+ // },
+ #[serde(other)]
+ Unknown
+}
+
/// Rich text objects contain data for displaying formatted text, mentions, and equations.
/// A rich text object also contains annotations for style information.
/// Arrays of rich text objects are used within property objects and property
@@ -74,6 +104,7 @@ pub enum RichText {
Mention {
#[serde(flatten)]
rich_text: RichTextCommon,
+ mention: MentionObject
},
/// See <https://developers.notion.com/reference/rich-text#equation-objects>
Equation {