From 0998be407a8e88a8ddfef9350ddcbbcdaecc34e0 Mon Sep 17 00:00:00 2001 From: Radek Molenda Date: Wed, 2 Aug 2023 12:43:42 +0200 Subject: [PATCH 1/4] Start api client --- org-notion.el | 163 +++++++----------- test.org | 1 + .../valid-block-children-response.txt | 12 ++ test/fixtures/valid-page-response.txt | 78 +++++++++ test/org-notion-test.el | 62 +++---- 5 files changed, 179 insertions(+), 137 deletions(-) create mode 100644 test.org create mode 100644 test/fixtures/valid-block-children-response.txt create mode 100644 test/fixtures/valid-page-response.txt diff --git a/org-notion.el b/org-notion.el index a45a560..bd4ec9a 100644 --- a/org-notion.el +++ b/org-notion.el @@ -2,9 +2,9 @@ ;; Author: qrczeno ;; Created: 01-Jun-2019 -;; Version: 0.1 +;; Version: 1.0 ;; Keywords: org, org-mode, notion -;; Package-Requires: ((s "1.9")) +;; Package-Requires: ((s "1.9") (dash "2.19.1")) ;;; Commentary: @@ -12,36 +12,73 @@ ;; Define the following properties your org note like that ;; * Notion page I would like to keep in sync ;; :PROPERTIES: -;; :NOTION_ID: 0dc73850-a48c-4096-ab71-3eeeda48522e -;; :NOTION_TOKEN_V2_FILE_PATH: ~/token_v2.gpg +;; :NOTION_PAGE_ID: 0dc73850-a48c-4096-ab71-3eeeda48522e ;; :END: ;; ;; while cursor being on note run -;; `org-node-push' to push the changes -;; `org-node-fetch' to fetch the changes +;; `org-notion-import' to push the changes +;; `org-notion-fetch' to fetch the changes ;; TODO: use (org-map-tree (lambda () (message (format "%s" (org-get-heading t t t t))))) ;; to publish tree as page ;; TODO: links and todo lists in notion is a must ;;; Code: -(defvar org-notion--get-record-values "https://www.notion.so/api/v3/getRecordValues") -(defvar org-notion--sumbit-transaction-url "https://www.notion.so/api/v3/submitTransaction") -(defvar org-notion--notion-id "NOTION_ID") +(defvar org-notion--notion-api-url "https://api.notion.com") +(defvar org-notion--notion-page-id-property-name "NOTION_PAGE_ID") +(defvar org-notion--notion-password-machine "notion.so") (require 's) (require 'dash) (require 'json) (require 'ox) (require 'cl-lib) +(require 'auth-source) + +(defun org-notion--get-password (host) + "Get the password for the specified HOST from auth-sources." + (let ((entry (auth-source-search :host host :max 1))) + (when entry + (let ((secret (plist-get (car entry) :secret))) + (if (functionp secret) + (funcall secret) + secret))))) + +(defun org-notion--make-request (method url &optional payload) + "Make the request to URL given METHOD and return a buffer with a response." + (let* ((url-request-method method) + (token (org-notion--get-password org-notion--notion-password-machine)) + (url-request-extra-headers `(("Content-Type" . "application/json") + ("Notion-Version" . "2022-06-28") + ("Authorization" . ,(s-concat "Bearer " token)))) + (url-request-data payload)) + (progn + (message (format "Payload %s" payload)) + (url-retrieve-synchronously url)))) + + +(defun org-notion--fetch-page (page-id) + "Given PAGE-ID fetch the page and return a buffer response." + (org-notion--make-request "GET" (s-concat "https://api.notion.com/v1/pages/" page-id))) + -(defun export-as-notion-block (callback) - "export as notion to buffer" +(defun org-notion--get-title (data) + "Extract the 'title' from the json DATA." + (let* ((properties (cdr (assoc 'properties data))) + (name (cdr (assoc 'Name properties))) + (title-info (cdr (assoc 'title name))) + (title (elt title-info 0)) + (title-text (cdr (assoc 'plain_text title)))) + title-text)) + + +(defun org-notion--export-as-notion-block (callback) + "Export as notion to buffer and call CALLBACK." (interactive) (org-export-to-buffer 'notion "*notion export*" nil nil nil nil nil callback)) - +;; TODO: refactor to use notion api (defun org-notion--format-plain-text (c _i) "Default notion format H C I." (format "[\"%s\"]" c)) @@ -53,7 +90,7 @@ (defun org-notion-link (link contents _i) "Format LINK, insert CONTENTS." (let ((url (org-element-property :raw-link link)) - (text (get-text link))) + (text (org-notion--get-text link))) (format "[\"%s \",[[\"a\",\"%s\"]]]" text url))) (defun org-notion--format-template (contents _i) @@ -65,14 +102,14 @@ (format "[%s]" (org-export-data (org-element-property :title h) i))) (defun org-notion--format-bold (&optional h c i) - "Format headline H I." - (format "[\"%s\",[[\"b\"]]]" (get-text h))) + "Format bold H I." + (format "[\"%s\",[[\"b\"]]]" (org-notion--get-text h))) (defun org-notion--format-italic (&optional h c i) - "Format headline H I." - (format "[\"%s\",[[\"i\"]]]" (get-text h) )) + "Format italic H I." + (format "[\"%s\",[[\"i\"]]]" (org-notion--get-text h) )) -(defun get-text (element) +(defun org-notion--get-text (element) (let ((start (org-element-property :contents-begin element)) (end (org-element-property :contents-end element))) (buffer-substring start end))) @@ -129,56 +166,7 @@ (underline . notion-format) (verbatim . notion-format) (verse-block . notion-format) - )) - - -(defun get-string-from-file (file-path) - "Return FILE_PATH file content." - (with-temp-buffer (insert-file-contents file-path) - (delete-blank-lines) - (buffer-string))) - - - -(defun org-notion--token-v2 () - "Get the token." - (save-excursion (let ((token-path (or (cdr (assoc "NOTION_TOKEN_V2_FILE_PATH" - (org-entry-properties))) - "~/token_v2.gpg"))) - (s-trim (format "token_v2=%s" (get-string-from-file token-path)))))) - -(defun org-notion--uuid (uuid-string) - "Tries to parse UUID-STRING and returns uuid or nil if failed." - (s-join "-" (cdr (s-match "^\\([[:alnum:]]\\{8\\}\\)-?\\([[:alnum:]]\\{4\\}\\)-?\\([[:alnum:]]\\{4\\}\\)-?\\([[:alnum:]]\\{4\\}\\)-?\\([[:alnum:]]\\{12\\}\\)$" uuid-string)))) - -(defun org-notion--request (url payload cb) - "Send PAYLOAD post json request to URL and call a CB in response buffer." - (let* ((url-request-method "POST") - (token-v2 (org-notion--token-v2)) - (url-request-extra-headers `(("Content-Type" . "application/json") - ("Cookie" . ,(org-notion--token-v2)))) - (url-request-data payload)) - (progn - (message (format "Payload %s" payload)) - (url-retrieve url cb)))) - -(defun fetch-block (notion-id callback) - "Given the NOTION-ID fetch the corresponding blocks." - (org-notion--request org-notion--get-record-values (json-encode `(("requests" . ((("id" . ,notion-id) ("table" . "block")))))) callback)) - -(defun push-title (notion-id title callback) - "Publish the item under point." - (message (format "notion-id %s title %s" notion-id title)) - (org-notion--request - org-notion--sumbit-transaction-url - (json-encode `(("operations" . - ((("args" . [[,title]]) - ("command" . "set") - ("path" . ("properties" "title")) - ("table" . "block") - ("id" . ,notion-id)))))) - callback)) - +)) ;;;###autoload (defun org-notion-send-block () @@ -189,37 +177,18 @@ (callback (lambda (_status) (message "OK")))) (push-title notion-id title callback))) -;;;###autoload -(defun org-notion-import-block (notion-id) - "Import notion block as org item. NOTION-ID is notion uuid of imported block." - (interactive "sNotion id: ") - (let ((notion-id (org-notion--uuid notion-id))) - (progn (message (format "Importing %s" notion-id)) - (org-insert-heading-after-current) - (org-set-property org-notion--notion-id notion-id) - (setq org-notion--current-org-notion-buffer (current-buffer)) - (fetch-block notion-id (lambda (_status) - (with-current-buffer (current-buffer) - (search-forward "\n\n") - (let* ((data (json-read)) - (first-result (elt (alist-get 'results data) 0)) - (value (alist-get 'value first-result)) - (properties (alist-get 'properties value)) - (title (elt (elt (alist-get 'title properties) 0) - 0))) - (message (format "title: %s" title)) - (switch-to-buffer org-notion--current-org-notion-buffer) - (insert (format " %s" title))))))))) +;; TODO: this is where refactoring ends -;;;###autoload -(defun org-notion-open () - "Fetch the notion block and update the note accordingly." - (interactive) - (let ((notion-id (cdr (assoc org-notion--notion-id (org-entry-properties)))) - (notion-namespace (cdr (assoc "NOTION_NAMESPACE" (org-entry-properties))))) - (if notion-id (browse-url (format "https://www.notion.so/%s/%s" notion-namespace notion-id)) - (message "please define NOTION_ID as property")))) +; Use the following id: de466bb4-4bb2-4610-9e91-f656e2a3a6dc +(defun org-notion-import-page (page-id) + "Import notion block as org item. PAGE-ID is notion uuid of imported block." + (let* ((response-buffer (org-notion--fetch-page page-id)) + (data-json (with-current-buffer response-buffer + (search-forward "\n\n" nil 'move) + (json-read))) + (title (org-notion--get-title data-json))) + title)) (provide 'org-notion) diff --git a/test.org b/test.org new file mode 100644 index 0000000..a955185 --- /dev/null +++ b/test.org @@ -0,0 +1 @@ +* Hello diff --git a/test/fixtures/valid-block-children-response.txt b/test/fixtures/valid-block-children-response.txt new file mode 100644 index 0000000..78260f4 --- /dev/null +++ b/test/fixtures/valid-block-children-response.txt @@ -0,0 +1,12 @@ +HTTP/2 200 +date: Tue, 01 Aug 2023 07:37:54 GMT +content-type: application/json; charset=utf-8 +x-powered-by: Express +etag: W/"2a78-tfBUxjWqFWU0PSu5lWcNWwWJUE0" +vary: Accept-Encoding +cf-cache-status: DYNAMIC +set-cookie: __cf_bm=JEM779Bf8h2YPCgLehzS4xsRpujWqU5H8sQcJQHgLY4-1690875474-0-AXSNJvx65rqCCyDngVbm4tgKYw99C2CqiYLTjny9FNCCjKPru6wLuYYHdxM9fxa2EaYWePip42ywmH4tNPIwnNA=; path=/; expires=Tue, 01-Aug-23 08:07:54 GMT; domain=.notion.com; HttpOnly; Secure; SameSite=None +server: cloudflare +cf-ray: 7efc6b2109d1bfd7-WAW + +{"object":"list","results":[{"object":"block","id":"d5f8d832-cd88-4836-8c66-d92b17a45cc8","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:52:00.000Z","last_edited_time":"2023-07-31T22:52:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"heading_1","heading_1":{"rich_text":[{"type":"text","text":{"content":"heading 1","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"heading 1","href":null}],"is_toggleable":false,"color":"default"}},{"object":"block","id":"b02f86d6-e08f-41a6-86d5-df262ad0cdd9","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:53:00.000Z","last_edited_time":"2023-07-31T22:53:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"heading_2","heading_2":{"rich_text":[{"type":"text","text":{"content":"heading 2","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"heading 2","href":null}],"is_toggleable":false,"color":"default"}},{"object":"block","id":"a397c845-3405-4a85-89fb-5f4c465173fe","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:53:00.000Z","last_edited_time":"2023-07-31T22:53:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"heading_3","heading_3":{"rich_text":[{"type":"text","text":{"content":"Heading 3","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"Heading 3","href":null}],"is_toggleable":false,"color":"default"}},{"object":"block","id":"df0f9160-be29-44c7-9516-42fa9a717fdb","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:53:00.000Z","last_edited_time":"2023-07-31T22:55:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"to_do","to_do":{"rich_text":[{"type":"text","text":{"content":"TODO One","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"TODO One","href":null}],"checked":false,"color":"default"}},{"object":"block","id":"124e7032-fa35-40c2-bd6d-d468ed07fb40","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:53:00.000Z","last_edited_time":"2023-07-31T22:54:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":true,"archived":false,"type":"to_do","to_do":{"rich_text":[{"type":"text","text":{"content":"TODO two","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"TODO two","href":null}],"checked":false,"color":"default"}},{"object":"block","id":"7dcd55be-dd36-41af-b84b-cc3a99e372d2","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:53:00.000Z","last_edited_time":"2023-07-31T22:54:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"to_do","to_do":{"rich_text":[{"type":"text","text":{"content":"TODO three","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"TODO three","href":null}],"checked":false,"color":"default"}},{"object":"block","id":"ede67d4a-685a-45f2-a869-eef29f2106ac","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:53:00.000Z","last_edited_time":"2023-07-31T22:53:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"paragraph","paragraph":{"rich_text":[],"color":"default"}},{"object":"block","id":"7db99a78-efc0-47e8-811a-7d855286244a","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:54:00.000Z","last_edited_time":"2023-07-31T22:54:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"bulleted_list_item","bulleted_list_item":{"rich_text":[{"type":"text","text":{"content":"bullet one","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"bullet one","href":null}],"color":"default"}},{"object":"block","id":"5bee2d84-038b-4787-97bc-c8dea81eb92f","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:54:00.000Z","last_edited_time":"2023-07-31T22:54:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"bulleted_list_item","bulleted_list_item":{"rich_text":[{"type":"text","text":{"content":"bullet two","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"bullet two","href":null}],"color":"default"}},{"object":"block","id":"0ce4fe5e-a8be-4a19-9b03-b588943b830c","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:54:00.000Z","last_edited_time":"2023-07-31T22:54:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"paragraph","paragraph":{"rich_text":[],"color":"default"}},{"object":"block","id":"51d0d32b-c7c0-468d-a983-7421019aaaac","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:54:00.000Z","last_edited_time":"2023-07-31T22:54:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"numbered_list_item","numbered_list_item":{"rich_text":[{"type":"text","text":{"content":"number one","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"number one","href":null}],"color":"default"}},{"object":"block","id":"20d675c7-6a41-4f36-9ddc-3800c34f6be9","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:54:00.000Z","last_edited_time":"2023-07-31T22:55:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":true,"archived":false,"type":"numbered_list_item","numbered_list_item":{"rich_text":[{"type":"text","text":{"content":"number two","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"number two","href":null}],"color":"default"}},{"object":"block","id":"6a506d6f-9d99-4b91-930a-d25b05a566a4","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:55:00.000Z","last_edited_time":"2023-07-31T22:55:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"numbered_list_item","numbered_list_item":{"rich_text":[{"type":"text","text":{"content":"number three","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"number three","href":null}],"color":"default"}},{"object":"block","id":"7df122e8-4195-40a4-8938-fe06f58d0f76","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:55:00.000Z","last_edited_time":"2023-07-31T22:55:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"paragraph","paragraph":{"rich_text":[],"color":"default"}},{"object":"block","id":"b2d5e08e-3664-403a-9dff-8c54faf3449d","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T20:12:00.000Z","last_edited_time":"2023-07-31T22:56:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"paragraph","paragraph":{"rich_text":[{"type":"text","text":{"content":"Some content and checkout ","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"Some content and checkout ","href":null},{"type":"text","text":{"content":"this page","link":{"url":"https://www.google.com/"}},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"this page","href":"https://www.google.com/"}],"color":"default"}},{"object":"block","id":"f7d61432-e124-4735-b00d-fa176877bdd1","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T20:13:00.000Z","last_edited_time":"2023-07-31T20:13:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"paragraph","paragraph":{"rich_text":[],"color":"default"}}],"next_cursor":null,"has_more":false,"type":"block","block":{}} \ No newline at end of file diff --git a/test/fixtures/valid-page-response.txt b/test/fixtures/valid-page-response.txt new file mode 100644 index 0000000..6252e77 --- /dev/null +++ b/test/fixtures/valid-page-response.txt @@ -0,0 +1,78 @@ +HTTP/2 200 +date: Tue, 01 Aug 2023 07:37:07 GMT +content-type: application/json; charset=utf-8 +x-powered-by: Express +etag: W/"41a-C4IquhvmvyaFm91BgpPwtVairf8" +vary: Accept-Encoding +cf-cache-status: DYNAMIC +set-cookie: __cf_bm=0xoPDtxPNhEUvY7.fxOT54hs.nGaWX3b6UN.puNspOk-1690875426-0-AQ6rc0L382iA/jtbBdiK5PXJQGEKAKWges8eVWDBS3ii8VERz/EzQ5LyarM1ij9bjZo5xiuc0aAVTcewiMrHIG0=; path=/; expires=Tue, 01-Aug-23 08:07:06 GMT; domain=.notion.com; HttpOnly; Secure; SameSite=None +server: cloudflare +cf-ray: 7efc69f9581cbfd5-WAW + +{ + "object": "page", + "id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc", + "created_time": "2023-07-31T20:12:00.000Z", + "last_edited_time": "2023-07-31T22:56:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "cover": null, + "icon": null, + "parent": { + "type": "database_id", + "database_id": "56f25737-10d0-439b-af69-c055bb9bec5c" + }, + "archived": false, + "properties": { + "Created": { + "id": "L%7DH%5B", + "type": "created_time", + "created_time": "2023-07-31T20:12:00.000Z" + }, + "Date": { + "id": "PQ%5DM", + "type": "date", + "date": { + "start": "2023-07-20", + "end": null, + "time_zone": null + } + }, + "Tags": { + "id": "%5ECz_", + "type": "multi_select", + "multi_select": [] + }, + "Name": { + "id": "title", + "type": "title", + "title": [ + { + "type": "text", + "text": { + "content": "yes yes yes", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "yes yes yes", + "href": null + } + ] + } + }, + "url": "https://www.notion.so/yes-yes-yes-de466bb44bb246109e91f656e2a3a6dc", + "public_url": null +} diff --git a/test/org-notion-test.el b/test/org-notion-test.el index 6296dfb..c239353 100644 --- a/test/org-notion-test.el +++ b/test/org-notion-test.el @@ -1,8 +1,8 @@ ;;; org-notion-test.el --- Test for org-notion.el -;; Copyright (C) 2016 by Radek MOLENDA +;; Copyright (C) 2023 by Radek Molenda -;; Author: Radek MOLENDA +;; Author: Radek Molenda ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -22,47 +22,29 @@ ;;; Code: (require 'ert) +(require 'cl-macs) + (require 'org-notion) -(ert-deftest parse-uuid () - "'parse-uuid' test" - (let ((id (org-notion--uuid "d8a60223-5a72-44c4-b075-145ecb173e05"))) - (should (s-equals? id "d8a60223-5a72-44c4-b075-145ecb173e05"))) - (let ((id (org-notion--uuid "d8a602235a7244c4b075145ecb173e05"))) - (should (s-equals? id "d8a60223-5a72-44c4-b075-145ecb173e05"))) - (let ((id (org-notion--uuid "some-garbage"))) - (should (s-equals? id "")))) - -(ert-deftest parsing-item () - "Parses the heading item correctly" - (with-temp-buffer - (org-mode) - (insert "* yes [[http://www.example.com][hello]] link *bold* /italics/") - (goto-char (point-min)) - (push-mark (point-max)) - (setq mark-active t) - (export-as-notion-block (lambda () - (let ((expected-string "[[\"yes \"],[\"hello \",[[\"a\",\"http://www.example.com\"]]],[\"link \"],[\"bold\",[[\"b\"]]],[\"italics\",[[\"i\"]]]]\n")) -(should (s-equals? (buffer-string) expected-string)) - ))) - ) - ) - -;; (ert-deftest parsing-headline-with-additional-text () -;; "Parses the headline item correctly" -;; (with-temp-buffer -;; (org-mode) -;; (insert "* some headline\n some text") -;; (goto-char (point-min)) -;; (push-mark (point-max)) -;; (setq mark-active t) -;; (export-as-notion-block (lambda () -;; (let ((expected-string "[[\"some headline\"]]\n[[\"some text\"]]\n")) -;; (should (s-equals? (buffer-string) expected-string)) -;; ))) -;; ) -;; ) +(ert-deftest fetching-page-calls-the-right-api () + "Fetching page calls make request" + (cl-letf (((symbol-function 'org-notion--make-request) + (lambda (method url &optional payload) + (should (equal method "GET")) + (should (equal url "https://api.notion.com/v1/pages/xxxx-xxxx-xxxx-xxxxxxxx"))))) + (org-notion--fetch-page "xxxx-xxxx-xxxx-xxxxxxxx"))) + +(setq stub-org-notion--fetch-page (lambda (page-id) + (let ((buffer (find-file-noselect "test/fixtures/valid-page-response.txt"))) + (with-current-buffer buffer (setq buffer-read-only t) buffer)))) + +(ert-deftest importing-page () + "Importing page builds buffer with correct org data in it" + (cl-letf (((symbol-function 'org-notion--fetch-page) + stub-org-notion--fetch-page)) + (should (s-equals? (org-notion-import-page "whatever") "yes yes yes")))) + ;;; test.el ends here From 31053687feaec5103ca2554c4a784a9693a92e51 Mon Sep 17 00:00:00 2001 From: Radek Molenda Date: Wed, 2 Aug 2023 12:44:15 +0200 Subject: [PATCH 2/4] Errands --- test.org => test/test.org | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test.org => test/test.org (100%) diff --git a/test.org b/test/test.org similarity index 100% rename from test.org rename to test/test.org From 4bc7c415b4a8aad6286738602ebbc7f626241e0a Mon Sep 17 00:00:00 2001 From: Radek Molenda Date: Wed, 2 Aug 2023 13:12:25 +0200 Subject: [PATCH 3/4] Better UI for readme --- README.org | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/README.org b/README.org index 4f95f61..be81dda 100644 --- a/README.org +++ b/README.org @@ -1,5 +1,45 @@ -* Org Notion - keeps the org and notion in sync +* Org Notion - Keep Org and Notion in Sync + +Org Notion is a package that allows you to keep your Org files and Notion in sync. Please note that this package is currently in the experimental stage and not available in MELPA or as a Notion integration. Follow the steps below to install and use it. + +** Installation + +*** Step 1: Getting the Notion Secret + + - Go to [[https://developers.notion.com/][Notion developers page]]. + - Create an integration and obtain a secret. + +*** Step 2: Managing Secrets + + The package uses the built-in `auth-source` package to manage secrets. For the simplest setup, store your Notion secret in your `~/.authinfo` file with "notion.so" as the machine name. + + #+BEGIN_EXAMPLE + machine notion.so login this-is-not-used@example.com password secret + #+END_EXAMPLE + +**** [NOTE] You don't have to store the secret as plain text + + This requires a bit more setup as you need to have GnuPG installed, but it pays off. Emacs has built-in support to decrypt and encrypt files with `.gpg` extension on the fly, so the best option would be to store your secret in `~/.authinfo.gpg`. + +*** Step 3: Install Org Notion + + 1. Clone the `org-notion` repository: + + #+BEGIN_SRC bash + git clone git@github.com:RadekMolenda/org-notion.git org-notion + cd org-notion + #+END_SRC + + 2. Add the `org-notion` directory to Emacs load path: + + #+BEGIN_SRC emacs-lisp + (add-to-list 'load-path "/path/to/org-notion/") + (require 'org-notion) + #+END_SRC + + That's it! You have successfully installed Org Notion. Now you can keep your Org files and Notion in sync with ease. + +* TODO Usage * Testing run From 823ca1635c57dfc5380ad94eff706f397c4efd90 Mon Sep 17 00:00:00 2001 From: Radek Molenda Date: Wed, 2 Aug 2023 16:08:14 +0200 Subject: [PATCH 4/4] Set the title and start building fetching children --- org-notion.el | 49 +- .../valid-block-children-response.json | 650 ++++++++++++++++++ .../valid-block-children-response.txt | 12 - ...-response.txt => valid-page-response.json} | 11 - test/org-notion-test.el | 21 +- 5 files changed, 696 insertions(+), 47 deletions(-) create mode 100644 test/fixtures/valid-block-children-response.json delete mode 100644 test/fixtures/valid-block-children-response.txt rename test/fixtures/{valid-page-response.txt => valid-page-response.json} (72%) diff --git a/org-notion.el b/org-notion.el index bd4ec9a..ef27d1a 100644 --- a/org-notion.el +++ b/org-notion.el @@ -25,7 +25,7 @@ ;;; Code: (defvar org-notion--notion-api-url "https://api.notion.com") -(defvar org-notion--notion-page-id-property-name "NOTION_PAGE_ID") +(defvar org-notion--page-id-property-name "NOTION_PAGE_ID") (defvar org-notion--notion-password-machine "notion.so") (require 's) @@ -45,23 +45,25 @@ secret))))) (defun org-notion--make-request (method url &optional payload) - "Make the request to URL given METHOD and return a buffer with a response." + "Make the request to URL given METHOD and return parsed json object." (let* ((url-request-method method) (token (org-notion--get-password org-notion--notion-password-machine)) (url-request-extra-headers `(("Content-Type" . "application/json") ("Notion-Version" . "2022-06-28") ("Authorization" . ,(s-concat "Bearer " token)))) (url-request-data payload)) - (progn - (message (format "Payload %s" payload)) - (url-retrieve-synchronously url)))) + (with-temp-buffer (url-insert-file-contents url) (json-read)))) -(defun org-notion--fetch-page (page-id) - "Given PAGE-ID fetch the page and return a buffer response." - (org-notion--make-request "GET" (s-concat "https://api.notion.com/v1/pages/" page-id))) +(defun org-notion--retrieve-page (notion-id) + "Given NOTION-ID fetch the page and return parsed json." + (org-notion--make-request "GET" (s-concat "https://api.notion.com/v1/pages/" notion-id))) +(defun org-notion--retrieve-children-block (notion-id) + "Given NOTION-ID fetch the page and return parsed json." + (org-notion--make-request "GET" (s-concat "https://api.notion.com/v1/blocks/" notion-id "/children?page_size=100"))) + (defun org-notion--get-title (data) "Extract the 'title' from the json DATA." (let* ((properties (cdr (assoc 'properties data))) @@ -79,6 +81,7 @@ nil nil nil nil nil callback)) ;; TODO: refactor to use notion api + (defun org-notion--format-plain-text (c _i) "Default notion format H C I." (format "[\"%s\"]" c)) @@ -168,6 +171,22 @@ (verse-block . notion-format) )) +(defun org-notion--insert-page-content (notion-id) + "Given NOTION-ID fetch page data and insert title and set property name to NOTION-ID." + (let ((notion-json (org-notion--retrieve-page notion-id))) + (org-insert-heading-respect-content) + (insert (org-notion--get-title notion-json)) + (org-entry-put nil org-notion--page-id-property-name notion-id))) + +(defun org-notion--insert-block-children-content (notion-id) + + "Given NOTION-ID fetch page children and import blocks to org file." + + (let ((notion-json (org-notion--retrieve-children-block notion-id))) + (org-insert-heading-respect-content t) + (org-do-demote) + (insert "all other cool stuff is going here"))) + ;;;###autoload (defun org-notion-send-block () "Import notion block as org item. NOTION-ID is notion uuid of imported block." @@ -180,15 +199,11 @@ ;; TODO: this is where refactoring ends -; Use the following id: de466bb4-4bb2-4610-9e91-f656e2a3a6dc -(defun org-notion-import-page (page-id) - "Import notion block as org item. PAGE-ID is notion uuid of imported block." - (let* ((response-buffer (org-notion--fetch-page page-id)) - (data-json (with-current-buffer response-buffer - (search-forward "\n\n" nil 'move) - (json-read))) - (title (org-notion--get-title data-json))) - title)) +;;;###autoload +(defun org-notion-import-page (notion-id) + "Import notion block as org item. NOTION-ID is notion uuid of imported block." + (org-notion--insert-page-content notion-id) + (org-notion--insert-block-children-content notion-id)) (provide 'org-notion) diff --git a/test/fixtures/valid-block-children-response.json b/test/fixtures/valid-block-children-response.json new file mode 100644 index 0000000..5e9961b --- /dev/null +++ b/test/fixtures/valid-block-children-response.json @@ -0,0 +1,650 @@ +{ + "object": "list", + "results": [ + { + "object": "block", + "id": "d5f8d832-cd88-4836-8c66-d92b17a45cc8", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:52:00.000Z", + "last_edited_time": "2023-07-31T22:52:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "heading_1", + "heading_1": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "heading 1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "heading 1", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "b02f86d6-e08f-41a6-86d5-df262ad0cdd9", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:53:00.000Z", + "last_edited_time": "2023-07-31T22:53:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "heading 2", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "heading 2", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "a397c845-3405-4a85-89fb-5f4c465173fe", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:53:00.000Z", + "last_edited_time": "2023-07-31T22:53:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "heading_3", + "heading_3": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Heading 3", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Heading 3", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "df0f9160-be29-44c7-9516-42fa9a717fdb", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:53:00.000Z", + "last_edited_time": "2023-07-31T22:55:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "to_do", + "to_do": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "TODO One", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "TODO One", + "href": null + } + ], + "checked": false, + "color": "default" + } + }, + { + "object": "block", + "id": "124e7032-fa35-40c2-bd6d-d468ed07fb40", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:53:00.000Z", + "last_edited_time": "2023-07-31T22:54:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": true, + "archived": false, + "type": "to_do", + "to_do": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "TODO two", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "TODO two", + "href": null + } + ], + "checked": false, + "color": "default" + } + }, + { + "object": "block", + "id": "7dcd55be-dd36-41af-b84b-cc3a99e372d2", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:53:00.000Z", + "last_edited_time": "2023-07-31T22:54:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "to_do", + "to_do": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "TODO three", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "TODO three", + "href": null + } + ], + "checked": false, + "color": "default" + } + }, + { + "object": "block", + "id": "ede67d4a-685a-45f2-a869-eef29f2106ac", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:53:00.000Z", + "last_edited_time": "2023-07-31T22:53:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "paragraph", + "paragraph": { + "rich_text": [], + "color": "default" + } + }, + { + "object": "block", + "id": "7db99a78-efc0-47e8-811a-7d855286244a", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:54:00.000Z", + "last_edited_time": "2023-07-31T22:54:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "bulleted_list_item", + "bulleted_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "bullet one", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "bullet one", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "5bee2d84-038b-4787-97bc-c8dea81eb92f", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:54:00.000Z", + "last_edited_time": "2023-07-31T22:54:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "bulleted_list_item", + "bulleted_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "bullet two", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "bullet two", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "0ce4fe5e-a8be-4a19-9b03-b588943b830c", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:54:00.000Z", + "last_edited_time": "2023-07-31T22:54:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "paragraph", + "paragraph": { + "rich_text": [], + "color": "default" + } + }, + { + "object": "block", + "id": "51d0d32b-c7c0-468d-a983-7421019aaaac", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:54:00.000Z", + "last_edited_time": "2023-07-31T22:54:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "numbered_list_item", + "numbered_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "number one", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "number one", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "20d675c7-6a41-4f36-9ddc-3800c34f6be9", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:54:00.000Z", + "last_edited_time": "2023-07-31T22:55:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": true, + "archived": false, + "type": "numbered_list_item", + "numbered_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "number two", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "number two", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "6a506d6f-9d99-4b91-930a-d25b05a566a4", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:55:00.000Z", + "last_edited_time": "2023-07-31T22:55:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "numbered_list_item", + "numbered_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "number three", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "number three", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "7df122e8-4195-40a4-8938-fe06f58d0f76", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T22:55:00.000Z", + "last_edited_time": "2023-07-31T22:55:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "paragraph", + "paragraph": { + "rich_text": [], + "color": "default" + } + }, + { + "object": "block", + "id": "b2d5e08e-3664-403a-9dff-8c54faf3449d", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T20:12:00.000Z", + "last_edited_time": "2023-07-31T22:56:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Some content and checkout ", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Some content and checkout ", + "href": null + }, + { + "type": "text", + "text": { + "content": "this page", + "link": { + "url": "https://www.google.com/" + } + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "this page", + "href": "https://www.google.com/" + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "f7d61432-e124-4735-b00d-fa176877bdd1", + "parent": { + "type": "page_id", + "page_id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc" + }, + "created_time": "2023-07-31T20:13:00.000Z", + "last_edited_time": "2023-07-31T20:13:00.000Z", + "created_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "last_edited_by": { + "object": "user", + "id": "aee4ec30-692d-499d-af7b-30ecd7c6112e" + }, + "has_children": false, + "archived": false, + "type": "paragraph", + "paragraph": { + "rich_text": [], + "color": "default" + } + } + ], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} diff --git a/test/fixtures/valid-block-children-response.txt b/test/fixtures/valid-block-children-response.txt deleted file mode 100644 index 78260f4..0000000 --- a/test/fixtures/valid-block-children-response.txt +++ /dev/null @@ -1,12 +0,0 @@ -HTTP/2 200 -date: Tue, 01 Aug 2023 07:37:54 GMT -content-type: application/json; charset=utf-8 -x-powered-by: Express -etag: W/"2a78-tfBUxjWqFWU0PSu5lWcNWwWJUE0" -vary: Accept-Encoding -cf-cache-status: DYNAMIC -set-cookie: __cf_bm=JEM779Bf8h2YPCgLehzS4xsRpujWqU5H8sQcJQHgLY4-1690875474-0-AXSNJvx65rqCCyDngVbm4tgKYw99C2CqiYLTjny9FNCCjKPru6wLuYYHdxM9fxa2EaYWePip42ywmH4tNPIwnNA=; path=/; expires=Tue, 01-Aug-23 08:07:54 GMT; domain=.notion.com; HttpOnly; Secure; SameSite=None -server: cloudflare -cf-ray: 7efc6b2109d1bfd7-WAW - -{"object":"list","results":[{"object":"block","id":"d5f8d832-cd88-4836-8c66-d92b17a45cc8","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:52:00.000Z","last_edited_time":"2023-07-31T22:52:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"heading_1","heading_1":{"rich_text":[{"type":"text","text":{"content":"heading 1","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"heading 1","href":null}],"is_toggleable":false,"color":"default"}},{"object":"block","id":"b02f86d6-e08f-41a6-86d5-df262ad0cdd9","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:53:00.000Z","last_edited_time":"2023-07-31T22:53:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"heading_2","heading_2":{"rich_text":[{"type":"text","text":{"content":"heading 2","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"heading 2","href":null}],"is_toggleable":false,"color":"default"}},{"object":"block","id":"a397c845-3405-4a85-89fb-5f4c465173fe","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:53:00.000Z","last_edited_time":"2023-07-31T22:53:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"heading_3","heading_3":{"rich_text":[{"type":"text","text":{"content":"Heading 3","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"Heading 3","href":null}],"is_toggleable":false,"color":"default"}},{"object":"block","id":"df0f9160-be29-44c7-9516-42fa9a717fdb","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:53:00.000Z","last_edited_time":"2023-07-31T22:55:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"to_do","to_do":{"rich_text":[{"type":"text","text":{"content":"TODO One","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"TODO One","href":null}],"checked":false,"color":"default"}},{"object":"block","id":"124e7032-fa35-40c2-bd6d-d468ed07fb40","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:53:00.000Z","last_edited_time":"2023-07-31T22:54:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":true,"archived":false,"type":"to_do","to_do":{"rich_text":[{"type":"text","text":{"content":"TODO two","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"TODO two","href":null}],"checked":false,"color":"default"}},{"object":"block","id":"7dcd55be-dd36-41af-b84b-cc3a99e372d2","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:53:00.000Z","last_edited_time":"2023-07-31T22:54:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"to_do","to_do":{"rich_text":[{"type":"text","text":{"content":"TODO three","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"TODO three","href":null}],"checked":false,"color":"default"}},{"object":"block","id":"ede67d4a-685a-45f2-a869-eef29f2106ac","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:53:00.000Z","last_edited_time":"2023-07-31T22:53:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"paragraph","paragraph":{"rich_text":[],"color":"default"}},{"object":"block","id":"7db99a78-efc0-47e8-811a-7d855286244a","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:54:00.000Z","last_edited_time":"2023-07-31T22:54:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"bulleted_list_item","bulleted_list_item":{"rich_text":[{"type":"text","text":{"content":"bullet one","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"bullet one","href":null}],"color":"default"}},{"object":"block","id":"5bee2d84-038b-4787-97bc-c8dea81eb92f","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:54:00.000Z","last_edited_time":"2023-07-31T22:54:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"bulleted_list_item","bulleted_list_item":{"rich_text":[{"type":"text","text":{"content":"bullet two","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"bullet two","href":null}],"color":"default"}},{"object":"block","id":"0ce4fe5e-a8be-4a19-9b03-b588943b830c","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:54:00.000Z","last_edited_time":"2023-07-31T22:54:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"paragraph","paragraph":{"rich_text":[],"color":"default"}},{"object":"block","id":"51d0d32b-c7c0-468d-a983-7421019aaaac","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:54:00.000Z","last_edited_time":"2023-07-31T22:54:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"numbered_list_item","numbered_list_item":{"rich_text":[{"type":"text","text":{"content":"number one","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"number one","href":null}],"color":"default"}},{"object":"block","id":"20d675c7-6a41-4f36-9ddc-3800c34f6be9","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:54:00.000Z","last_edited_time":"2023-07-31T22:55:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":true,"archived":false,"type":"numbered_list_item","numbered_list_item":{"rich_text":[{"type":"text","text":{"content":"number two","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"number two","href":null}],"color":"default"}},{"object":"block","id":"6a506d6f-9d99-4b91-930a-d25b05a566a4","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:55:00.000Z","last_edited_time":"2023-07-31T22:55:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"numbered_list_item","numbered_list_item":{"rich_text":[{"type":"text","text":{"content":"number three","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"number three","href":null}],"color":"default"}},{"object":"block","id":"7df122e8-4195-40a4-8938-fe06f58d0f76","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T22:55:00.000Z","last_edited_time":"2023-07-31T22:55:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"paragraph","paragraph":{"rich_text":[],"color":"default"}},{"object":"block","id":"b2d5e08e-3664-403a-9dff-8c54faf3449d","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T20:12:00.000Z","last_edited_time":"2023-07-31T22:56:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"paragraph","paragraph":{"rich_text":[{"type":"text","text":{"content":"Some content and checkout ","link":null},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"Some content and checkout ","href":null},{"type":"text","text":{"content":"this page","link":{"url":"https://www.google.com/"}},"annotations":{"bold":false,"italic":false,"strikethrough":false,"underline":false,"code":false,"color":"default"},"plain_text":"this page","href":"https://www.google.com/"}],"color":"default"}},{"object":"block","id":"f7d61432-e124-4735-b00d-fa176877bdd1","parent":{"type":"page_id","page_id":"de466bb4-4bb2-4610-9e91-f656e2a3a6dc"},"created_time":"2023-07-31T20:13:00.000Z","last_edited_time":"2023-07-31T20:13:00.000Z","created_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"last_edited_by":{"object":"user","id":"aee4ec30-692d-499d-af7b-30ecd7c6112e"},"has_children":false,"archived":false,"type":"paragraph","paragraph":{"rich_text":[],"color":"default"}}],"next_cursor":null,"has_more":false,"type":"block","block":{}} \ No newline at end of file diff --git a/test/fixtures/valid-page-response.txt b/test/fixtures/valid-page-response.json similarity index 72% rename from test/fixtures/valid-page-response.txt rename to test/fixtures/valid-page-response.json index 6252e77..47657b6 100644 --- a/test/fixtures/valid-page-response.txt +++ b/test/fixtures/valid-page-response.json @@ -1,14 +1,3 @@ -HTTP/2 200 -date: Tue, 01 Aug 2023 07:37:07 GMT -content-type: application/json; charset=utf-8 -x-powered-by: Express -etag: W/"41a-C4IquhvmvyaFm91BgpPwtVairf8" -vary: Accept-Encoding -cf-cache-status: DYNAMIC -set-cookie: __cf_bm=0xoPDtxPNhEUvY7.fxOT54hs.nGaWX3b6UN.puNspOk-1690875426-0-AQ6rc0L382iA/jtbBdiK5PXJQGEKAKWges8eVWDBS3ii8VERz/EzQ5LyarM1ij9bjZo5xiuc0aAVTcewiMrHIG0=; path=/; expires=Tue, 01-Aug-23 08:07:06 GMT; domain=.notion.com; HttpOnly; Secure; SameSite=None -server: cloudflare -cf-ray: 7efc69f9581cbfd5-WAW - { "object": "page", "id": "de466bb4-4bb2-4610-9e91-f656e2a3a6dc", diff --git a/test/org-notion-test.el b/test/org-notion-test.el index c239353..652c1f3 100644 --- a/test/org-notion-test.el +++ b/test/org-notion-test.el @@ -33,17 +33,24 @@ (lambda (method url &optional payload) (should (equal method "GET")) (should (equal url "https://api.notion.com/v1/pages/xxxx-xxxx-xxxx-xxxxxxxx"))))) - (org-notion--fetch-page "xxxx-xxxx-xxxx-xxxxxxxx"))) + (org-notion--retrieve-page "xxxx-xxxx-xxxx-xxxxxxxx"))) -(setq stub-org-notion--fetch-page (lambda (page-id) - (let ((buffer (find-file-noselect "test/fixtures/valid-page-response.txt"))) - (with-current-buffer buffer (setq buffer-read-only t) buffer)))) +(setq stub-org-notion--retrieve-page (lambda (page-id) + (let ((buffer (find-file-noselect "test/fixtures/valid-page-response.json"))) + (with-current-buffer buffer (setq buffer-read-only t) (json-read))))) + +(setq stub-org-notion--retrieve-children-block (lambda (notion-id) + (let ((buffer (find-file-noselect "test/fixtures/valid-block-children-response.json"))) + (with-current-buffer buffer (setq buffer-read-only t) (json-read))))) (ert-deftest importing-page () "Importing page builds buffer with correct org data in it" - (cl-letf (((symbol-function 'org-notion--fetch-page) - stub-org-notion--fetch-page)) - (should (s-equals? (org-notion-import-page "whatever") "yes yes yes")))) + (cl-letf (((symbol-function 'org-notion--retrieve-page) stub-org-notion--retrieve-page) + ((symbol-function 'org-notion--retrieve-children-block) stub-org-notion--retrieve-children-block)) + (with-temp-buffer + (org-mode) + (org-notion-import-page "some-notion-id") + (should (s-equals? (buffer-substring-no-properties (point-min) (point-max)) "* yes yes yes\n:PROPERTIES:\n:NOTION_PAGE_ID: some-notion-id\n:END:\n** all other cool stuff is going here\n")))))