notion 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b3ebde6eb43b792a313801584c8043c579e9fd356427df080a14c265e28cf2db
4
+ data.tar.gz: 9bf0242abe3ad4c15cebbb9a1cdd65ae64b6ec76a1d8e872836a90b5c2ec9404
5
+ SHA512:
6
+ metadata.gz: 2f0b872d9f1eb4f88f05e4d8e41959291dc4b86bdb0be46582d196de095f174907b78c165c35140c7bc68b41b4cd6b7b7098ae23a3502d8adecc488e9185c03a
7
+ data.tar.gz: 8c69f7a02a7374a69590b4b1cdb92767557efaf7f56e5fa7fe3f8450f78c19e4fa9e37b5db8c46e9aef6661dda8dfc56692baeff57912769f052e831af9ae594
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Dan Murphy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,196 @@
1
+ # Unofficial Notion Client for Ruby.
2
+ [![Build Status](https://travis-ci.com/danmurphy1217/notion-ruby.svg?branch=master)](https://travis-ci.com/danmurphy1217/notion-ruby) [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop-hq/rubocop)
3
+
4
+ ## Getting Started
5
+ To get started using package, you'll first need to retrieve your token_v2 credentials by signing into Notion online, navigating to the developer tools, inspecting the cookies, and finding the value associated with the **token_v2** key.
6
+
7
+ From here, you can instantiate the Notion Client with the following code:
8
+ ```ruby
9
+ >>> @client = NotionAPI::Client.new("<insert_v2_token_here>")
10
+ ```
11
+ ## Retrieving a Page
12
+ A typical starting point is the `get_page` method, which returns a Notion Page Block. The `get_page` method accepts the ID (formatted or not) or the URL of the page:
13
+ 1. URL → https://www.notion.so/danmurphy/TEST-PAGE-d2ce338f19e847f586bd17679f490e66
14
+ 2. ID → d2ce338f19e847f586bd17679f490e66
15
+ 3. Formatted ID → d2ce338f-19e8-47f5-86bd-17679f490e66
16
+ ```ruby
17
+ >>> @client.get_page("https://www.notion.so/danmurphy/TEST-PAGE-d2ce338f19e847f586bd17679f490e66")
18
+ >>> @client.get_page("d2ce338f19e847f586bd17679f490e66")
19
+ >>> @client.get_page("d2ce338f-19e8-47f5-86bd-17679f490e66")
20
+ ```
21
+ All three of these will return the same block instance:
22
+ ```ruby
23
+ #<NotionAPI::PageBlock id="d2ce338f-19e8-47f5-86bd-17679f490e66" title="TEST" parent_id="<omitted>">
24
+ ```
25
+ The following attributes can be read from any block class instance:
26
+ 1. `id`: the ID associated with the block.
27
+ 2. `title`: the title associated with the block.
28
+ 3. `parent_id`: the parent ID of the block.
29
+ 4. `type`: the type of the block.
30
+
31
+ ## Retrieving a Block within the Page
32
+ Now that you have retrieved a Notion Page, you have full access to the blocks on that page. You can retrieve a specific block or collection view, retrieve all children IDs (array of children IDs), or retrieve all children (array of children class instances).
33
+
34
+ ### Get a Block
35
+ To retrieve a specific block, you can use the `get_block` method. This method accepts the ID of the block (formatted or not), and will return the block as an instantiated class instance:
36
+ ```ruby
37
+ @page = @client.get_page("https://www.notion.so/danmurphy/TEST-PAGE-d2ce338f19e847f586bd17679f490e66")
38
+ @page.get_block("2cbbe0bf-34cd-409b-9162-64284b33e526")
39
+ #<TextBlock id="2cbbe0bf-34cd-409b-9162-64284b33e526" title="TEST" parent_id="d2ce338f-19e8-47f5-86bd-17679f490e66">
40
+ ```
41
+ Any Notion Block has access to the following methods:
42
+ 1. `title=` → change the title of a block.
43
+ ```ruby
44
+ >>> @block = @client.get_block("2cbbe0bf-34cd-409b-9162-64284b33e526")
45
+ >>> @block.title # get the current title...
46
+ "TEST"
47
+ >>> @block.title= "New Title Here" # lets update it...
48
+ >>> @block.title
49
+ "New Title Here"
50
+ ```
51
+ 2. `convert` → convert a block to a different type.
52
+ ```ruby
53
+ >>> @block = @client.get_block("2cbbe0bf-34cd-409b-9162-64284b33e526")
54
+ >>> @block.type
55
+ "text"
56
+ >>> @new_block = @block.convert(NotionAPI::CalloutBlock)
57
+ >>> @new_block.type
58
+ "callout"
59
+ >>> @new_block # new class instance returned...
60
+ #<NotionAPI::CalloutBlock:0x00007ffb75b19ea0 id="2cbbe0bf-34cd-409b-9162-64284b33e526" title="New Title Here" parent_id="d2ce338f-19e8-47f5-86bd-17679f490e66">
61
+ ```
62
+ 3. `duplicate`→ duplicate the current block.
63
+ ```ruby
64
+ >>> @block = @client.get_block("2cbbe0bf-34cd-409b-9162-64284b33e526")
65
+ >>> @block.duplicate # block is duplicated and placed directly after the current block
66
+ >>> @block.duplicate("f13da22b-9012-4c49-ac41-6b7f97bd519e") # the duplicated block is placed after 'f13da22b-9012-4c49-ac41-6b7f97bd519e'
67
+ ```
68
+ 4. `move` → move a block to another location.
69
+ ```ruby
70
+ >>> @block = @client.get_block("2cbbe0bf-34cd-409b-9162-64284b33e526")
71
+ >>> @target_block = @client.get_block("c3ce468f-11e3-48g5-87be-27679g491e66")
72
+ >>> @block.move(@target_block) # @block moved to **after** @target_block
73
+ >>> @block.move(@target_block, "before") # @block moved to **before** @target_block
74
+ ```
75
+ ### Get a Collection View - Table
76
+ To retrieve a collection, you use the `get_collection` method. This method is designed to work with Table collections, but the codebase is actively being updated to support others:
77
+ ```ruby
78
+ >>> @page = @client.get_page("https://www.notion.so/danmurphy/TEST-PAGE-d2ce338f19e847f586bd17679f490e66")
79
+ >>> @page.get_collection("34d03794-ecdd-e42d-bb76-7e4aa77b6503")
80
+ #<NotionAPI::CollectionView:0x00007fecd8859770 @id="34d03794-ecdd-e42d-bb76-7e4aa77b6503", @title="Car Data", @parent_id="9c50a7b3-9ad7-4f2b-aa08-b3c95f1f19e7", @collection_id="5ea0fa7c-00cd-4ee0-1915-8b5c423f8f3a", @view_id="5fdb08da-0732-49dc-d0c3-2e31fccca73a">
81
+ ```
82
+ Any Notion Block has access to the following methods:
83
+ 1. `row_ids` → retrieve the IDs associated with each row.
84
+ ```ruby
85
+ >>> @collection = @page.get_collection("34d03794-ecdd-e42d-bb76-7e4aa77b6503")
86
+ >>> @collection.row_ids
87
+ ent.rb
88
+ ["785f4e24-e489-a316-50cf-b0b100c6673a", "78642d95-da23-744c-b084-46d039927bba", "96dff83c-6961-894c-39c2-c2c8bfcbfa90", "87ae8ae7-5518-fbe1-748e-eb690c707fac",..., "5a50bdd4-69c5-0708-5093-b135676e83c1", "ff9b8b89-1fed-f955-4afa-5a071198b0ee", "721fe76a-9e3c-d348-8324-994c95d77b2e"]
89
+ ```
90
+ 2. `rows` → retrieve each Row, returned as an array of TableRowInstance classes.
91
+ ```ruby
92
+ >>> @collection = @page.get_collection("34d03794-ecdd-e42d-bb76-7e4aa77b6503")
93
+ >>> @collection.rows
94
+ #<NotionAPI::CollectionViewRow:0x00007ffecca82078 @id="785f4e24-e489-a316-50cf-b0b100c6673a", @parent_id="9c50a7b3-9ad7-4f2b-aa08-b3c95f1f19e7", @collection_id="5ea0fa7c-00cd-4ee0-1915-8b5c423f8f3a", @view_id="5fdb08da-0732-49dc-d0c3-2e31fccca73a">,..., #<NotionAPI::CollectionViewRow:0x00007ffecca81998 @id="fbf44f93-52ee-0e88-262a-94982ffb3fb2", @parent_id="9c50a7b3-9ad7-4f2b-aa08-b3c95f1f19e7", @collection_id="5ea0fa7c-00cd-4ee0-1915-8b5c423f8f3a", @view_id="5fdb08da-0732-49dc-d0c3-2e31fccca73a">]
95
+ ```
96
+ 3. `row("<row_id>")` → retrieve a specific row.
97
+ ```ruby
98
+ >>> @collection = @page.get_collection("34d03794-ecdd-e42d-bb76-7e4aa77b6503")
99
+ >>> @collection.row("f1c7077f-44a9-113d-a156-90ab6880c3e2")
100
+ {"age"=>[9], "vin"=>["1C6SRFLT1MN591852"], "body"=>["4D Crew Cab"], "fuel"=>["Gasoline"], "make"=>["Ram"], "msrp"=>[64935], "year"=>[2021], "model"=>[1500], "price"=>[59688], "stock"=>["21R14"], "dealerid"=>["MP2964D"], "colour"=>["Bright White Clearcoat"], "engine"=>["HEMI 5.7L V8 Multi Displacement VVT"], "photos"=>["http://vehicle-photos-published.vauto.com/d0/c2/dd/8b-1307-4c67-8d31-5a301764b875/image-1.jpg"], "series"=>["Rebel"], "newused"=>["N"], "city_mpg"=>[""],...,"engine_cylinder_ct"=>[8], "engine_displacement"=>[5.7], "photos_last_modified_date"=>["11/13/2020 8:16:56 AM"]}
101
+ ```
102
+
103
+ ## Creating New Blocks
104
+ To create a new block, you have a few options:
105
+ ### Create a block whose parent is the page
106
+ If you want to create a new block whose parent ID is the **page**, call the `create` method on the PageBlock instance.
107
+ 1. `@page.create("<type_of_block", "title of block")` → create a new block at the end of the page.
108
+ ```ruby
109
+ >>> @page = @client.get_page("https://www.notion.so/danmurphy/Notion-API-Testing-66447bc817f044bc81ed3cf4802e9b00")
110
+ >>> @page.create(NotionAPI::TextBlock, "Hiya!")
111
+ #<NotionAPI::TextBlock:0x00007fecd4459770 **omitted**>
112
+ ```
113
+ 2. `@page.create("<type_of_block", "title of block", "target_block_id")` → create a new block after the target block.
114
+ ```ruby
115
+ >>> @page = @client.get_page("https://www.notion.so/danmurphy/Notion-API-Testing-66447bc817f044bc81ed3cf4802e9b00")
116
+ >>> @page.create(NotionAPI::TextBlock, "Hiya!", "ee0a6531-44cd-439f-a68c-1bdccbebfc8a")
117
+ #<NotionAPI::TextBlock:0x00007fecd8859770 **omitted**>
118
+ ```
119
+ 3. `@page.create("<type_of_block"), "title of block", "target_block_id", "before/after")` → create a new block after or before the target block.
120
+ ```ruby
121
+ >>> @page = @client.get_page("https://www.notion.so/danmurphy/Notion-API-Testing-66447bc817f044bc81ed3cf4802e9b00")
122
+ >>> @page.create(NotionAPI::TextBlock, "Hiya!", "ee0a6531-44cd-439f-a68c-1bdccbebfc8a", "before")
123
+ #<NotionAPI::TextBlock:0x00007fecd8859880 **omitted**>
124
+ ```
125
+ ### Create a block whose parent is another block
126
+ If you want to create a nested block whose parent ID is **another block**, call the `create` method on that block.
127
+ 1. `@block.create("<type_of_block", "title of block")` → create a new nested block whose parent ID is @block.id
128
+ ```ruby
129
+ >>> @page = @client.get_page("https://www.notion.so/danmurphy/Notion-API-Testing-66447bc817f044bc81ed3cf4802e9b00")
130
+ >>> @block = @page.get_block("2cbbe0bf-34cd-409b-9162-64284b33e526")
131
+ >>> @block.create(NotionAPI::TextBlock, "Hiya!") # create a nested text block
132
+ #<NotionAPI::TextBlock:0x00007fecd8861780 **omitted**>
133
+ ```
134
+ 2. `@block.create("<type_of_block", "title of block", "target_block")` → create a new nested block whose parent ID is @block.id and whose location is after the target block
135
+ ```ruby
136
+ >>> @page = @client.get_page("https://www.notion.so/danmurphy/Notion-API-Testing-66447bc817f044bc81ed3cf4802e9b00")
137
+ >>> @block = @page.get_block("2cbbe0bf-34cd-409b-9162-64284b33e526")
138
+ >>> @block.create(NotionAPI::TextBlock, "Hiya!" "ae3d1c60-b9d1-0ac0-0fff-16d3fc8907a2") # create a nested text block after a specific child
139
+ #<NotionAPI::TextBlock:0x00007fecd8859781 **omitted**>
140
+ ```
141
+ 3. `@block.create("<type_of_block", "title of block", "target_block", "before/after")` → reate a new nested block whose parent ID is @block.id and whose location is before the target block
142
+ ```ruby
143
+ >>> @page = @client.get_page("https://www.notion.so/danmurphy/Notion-API-Testing-66447bc817f044bc81ed3cf4802e9b00")
144
+ >>> @block = @page.get_block("2cbbe0bf-34cd-409b-9162-64284b33e526")
145
+ >>> @block.create(NotionAPI::TextBlock, "Hiya!" "ae3d1c60-b9d1-0ac0-0fff-16d3fc8907a2", "before") # create a nested text block before a specific child
146
+ #<NotionAPI::TextBlock:0x00007fecd8859781 **omitted**>
147
+ ```
148
+ The simplest way to describe this: the parent ID of the created block is the ID of the block the `create` method is invoked on. If the `create` method is invoked on a **PageBlock**, the block is a child of that page. If the `create` method is invoked on a block within the page, the block is a child of that block.
149
+
150
+ ** NOTE: Notion only supports 'nesting' certain block types. If you try to nest a block that cannot be nested, it will fail. **
151
+ ## Creating New Collections
152
+ Let's say we have the following JSON data:
153
+ ```json
154
+ [
155
+ {
156
+ "emoji": "😀",
157
+ "description": "grinning face",
158
+ "category": "Smileys & Emotion",
159
+ "aliases": ["grinning"],
160
+ "tags": ["smile", "happy"],
161
+ "unicode_version": "6.1",
162
+ "ios_version": "6.0"
163
+ },
164
+ {
165
+ "emoji": "😃",
166
+ "description": "grinning face with big eyes",
167
+ "category": "Smileys & Emotion",
168
+ "aliases": ["smiley"],
169
+ "tags": ["happy", "joy", "haha"],
170
+ "unicode_version": "6.0",
171
+ "ios_version": "6.0"
172
+ }
173
+ ]
174
+ ```
175
+ A new collection containing this data is created with the following code:
176
+ ```ruby
177
+ >>> @page = @client.get_page("https://www.notion.so/danmurphy/Notion-API-Testing-66447bc817f044bc81ed3cf4802e9b00")
178
+ >>> @page.create_collection("table", "title for table", JSON.parse(File.read("./path/to/emoji_json_data.json")))
179
+ ```
180
+ Additionally, say you already have a Table and want to add a new row with it containing the following data:
181
+ ```ruby
182
+ {
183
+ "emoji": "😉",
184
+ "description": "winking face",
185
+ "category": "Smileys & Emotion",
186
+ "aliases": ["wink"],
187
+ "tags": ["flirt"],
188
+ "unicode_version": "6.0",
189
+ "ios_version": "6.0"
190
+ }
191
+ ```
192
+ ```ruby
193
+ >>> @page = @client.get_page("https://www.notion.so/danmurphy/Notion-API-Testing-66447bc817f044bc81ed3cf4802e9b00")
194
+ >>> @collection = @page.get_collection("f1664a99-165b-49cc-811c-84f37655908a")
195
+ >>> @collection.add_row(JSON.parse(File.read("path/to/new_emoji_row.json")))
196
+ ```
@@ -0,0 +1,5 @@
1
+ require "notion_api/client"
2
+ require "notion_api/core"
3
+ require "notion_api/blocks"
4
+ require "notion_api/utils"
5
+ require "notion_api/markdown"
@@ -0,0 +1,984 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'core'
4
+ require 'httparty'
5
+
6
+ module NotionAPI
7
+ # Base Template for all blocks. Inherits core methods from the Block class defined in block.rb
8
+ class BlockTemplate < Core
9
+ include Utils
10
+
11
+ attr_reader :id, :title, :parent_id
12
+
13
+ def initialize(id, title, parent_id)
14
+ @id = id
15
+ @title = title
16
+ @parent_id = parent_id
17
+ end
18
+
19
+ def title=(new_title)
20
+ # ! Change the title of a block.
21
+ # ! new_title -> new title for the block : ``str``
22
+ request_id = extract_id(SecureRandom.hex(16))
23
+ transaction_id = extract_id(SecureRandom.hex(16))
24
+ space_id = extract_id(SecureRandom.hex(16))
25
+ update_title(new_title.to_s, request_id, transaction_id, space_id)
26
+ @title = new_title
27
+ end
28
+
29
+ def convert(block_class_to_convert_to)
30
+ # ! convert a block from its current type to another.
31
+ # ! block_class_to_convert_to -> the type of block to convert to : ``cls``
32
+ if type == block_class_to_convert_to.notion_type
33
+ # if converting to same type, skip and return self
34
+ self
35
+ else
36
+ # setup cookies, headers, and grab/create static vars for request
37
+ cookies = Core.options['cookies']
38
+ headers = Core.options['headers']
39
+ request_url = URLS[:UPDATE_BLOCK]
40
+
41
+ # set random IDs for request
42
+ request_id = extract_id(SecureRandom.hex(16))
43
+ transaction_id = extract_id(SecureRandom.hex(16))
44
+ space_id = extract_id(SecureRandom.hex(16))
45
+ request_ids = {
46
+ request_id: request_id,
47
+ transaction_id: transaction_id,
48
+ space_id: space_id
49
+ }
50
+
51
+ # build hash's that contain the operations to send to Notions backend
52
+ convert_type_hash = Utils::BlockComponents.convert_type(@id, block_class_to_convert_to)
53
+ last_edited_time_parent_hash = Utils::BlockComponents.last_edited_time(@parent_id)
54
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(@id)
55
+
56
+ operations = [
57
+ convert_type_hash,
58
+ last_edited_time_parent_hash,
59
+ last_edited_time_child_hash
60
+ ]
61
+
62
+ request_body = build_payload(operations, request_ids)
63
+ response = HTTParty.post(
64
+ request_url,
65
+ body: request_body.to_json,
66
+ cookies: cookies,
67
+ headers: headers
68
+ )
69
+ unless response.code == 200; raise "There was an issue completing your request. Here is the response from Notion: #{response.body}, and here is the payload that was sent: #{operations}.
70
+ Please try again, and if issues persist open an issue in GitHub."; end
71
+
72
+ block_class_to_convert_to.new(@id, @title, @parent_id)
73
+
74
+ end
75
+ end
76
+
77
+ def duplicate(target_block = nil)
78
+ # ! duplicate the block that this method is invoked upon.
79
+ # ! target_block -> the block to place the duplicated block after. Can be any valid Block ID! : ``str``
80
+ cookies = Core.options['cookies']
81
+ headers = Core.options['headers']
82
+ request_url = URLS[:UPDATE_BLOCK]
83
+
84
+ new_block_id = extract_id(SecureRandom.hex(16))
85
+ request_id = extract_id(SecureRandom.hex(16))
86
+ transaction_id = extract_id(SecureRandom.hex(16))
87
+ space_id = extract_id(SecureRandom.hex(16))
88
+
89
+ root_children = children_ids(@id)
90
+ sub_children = []
91
+ root_children.each { |root_id| sub_children.push(children_ids(root_id)) }
92
+
93
+ request_ids = {
94
+ request_id: request_id,
95
+ transaction_id: transaction_id,
96
+ space_id: space_id
97
+ }
98
+ body = {
99
+ pageId: @id,
100
+ chunkNumber: 0,
101
+ limit: 100,
102
+ verticalColumns: false
103
+ }
104
+
105
+ user_notion_id = get_notion_id(body)
106
+
107
+ block = target_block ? get(target_block) : self # allows dev to place block anywhere!
108
+
109
+ duplicate_hash = Utils::BlockComponents.duplicate(type, @title, block.id, new_block_id, user_notion_id, root_children)
110
+ set_parent_alive_hash = Utils::BlockComponents.set_parent_to_alive(block.parent_id, new_block_id)
111
+ block_location_hash = Utils::BlockComponents.block_location_add(block.parent_id, block.id, new_block_id, target_block, 'listAfter')
112
+ last_edited_time_parent_hash = Utils::BlockComponents.last_edited_time(block.parent_id)
113
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(block.id)
114
+
115
+ operations = [
116
+ duplicate_hash,
117
+ set_parent_alive_hash,
118
+ block_location_hash,
119
+ last_edited_time_parent_hash,
120
+ last_edited_time_child_hash
121
+ ]
122
+
123
+ request_body = build_payload(operations, request_ids)
124
+ response = HTTParty.post(
125
+ request_url,
126
+ body: request_body.to_json,
127
+ cookies: cookies,
128
+ headers: headers
129
+ )
130
+ unless response.code == 200; raise "There was an issue completing your request. Here is the response from Notion: #{response.body}, and here is the payload that was sent: #{operations}.
131
+ Please try again, and if issues persist open an issue in GitHub."; end
132
+
133
+ class_to_return = NotionAPI.const_get(Classes.select { |cls| NotionAPI.const_get(cls).notion_type == type }.join.to_s)
134
+ class_to_return.new(new_block_id, @title, block.parent_id)
135
+ end
136
+
137
+ def move(target_block, position = 'after')
138
+ # ! move the block to a new location.
139
+ # ! target_block -> the targetted block to move to. : ``str``
140
+ # ! position -> where the block should be listed, in positions relative to the target_block [before, after, top-child, last-child]
141
+ positions_hash = {
142
+ 'after' => 'listAfter',
143
+ 'before' => 'listBefore'
144
+ }
145
+
146
+ unless positions_hash.keys.include?(position); raise ArgumentError, "Invalid position. You said: #{position}, valid options are: #{positions_hash.keys.join(', ')}"; end
147
+
148
+ position_command = positions_hash[position]
149
+ cookies = Core.options['cookies']
150
+ headers = Core.options['headers']
151
+ request_url = URLS[:UPDATE_BLOCK]
152
+
153
+ request_id = extract_id(SecureRandom.hex(16))
154
+ transaction_id = extract_id(SecureRandom.hex(16))
155
+ space_id = extract_id(SecureRandom.hex(16))
156
+
157
+ request_ids = {
158
+ request_id: request_id,
159
+ transaction_id: transaction_id,
160
+ space_id: space_id
161
+ }
162
+
163
+ check_parents = (@parent_id == target_block.parent_id)
164
+ set_block_dead_hash = Utils::BlockComponents.set_block_to_dead(@id) # kill the block this method is invoked on...
165
+ block_location_remove_hash = Utils::BlockComponents.block_location_remove(@parent_id, @id) # remove the block this method is invoked on...
166
+ parent_location_hash = Utils::BlockComponents.parent_location_add(check_parents ? @parent_id : target_block.parent_id, @id) # set parent location to alive
167
+ block_location_add_hash = Utils::BlockComponents.block_location_add(check_parents ? @parent_id : target_block.parent_id, @id, target_block.id, position_command)
168
+ last_edited_time_parent_hash = Utils::BlockComponents.last_edited_time(@parent_id)
169
+
170
+ if check_parents
171
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(@id)
172
+ operations = [
173
+ set_block_dead_hash,
174
+ block_location_remove_hash,
175
+ parent_location_hash,
176
+ block_location_add_hash,
177
+ last_edited_time_parent_hash,
178
+ last_edited_time_child_hash
179
+ ]
180
+ else
181
+ last_edited_time_new_parent_hash = Utils::BlockComponents.last_edited_time(target_block.parent_id)
182
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(@id)
183
+ @parent_id = target_block.parent_id
184
+ operations = [
185
+ set_block_dead_hash,
186
+ block_location_remove_hash,
187
+ parent_location_hash,
188
+ block_location_add_hash,
189
+ last_edited_time_parent_hash,
190
+ last_edited_time_new_parent_hash,
191
+ last_edited_time_child_hash
192
+ ]
193
+ end
194
+ request_body = build_payload(operations, request_ids)
195
+ response = HTTParty.post(
196
+ request_url,
197
+ body: request_body.to_json,
198
+ cookies: cookies,
199
+ headers: headers
200
+ )
201
+ unless response.code == 200; raise "There was an issue completing your request. Here is the response from Notion: #{response.body}, and here is the payload that was sent: #{operations}.
202
+ Please try again, and if issues persist open an issue in GitHub."; end
203
+
204
+ self
205
+ end
206
+
207
+ def create(block_type, block_title, target = nil, position = 'after')
208
+ # ! create a new block
209
+ # ! block_type -> the type of block to create : ``cls``
210
+ # ! block_title -> the title of the new block : ``str``
211
+ # ! target -> the block_id that the new block should be placed after. ``str``
212
+ # ! position -> 'after' or 'before'
213
+ positions_hash = {
214
+ 'after' => 'listAfter',
215
+ 'before' => 'listBefore'
216
+ }
217
+ unless positions_hash.keys.include?(position); raise "Invalid position. You said: #{position}, valid options are: #{positions_hash.keys.join(', ')}"; end
218
+
219
+ position_command = positions_hash[position]
220
+
221
+ cookies = Core.options['cookies']
222
+ headers = Core.options['headers']
223
+
224
+ new_block_id = extract_id(SecureRandom.hex(16))
225
+ request_id = extract_id(SecureRandom.hex(16))
226
+ transaction_id = extract_id(SecureRandom.hex(16))
227
+ space_id = extract_id(SecureRandom.hex(16))
228
+
229
+ request_ids = {
230
+ request_id: request_id,
231
+ transaction_id: transaction_id,
232
+ space_id: space_id
233
+ }
234
+
235
+ create_hash = Utils::BlockComponents.create(new_block_id, block_type.notion_type)
236
+ set_parent_alive_hash = Utils::BlockComponents.set_parent_to_alive(@id, new_block_id)
237
+ block_location_hash = Utils::BlockComponents.block_location_add(@id, @id, new_block_id, target, position_command)
238
+ last_edited_time_parent_hash = Utils::BlockComponents.last_edited_time(@id)
239
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(@id)
240
+ title_hash = Utils::BlockComponents.title(new_block_id, block_title)
241
+
242
+ operations = [
243
+ create_hash,
244
+ set_parent_alive_hash,
245
+ block_location_hash,
246
+ last_edited_time_parent_hash,
247
+ last_edited_time_child_hash,
248
+ title_hash
249
+ ]
250
+
251
+ request_url = URLS[:UPDATE_BLOCK]
252
+ request_body = build_payload(operations, request_ids)
253
+ response = HTTParty.post(
254
+ request_url,
255
+ body: request_body.to_json,
256
+ cookies: cookies,
257
+ headers: headers
258
+ )
259
+ unless response.code == 200; raise "There was an issue completing your request. Here is the response from Notion: #{response.body}, and here is the payload that was sent: #{operations}.
260
+ Please try again, and if issues persist open an issue in GitHub."; end
261
+
262
+ block_type.new(new_block_id, block_title, @id)
263
+ end
264
+
265
+ private
266
+
267
+ def get(url_or_id)
268
+ # ! retrieve a Notion Block and return its instantiated class object.
269
+ # ! url_or_id -> the block ID or URL : ``str``
270
+ clean_id = extract_id(url_or_id)
271
+
272
+ request_body = {
273
+ pageId: clean_id,
274
+ chunkNumber: 0,
275
+ limit: 100,
276
+ verticalColumns: false
277
+ }
278
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
279
+ i = 0
280
+ while jsonified_record_response.empty? || jsonified_record_response['block'].empty?
281
+ return {} if i >= 10
282
+
283
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
284
+ i += 1
285
+ end
286
+ block_type = extract_type(clean_id, jsonified_record_response)
287
+ block_parent_id = extract_parent_id(clean_id, jsonified_record_response)
288
+
289
+ if block_type.nil?
290
+ {}
291
+ else
292
+ block_class = NotionAPI.const_get(BLOCK_TYPES[block_type].to_s)
293
+ if block_class == NotionAPI::CollectionView
294
+ block_collection_id = extract_collection_id(clean_id, jsonified_record_response)
295
+ block_view_id = extract_view_ids(clean_id, jsonified_record_response)
296
+ collection_title = extract_collection_title(clean_id, block_collection_id, jsonified_record_response)
297
+ block_class.new(clean_id, collection_title, block_parent_id, block_collection_id, block_view_id.join)
298
+ else
299
+ block_title = extract_title(clean_id, jsonified_record_response)
300
+ block_class.new(clean_id, block_title, block_parent_id)
301
+ end
302
+ end
303
+ end
304
+
305
+ def update_title(new_title, request_id, transaction_id, space_id)
306
+ # ! Helper method for sending POST request to change title of block.
307
+ # ! new_title -> new title for the block : ``str``
308
+ # ! request_id -> the unique ID for the request key. Generated using SecureRandom : ``str``
309
+ # ! transaction_id -> the unique ID for the transaction key. Generated using SecureRandom: ``str``
310
+ # ! transaction_id -> the unique ID for the space key. Generated using SecureRandom: ``str``
311
+ # setup cookies, headers, and grab/create static vars for request
312
+ cookies = Core.options['cookies']
313
+ headers = Core.options['headers']
314
+ request_url = URLS[:UPDATE_BLOCK]
315
+
316
+ # set unique IDs for request
317
+ request_ids = {
318
+ request_id: request_id,
319
+ transaction_id: transaction_id,
320
+ space_id: space_id
321
+ }
322
+
323
+ # build and set operations to send to Notion
324
+ title_hash = Utils::BlockComponents.title(@id, new_title)
325
+ last_edited_time_parent_hash = Utils::BlockComponents.last_edited_time(@parent_id)
326
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(@id)
327
+ operations = [
328
+ title_hash,
329
+ last_edited_time_parent_hash,
330
+ last_edited_time_child_hash
331
+ ]
332
+
333
+ request_body = build_payload(operations, request_ids) # defined in utils.rb
334
+
335
+ response = HTTParty.post(
336
+ request_url,
337
+ body: request_body.to_json,
338
+ cookies: cookies,
339
+ headers: headers
340
+ )
341
+ response.body
342
+ end
343
+ end
344
+
345
+ # divider block: ---------
346
+ class DividerBlock < BlockTemplate
347
+ @notion_type = 'divider'
348
+ @type = 'divider'
349
+
350
+ def type
351
+ NotionAPI::DividerBlock.notion_type
352
+ end
353
+
354
+ class << self
355
+ attr_reader :notion_type, :type
356
+ end
357
+ end
358
+
359
+ # To-Do block: best for checklists and tracking to-dos.
360
+ class TodoBlock < BlockTemplate
361
+ @notion_type = 'to_do'
362
+ @type = 'to_do'
363
+
364
+ def type
365
+ NotionAPI::TodoBlock.notion_type
366
+ end
367
+
368
+ class << self
369
+ attr_reader :notion_type, :type
370
+ end
371
+
372
+ def checked=(checked_value)
373
+ # ! change the checked property of the Todo Block.
374
+ # ! checked_value -> boolean value used to determine whether the block should be checked [yes] or not [no] : ``str``
375
+ # set static variables for request
376
+ cookies = Core.options['cookies']
377
+ headers = Core.options['headers']
378
+ request_url = URLS[:UPDATE_BLOCK]
379
+
380
+ # set unique values for request
381
+ request_id = extract_id(SecureRandom.hex(16))
382
+ transaction_id = extract_id(SecureRandom.hex(16))
383
+ space_id = extract_id(SecureRandom.hex(16))
384
+ request_ids = {
385
+ request_id: request_id,
386
+ transaction_id: transaction_id,
387
+ space_id: space_id
388
+ }
389
+
390
+ if %w[yes no].include?(checked_value.downcase)
391
+ checked_hash = Utils::BlockComponents.checked_todo(@id, checked_value.downcase)
392
+ last_edited_time_parent_hash = Utils::BlockComponents.last_edited_time(@parent_id)
393
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(@id)
394
+
395
+ operations = [
396
+ checked_hash,
397
+ last_edited_time_parent_hash,
398
+ last_edited_time_child_hash
399
+ ]
400
+ request_body = build_payload(operations, request_ids)
401
+ response = HTTParty.post(
402
+ request_url,
403
+ body: request_body.to_json,
404
+ cookies: cookies,
405
+ headers: headers
406
+ )
407
+ unless response.code == 200; raise "There was an issue completing your request. Here is the response from Notion: #{response.body}, and here is the payload that was sent: #{operations}.
408
+ Please try again, and if issues persist open an issue in GitHub."; end
409
+
410
+ true
411
+ else
412
+ false
413
+ end
414
+ end
415
+ end
416
+
417
+ # Code block: used to store code, should be assigned a coding language.
418
+ class CodeBlock < BlockTemplate
419
+ @notion_type = 'code'
420
+ @type = 'code'
421
+
422
+ def type
423
+ NotionAPI::CodeBlock.notion_type
424
+ end
425
+
426
+ class << self
427
+ attr_reader :notion_type, :type
428
+ end
429
+ end
430
+
431
+ # Header block: H1
432
+ class HeaderBlock < BlockTemplate
433
+ @notion_type = 'header'
434
+ @type = 'header'
435
+
436
+ def type
437
+ NotionAPI::HeaderBlock.notion_type
438
+ end
439
+
440
+ class << self
441
+ attr_reader :notion_type, :type
442
+ end
443
+ end
444
+
445
+ # SubHeader Block: H2
446
+ class SubHeaderBlock < BlockTemplate
447
+ @notion_type = 'sub_header'
448
+ @type = 'sub_header'
449
+
450
+ def type
451
+ NotionAPI::SubHeaderBlock.notion_type
452
+ end
453
+
454
+ class << self
455
+ attr_reader :notion_type, :type
456
+ end
457
+ end
458
+
459
+ # Sub-Sub Header Block: H3
460
+ class SubSubHeaderBlock < BlockTemplate
461
+ @notion_type = 'sub_sub_header'
462
+ @type = 'sub_sub_header'
463
+
464
+ def type
465
+ NotionAPI::SubSubHeaderBlock.notion_type
466
+ end
467
+
468
+ class << self
469
+ attr_reader :notion_type, :type
470
+ end
471
+ end
472
+
473
+ # Page Block, entrypoint for the application
474
+ class PageBlock < BlockTemplate
475
+ @notion_type = 'page'
476
+ @type = 'page'
477
+
478
+ def type
479
+ NotionAPI::PageBlock.notion_type
480
+ end
481
+
482
+ class << self
483
+ attr_reader :notion_type, :type
484
+ end
485
+
486
+ def get_block(url_or_id)
487
+ # ! retrieve a Notion Block and return its instantiated class object.
488
+ # ! url_or_id -> the block ID or URL : ``str``
489
+ get(url_or_id)
490
+ end
491
+
492
+ def get_collection(url_or_id)
493
+ # ! retrieve a Notion Collection and return its instantiated class object.
494
+ # ! url_or_id -> the block ID or URL : ``str``
495
+ clean_id = extract_id(url_or_id)
496
+
497
+ request_body = {
498
+ pageId: clean_id,
499
+ chunkNumber: 0,
500
+ limit: 100,
501
+ verticalColumns: false
502
+ }
503
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
504
+ i = 0
505
+ while jsonified_record_response.empty? || jsonified_record_response['block'].empty?
506
+ return {} if i >= 10
507
+
508
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
509
+ i += 1
510
+ end
511
+ block_parent_id = extract_parent_id(clean_id, jsonified_record_response)
512
+ block_collection_id = extract_collection_id(clean_id, jsonified_record_response)
513
+ block_view_id = extract_view_ids(clean_id, jsonified_record_response).join
514
+ block_title = extract_collection_title(clean_id, block_collection_id, jsonified_record_response)
515
+
516
+ CollectionView.new(clean_id, block_title, block_parent_id, block_collection_id, block_view_id)
517
+ end
518
+
519
+ def create_collection(_collection_type, collection_title, data)
520
+ # ! create a Notion Collection View and return its instantiated class object.
521
+ # ! _collection_type -> the type of collection to create : ``str``
522
+ # ! collection_title -> the title of the collection view : ``str``
523
+ # ! data -> JSON data to add to the table : ``str``
524
+
525
+ unless %w[table].include?(_collection_type) ; raise ArgumentError, "That collection type is not yet supported. Try: \"table\"."; end
526
+ cookies = Core.options['cookies']
527
+ headers = Core.options['headers']
528
+
529
+ new_block_id = extract_id(SecureRandom.hex(16))
530
+ parent_id = extract_id(SecureRandom.hex(16))
531
+ collection_id = extract_id(SecureRandom.hex(16))
532
+ view_id = extract_id(SecureRandom.hex(16))
533
+
534
+ children = []
535
+ alive_blocks = []
536
+ data.each do |_row|
537
+ child = extract_id(SecureRandom.hex(16))
538
+ children.push(child)
539
+ alive_blocks.push(Utils::CollectionViewComponents.set_collection_blocks_alive(child, collection_id))
540
+ end
541
+
542
+ request_id = extract_id(SecureRandom.hex(16))
543
+ transaction_id = extract_id(SecureRandom.hex(16))
544
+ space_id = extract_id(SecureRandom.hex(16))
545
+
546
+ request_ids = {
547
+ request_id: request_id,
548
+ transaction_id: transaction_id,
549
+ space_id: space_id
550
+ }
551
+
552
+ create_collection_view = Utils::CollectionViewComponents.create_collection_view(new_block_id, collection_id, view_id)
553
+ configure_view = Utils::CollectionViewComponents.set_view_config(new_block_id, view_id, children)
554
+ configure_columns = Utils::CollectionViewComponents.set_collection_columns(collection_id, new_block_id, data)
555
+ set_parent_alive_hash = Utils::BlockComponents.set_parent_to_alive(@id, new_block_id)
556
+ add_block_hash = Utils::BlockComponents.block_location_add(@id, @id, new_block_id, nil, 'listAfter')
557
+ new_block_edited_time = Utils::BlockComponents.last_edited_time(new_block_id)
558
+ collection_title_hash = Utils::CollectionViewComponents.set_collection_title(collection_title, collection_id)
559
+
560
+ operations = [
561
+ create_collection_view,
562
+ configure_view,
563
+ configure_columns,
564
+ set_parent_alive_hash,
565
+ add_block_hash,
566
+ new_block_edited_time,
567
+ collection_title_hash
568
+ ]
569
+ operations << alive_blocks
570
+ all_ops = operations.flatten
571
+ data.each_with_index do |row, i|
572
+ child = children[i]
573
+ row.keys.each_with_index do |col_name, j|
574
+ child_component = Utils::CollectionViewComponents.insert_data(child, j.zero? ? 'title' : col_name, row[col_name])
575
+ all_ops.push(child_component)
576
+ end
577
+ end
578
+
579
+ request_url = URLS[:UPDATE_BLOCK]
580
+ request_body = build_payload(all_ops, request_ids)
581
+ response = HTTParty.post(
582
+ request_url,
583
+ body: request_body.to_json,
584
+ cookies: cookies,
585
+ headers: headers
586
+ )
587
+
588
+ unless response.code == 200; raise "There was an issue completing your request. Here is the response from Notion: #{response.body}, and here is the payload that was sent: #{operations}.
589
+ Please try again, and if issues persist open an issue in GitHub."; end
590
+
591
+ CollectionView.new(new_block_id, collection_title, parent_id, collection_id, view_id)
592
+ end
593
+ end
594
+
595
+ # Toggle block: best for storing children blocks
596
+ class ToggleBlock < BlockTemplate
597
+ @notion_type = 'toggle'
598
+ @type = 'toggle'
599
+
600
+ def type
601
+ NotionAPI::ToggleBlock.notion_type
602
+ end
603
+
604
+ class << self
605
+ attr_reader :notion_type, :type
606
+ end
607
+ end
608
+
609
+ # Bullet list block: best for an unordered list
610
+ class BulletedBlock < BlockTemplate
611
+ @notion_type = 'bulleted_list'
612
+ @type = 'bulleted_list'
613
+
614
+ def type
615
+ NotionAPI::BulletedBlock.notion_type
616
+ end
617
+
618
+ class << self
619
+ attr_reader :notion_type, :type
620
+ end
621
+ end
622
+
623
+ # Numbered list Block: best for an ordered list
624
+ class NumberedBlock < BlockTemplate
625
+ @notion_type = 'numbered_list'
626
+ @type = 'numbered_list'
627
+
628
+ def type
629
+ NotionAPI::NumberedBlock.notion_type
630
+ end
631
+
632
+ class << self
633
+ attr_reader :notion_type, :type
634
+ end
635
+ end
636
+
637
+ # best for memorable information
638
+ class QuoteBlock < BlockTemplate
639
+ @notion_type = 'quote'
640
+ @type = 'quote'
641
+
642
+ def type
643
+ NotionAPI::QuoteBlock.notion_type
644
+ end
645
+
646
+ class << self
647
+ attr_reader :notion_type, :type
648
+ end
649
+ end
650
+
651
+ # same as quote... works similarly to page block
652
+ class CalloutBlock < BlockTemplate
653
+ @notion_type = 'callout'
654
+ @type = 'callout'
655
+
656
+ def type
657
+ NotionAPI::CalloutBlock.notion_type
658
+ end
659
+
660
+ class << self
661
+ attr_reader :notion_type, :type
662
+ end
663
+ end
664
+
665
+ # simiilar to code block but for mathematical functions.
666
+ class LatexBlock < BlockTemplate
667
+ @notion_type = 'equation'
668
+ @type = 'equation'
669
+
670
+ def type
671
+ NotionAPI::LatexBlock.notion_type
672
+ end
673
+
674
+ class << self
675
+ attr_reader :notion_type, :type
676
+ end
677
+ end
678
+
679
+ # good for just about anything (-:
680
+ class TextBlock < BlockTemplate
681
+ @notion_type = 'text'
682
+ @type = 'text'
683
+
684
+ def type
685
+ NotionAPI::TextBlock.notion_type
686
+ end
687
+
688
+ class << self
689
+ attr_reader :notion_type, :type
690
+ end
691
+ end
692
+
693
+ # good for visual information
694
+ class ImageBlock < BlockTemplate
695
+ @notion_type = 'image'
696
+ @type = 'image'
697
+
698
+ def type
699
+ NotionAPI::ImageBlock.notion_type
700
+ end
701
+
702
+ class << self
703
+ attr_reader :notion_type, :type
704
+ end
705
+ end
706
+
707
+ # maps out the headers - sub-headers - sub-sub-headers on the page
708
+ class TableOfContentsBlock < BlockTemplate
709
+ @notion_type = 'table_of_contents'
710
+ @type = 'table_of_contents'
711
+
712
+ def type
713
+ NotionAPI::TableOfContentsBlock.notion_type
714
+ end
715
+
716
+ class << self
717
+ attr_reader :notion_type, :type
718
+ end
719
+ end
720
+
721
+ # no use case for this yet.
722
+ class ColumnListBlock < BlockTemplate
723
+ @notion_type = 'column_list'
724
+ @type = 'column_list'
725
+
726
+ def type
727
+ NotionAPI::ColumnListBlock.notion_type
728
+ end
729
+
730
+ class << self
731
+ attr_reader :notion_type, :type
732
+ end
733
+ end
734
+
735
+ # no use case for this yet.
736
+ class ColumnBlock < BlockTemplate
737
+ @notion_type = 'column'
738
+ @type = 'column'
739
+
740
+ def type
741
+ NotionAPI::ColumnBlock.notion_type
742
+ end
743
+
744
+ class << self
745
+ attr_reader :notion_type, :type
746
+ end
747
+ end
748
+ end
749
+
750
+ module NotionAPI
751
+ # collection views such as tables and timelines.
752
+ class CollectionView < Core
753
+ attr_reader :id, :title, :parent_id, :collection_id, :view_id
754
+
755
+ @notion_type = 'collection_view'
756
+ @type = 'collection_view'
757
+
758
+ def type
759
+ NotionAPI::CollectionView.notion_type
760
+ end
761
+
762
+ class << self
763
+ attr_reader :notion_type, :type
764
+ end
765
+
766
+ def initialize(id, title, parent_id, collection_id, view_id)
767
+ @id = id
768
+ @title = title
769
+ @parent_id = parent_id
770
+ @collection_id = collection_id
771
+ @view_id = view_id
772
+ end
773
+
774
+ def add_row(data)
775
+ # ! add new row to Collection View table.
776
+ # ! data -> data to add to table : ``hash``
777
+
778
+ cookies = Core.options['cookies']
779
+ headers = Core.options['headers']
780
+
781
+ request_id = extract_id(SecureRandom.hex(16))
782
+ transaction_id = extract_id(SecureRandom.hex(16))
783
+ space_id = extract_id(SecureRandom.hex(16))
784
+ new_block_id = extract_id(SecureRandom.hex(16))
785
+ schema = extract_collection_schema(@collection_id, @view_id)
786
+ keys = schema.keys
787
+ col_map = {}
788
+ keys.map { |key| col_map[schema[key]['name']] = key }
789
+
790
+ request_ids = {
791
+ request_id: request_id,
792
+ transaction_id: transaction_id,
793
+ space_id: space_id
794
+ }
795
+
796
+ instantiate_row = Utils::CollectionViewComponents.add_new_row(new_block_id)
797
+ set_block_alive = Utils::CollectionViewComponents.set_collection_blocks_alive(new_block_id, @collection_id)
798
+ new_block_edited_time = Utils::BlockComponents.last_edited_time(new_block_id)
799
+ parent_edited_time = Utils::BlockComponents.last_edited_time(@parent_id)
800
+
801
+ operations = [
802
+ instantiate_row,
803
+ set_block_alive,
804
+ new_block_edited_time,
805
+ parent_edited_time
806
+ ]
807
+
808
+ data.keys.each_with_index do |col_name, j|
809
+ child_component = Utils::CollectionViewComponents.insert_data(new_block_id, j.zero? ? 'title' : col_map[col_name], data[col_name])
810
+ operations.push(child_component)
811
+ end
812
+
813
+ request_url = URLS[:UPDATE_BLOCK]
814
+ request_body = build_payload(operations, request_ids)
815
+ response = HTTParty.post(
816
+ request_url,
817
+ body: request_body.to_json,
818
+ cookies: cookies,
819
+ headers: headers
820
+ )
821
+
822
+ unless response.code == 200; raise "There was an issue completing your request. Here is the response from Notion: #{response.body}, and here is the payload that was sent: #{operations}.
823
+ Please try again, and if issues persist open an issue in GitHub."; end
824
+
825
+ NotionAPI::CollectionViewRow.new(new_block_id, @parent_id, @collection_id, @view_id)
826
+ end
827
+
828
+ def add_property(name, type)
829
+ # ! add a property (column) to the table.
830
+ # ! name -> name of the property : ``str``
831
+ # ! type -> type of the property : ``str``
832
+ cookies = Core.options['cookies']
833
+ headers = Core.options['headers']
834
+
835
+ request_id = extract_id(SecureRandom.hex(16))
836
+ transaction_id = extract_id(SecureRandom.hex(16))
837
+ space_id = extract_id(SecureRandom.hex(16))
838
+
839
+ request_ids = {
840
+ request_id: request_id,
841
+ transaction_id: transaction_id,
842
+ space_id: space_id
843
+ }
844
+
845
+ # create updated schema
846
+ schema = extract_collection_schema(@collection_id, @view_id)
847
+ schema[name] = {
848
+ name: name,
849
+ type: type
850
+ }
851
+ new_schema = {
852
+ schema: schema
853
+ }
854
+
855
+ add_collection_property = Utils::CollectionViewComponents.add_collection_property(@collection_id, new_schema)
856
+
857
+ operations = [
858
+ add_collection_property
859
+ ]
860
+
861
+ request_url = URLS[:UPDATE_BLOCK]
862
+ request_body = build_payload(operations, request_ids)
863
+ response = HTTParty.post(
864
+ request_url,
865
+ body: request_body.to_json,
866
+ cookies: cookies,
867
+ headers: headers
868
+ )
869
+ unless response.code == 200; raise "There was an issue completing your request. Here is the response from Notion: #{response.body}, and here is the payload that was sent: #{operations}.
870
+ Please try again, and if issues persist open an issue in GitHub."; end
871
+
872
+ true
873
+ end
874
+
875
+ def row(row_id)
876
+ # ! retrieve a row from a CollectionView Table.
877
+ # ! row_id -> the ID for the row to retrieve: ``str``
878
+ clean_id = extract_id(row_id)
879
+
880
+ request_body = {
881
+ pageId: clean_id,
882
+ chunkNumber: 0,
883
+ limit: 100,
884
+ verticalColumns: false
885
+ }
886
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
887
+ schema = extract_collection_schema(@collection_id, @view_id)
888
+ keys = schema.keys
889
+ column_names = keys.map { |key| schema[key]['name'] }
890
+ i = 0
891
+ while jsonified_record_response.empty? || jsonified_record_response['block'].empty?
892
+ return {} if i >= 10
893
+
894
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
895
+ i += 1
896
+ end
897
+ row_jsonified_response = jsonified_record_response['block'][clean_id]['value']['properties']
898
+ row_data = {}
899
+ keys.each_with_index { |key, idx| row_data[column_names[idx]] = row_jsonified_response[key] ? row_jsonified_response[key].flatten : [] }
900
+ row_data
901
+ end
902
+
903
+ def row_ids
904
+ # ! retrieve all Collection View table rows.
905
+ clean_id = extract_id(@id)
906
+
907
+ request_body = {
908
+ pageId: clean_id,
909
+ chunkNumber: 0,
910
+ limit: 100,
911
+ verticalColumns: false
912
+ }
913
+
914
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
915
+ i = 0
916
+ while jsonified_record_response.empty? || jsonified_record_response['block'].empty?
917
+ return {} if i >= 10
918
+
919
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
920
+ i += 1
921
+ end
922
+
923
+ jsonified_record_response['collection_view'][@view_id]['value']['page_sort']
924
+ end
925
+
926
+ def rows
927
+ # ! returns all rows as instantiated class instances.
928
+ row_id_array = row_ids
929
+ parent_id = @parent_id
930
+ collection_id = @collection_id
931
+ view_id = @view_id
932
+
933
+ row_id_array.map { |row_id| NotionAPI::CollectionViewRow.new(row_id, parent_id, collection_id, view_id) }
934
+ end
935
+
936
+ private
937
+
938
+ def extract_collection_schema(collection_id, view_id)
939
+ # ! retrieve the collection scehma. Useful for 'building' the backbone for a table.
940
+ # ! collection_id -> the collection ID : ``str``
941
+ # ! view_id -> the view ID : ``str``
942
+ cookies = Core.options['cookies']
943
+ headers = Core.options['headers']
944
+
945
+ query_collection_hash = Utils::CollectionViewComponents.query_collection(collection_id, view_id, '')
946
+
947
+ request_url = URLS[:GET_COLLECTION]
948
+ response = HTTParty.post(
949
+ request_url,
950
+ body: query_collection_hash.to_json,
951
+ cookies: cookies,
952
+ headers: headers
953
+ )
954
+ response['recordMap']['collection'][collection_id]['value']['schema']
955
+ end
956
+ end
957
+ # Class for each row in a Collection View Table.
958
+ class CollectionViewRow < Core
959
+ @notion_type = 'table_row'
960
+ @type = 'table_row'
961
+
962
+ def type
963
+ NotionAPI::CollectionViewRow.notion_type
964
+ end
965
+
966
+ class << self
967
+ attr_reader :notion_type, :type, :parent_id
968
+ end
969
+
970
+ attr_reader :parent_id, :id
971
+ def initialize(id, parent_id, collection_id, view_id)
972
+ @id = id
973
+ @parent_id = parent_id
974
+ @collection_id = collection_id
975
+ @view_id = view_id
976
+ end
977
+ end
978
+ end
979
+
980
+ # gather a list of all the classes defined here...
981
+ Classes = NotionAPI.constants.select { |c| NotionAPI.const_get(c).is_a? Class and c.to_s != 'BlockTemplate' and c.to_s != 'Core' and c.to_s !='Client' }
982
+ notion_types = []
983
+ Classes.each { |cls| notion_types.push(NotionAPI.const_get(cls).notion_type) }
984
+ BLOCK_TYPES = notion_types.zip(Classes).to_h