notion_into_sqlite/
notion_client.rs1use anyhow::{anyhow, Result};
2use serde_json::{json, Value};
3
4use crate::json_util::{dig_json, JsonKey};
5use crate::notion_database::{parse_database_schema, NotionDatabaseSchema};
6use crate::notion_pages::{parse_notion_page_list, NotionPage};
7
8pub struct NotionClient {
9 pub api_key: String,
10}
11
12impl NotionClient {
13 pub fn get_database(&self, database_id: &str) -> Result<NotionDatabaseSchema> {
14 let url = format!("https://api.notion.com/v1/databases/{0}", database_id);
15 let client = reqwest::blocking::Client::new();
16 info!("Requesting database schema. URL: {}", &url);
17 let resp = client
18 .get(url)
19 .header("Authorization", "Bearer ".to_string() + &self.api_key)
20 .header("Notion-Version", "2022-02-22")
21 .send()?
22 .json::<Value>()?;
23 info!("Request done.");
24
25 self.validate_response(&resp)?;
26
27 let schema = parse_database_schema(&resp)?;
28 info!("Database schema: {:?}", schema);
29 Ok(schema)
30 }
31
32 pub fn get_all_pages(
33 &self,
34 database_id: &str,
35 schema: &NotionDatabaseSchema,
36 ) -> Result<Vec<NotionPage>> {
37 let url = format!("https://api.notion.com/v1/databases/{0}/query", database_id);
38 let client = reqwest::blocking::Client::new();
39
40 let mut next_cursor: Option<String> = None;
41 let mut all_pages: Vec<NotionPage> = vec![];
42 loop {
43 let mut query = json!({
44 "page_size": 10i32,
45 "sorts": [{
46 "timestamp": "created_time",
47 "direction": "ascending",
48 }]
49 });
50 if let Some(cursor) = (&next_cursor).as_ref() {
51 query
52 .as_object_mut()
53 .unwrap()
54 .insert("start_cursor".into(), cursor.clone().into());
55 }
56 let query_str = query.to_string();
57
58 info!("Requesting query: URL: {}, query: {}", &url, &query_str);
59 let resp = client
60 .post(&url)
61 .header("Authorization", "Bearer ".to_string() + &self.api_key)
62 .header("Notion-Version", "2022-02-22")
63 .header("Content-Type", "application/json")
64 .body(query_str)
65 .send()?
66 .json::<Value>()?;
67 info!("Request done.");
68
69 self.validate_response(&resp)?;
70
71 let (mut pages, _next_cursor) = parse_notion_page_list(schema, &resp)?;
72 info!("Pages: {:?}", pages.len());
73 all_pages.append(&mut pages);
74 next_cursor = _next_cursor;
75
76 if next_cursor.is_none() {
77 info!("Fetched all items.");
78 break;
79 } else {
80 info!("Has more items.");
81 }
82 }
83
84 Ok(all_pages)
85 }
86
87 fn validate_response(&self, resp: &Value) -> Result<()> {
88 let json_keys = vec![JsonKey::String("object")];
89 let object_field = dig_json(resp, &json_keys)
90 .and_then(|o| o.as_str())
91 .ok_or_else(|| anyhow!("Unexpected response from Notion API: {}", resp))?;
92
93 if object_field == "error" {
94 Err(anyhow!("Error response from Notion API: {}", resp,))
95 } else {
96 Ok(())
97 }
98 }
99}