notion 1.0.1 → 1.0.2

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,16 @@
1
+ module NotionAPI
2
+
3
+ # best for memorable information
4
+ class QuoteBlock < BlockTemplate
5
+ @notion_type = 'quote'
6
+ @type = 'quote'
7
+
8
+ def type
9
+ NotionAPI::QuoteBlock.notion_type
10
+ end
11
+
12
+ class << self
13
+ attr_reader :notion_type, :type
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module NotionAPI
2
+
3
+ # SubHeader Block: H2
4
+ class SubHeaderBlock < BlockTemplate
5
+ @notion_type = 'sub_header'
6
+ @type = 'sub_header'
7
+
8
+ def type
9
+ NotionAPI::SubHeaderBlock.notion_type
10
+ end
11
+
12
+ class << self
13
+ attr_reader :notion_type, :type
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module NotionAPI
2
+
3
+ # Sub-Sub Header Block: H3
4
+ class SubSubHeaderBlock < BlockTemplate
5
+ @notion_type = 'sub_sub_header'
6
+ @type = 'sub_sub_header'
7
+
8
+ def type
9
+ NotionAPI::SubSubHeaderBlock.notion_type
10
+ end
11
+
12
+ class << self
13
+ attr_reader :notion_type, :type
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ module NotionAPI
2
+ # maps out the headers - sub-headers - sub-sub-headers on the page
3
+ class TableOfContentsBlock < BlockTemplate
4
+ @notion_type = 'table_of_contents'
5
+ @type = 'table_of_contents'
6
+
7
+ def type
8
+ NotionAPI::TableOfContentsBlock.notion_type
9
+ end
10
+
11
+ class << self
12
+ attr_reader :notion_type, :type
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,352 @@
1
+ require_relative '../core'
2
+ require 'httparty'
3
+
4
+ module NotionAPI
5
+ # Base Template for all blocks. Inherits core methods from the Block class defined in block.rb
6
+ class BlockTemplate < Core
7
+ include Utils
8
+
9
+ attr_reader :id, :title, :parent_id
10
+
11
+ def initialize(id, title, parent_id)
12
+ @id = id
13
+ @title = title
14
+ @parent_id = parent_id
15
+ end
16
+
17
+ def title=(new_title)
18
+ # ! Change the title of a block.
19
+ # ! new_title -> new title for the block : ``str``
20
+ request_id = extract_id(SecureRandom.hex(16))
21
+ transaction_id = extract_id(SecureRandom.hex(16))
22
+ space_id = extract_id(SecureRandom.hex(16))
23
+ update_title(new_title.to_s, request_id, transaction_id, space_id)
24
+ @title = new_title
25
+ end
26
+
27
+ def convert(block_class_to_convert_to)
28
+ # ! convert a block from its current type to another.
29
+ # ! block_class_to_convert_to -> the type of block to convert to : ``cls``
30
+ if type == block_class_to_convert_to.notion_type
31
+ # if converting to same type, skip and return self
32
+ self
33
+ else
34
+ # setup cookies, headers, and grab/create static vars for request
35
+ cookies = Core.options['cookies']
36
+ headers = Core.options['headers']
37
+ request_url = URLS[:UPDATE_BLOCK]
38
+
39
+ # set random IDs for request
40
+ request_id = extract_id(SecureRandom.hex(16))
41
+ transaction_id = extract_id(SecureRandom.hex(16))
42
+ space_id = extract_id(SecureRandom.hex(16))
43
+ request_ids = {
44
+ request_id: request_id,
45
+ transaction_id: transaction_id,
46
+ space_id: space_id
47
+ }
48
+
49
+ # build hash's that contain the operations to send to Notions backend
50
+ convert_type_hash = Utils::BlockComponents.convert_type(@id, block_class_to_convert_to)
51
+ last_edited_time_parent_hash = Utils::BlockComponents.last_edited_time(@parent_id)
52
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(@id)
53
+
54
+ operations = [
55
+ convert_type_hash,
56
+ last_edited_time_parent_hash,
57
+ last_edited_time_child_hash
58
+ ]
59
+
60
+ request_body = build_payload(operations, request_ids)
61
+ response = HTTParty.post(
62
+ request_url,
63
+ body: request_body.to_json,
64
+ cookies: cookies,
65
+ headers: headers
66
+ )
67
+ 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}.
68
+ Please try again, and if issues persist open an issue in GitHub."; end
69
+
70
+ block_class_to_convert_to.new(@id, @title, @parent_id)
71
+
72
+ end
73
+ end
74
+
75
+ def duplicate(target_block = nil)
76
+ # ! duplicate the block that this method is invoked upon.
77
+ # ! target_block -> the block to place the duplicated block after. Can be any valid Block ID! : ``str``
78
+ cookies = Core.options['cookies']
79
+ headers = Core.options['headers']
80
+ request_url = URLS[:UPDATE_BLOCK]
81
+
82
+ new_block_id = extract_id(SecureRandom.hex(16))
83
+ request_id = extract_id(SecureRandom.hex(16))
84
+ transaction_id = extract_id(SecureRandom.hex(16))
85
+ space_id = extract_id(SecureRandom.hex(16))
86
+
87
+ root_children = children_ids(@id)
88
+ sub_children = []
89
+ root_children.each { |root_id| sub_children.push(children_ids(root_id)) }
90
+
91
+ request_ids = {
92
+ request_id: request_id,
93
+ transaction_id: transaction_id,
94
+ space_id: space_id
95
+ }
96
+ body = {
97
+ pageId: @id,
98
+ chunkNumber: 0,
99
+ limit: 100,
100
+ verticalColumns: false
101
+ }
102
+
103
+ user_notion_id = get_notion_id(body)
104
+
105
+ block = target_block ? get(target_block) : self # allows dev to place block anywhere!
106
+
107
+ props_and_formatting = get_block_props_and_format(@id, @title)
108
+ props = props_and_formatting[:properties]
109
+ formats = props_and_formatting[:format]
110
+ duplicate_hash = Utils::BlockComponents.duplicate(type, @title, block.id, new_block_id, user_notion_id, root_children, props, formats)
111
+ set_parent_alive_hash = Utils::BlockComponents.set_parent_to_alive(block.parent_id, new_block_id)
112
+ block_location_hash = Utils::BlockComponents.block_location_add(block.parent_id, block.id, new_block_id, target_block, 'listAfter')
113
+ last_edited_time_parent_hash = Utils::BlockComponents.last_edited_time(block.parent_id)
114
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(block.id)
115
+
116
+ operations = [
117
+ duplicate_hash,
118
+ set_parent_alive_hash,
119
+ block_location_hash,
120
+ last_edited_time_parent_hash,
121
+ last_edited_time_child_hash
122
+ ]
123
+
124
+ request_body = build_payload(operations, request_ids)
125
+ response = HTTParty.post(
126
+ request_url,
127
+ body: request_body.to_json,
128
+ cookies: cookies,
129
+ headers: headers
130
+ )
131
+ 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}.
132
+ Please try again, and if issues persist open an issue in GitHub."; end
133
+
134
+ class_to_return = NotionAPI.const_get(Classes.select { |cls| NotionAPI.const_get(cls).notion_type == type }.join.to_s)
135
+ class_to_return.new(new_block_id, @title, block.parent_id)
136
+ end
137
+
138
+ def move(target_block, position = 'after')
139
+ # ! move the block to a new location.
140
+ # ! target_block -> the targetted block to move to. : ``str``
141
+ # ! position -> where the block should be listed, in positions relative to the target_block [before, after, top-child, last-child]
142
+ positions_hash = {
143
+ 'after' => 'listAfter',
144
+ 'before' => 'listBefore'
145
+ }
146
+
147
+ unless positions_hash.keys.include?(position); raise ArgumentError, "Invalid position. You said: #{position}, valid options are: #{positions_hash.keys.join(', ')}"; end
148
+
149
+ position_command = positions_hash[position]
150
+ cookies = Core.options['cookies']
151
+ headers = Core.options['headers']
152
+ request_url = URLS[:UPDATE_BLOCK]
153
+
154
+ request_id = extract_id(SecureRandom.hex(16))
155
+ transaction_id = extract_id(SecureRandom.hex(16))
156
+ space_id = extract_id(SecureRandom.hex(16))
157
+
158
+ request_ids = {
159
+ request_id: request_id,
160
+ transaction_id: transaction_id,
161
+ space_id: space_id
162
+ }
163
+
164
+ check_parents = (@parent_id == target_block.parent_id)
165
+ set_block_dead_hash = Utils::BlockComponents.set_block_to_dead(@id) # kill the block this method is invoked on...
166
+ block_location_remove_hash = Utils::BlockComponents.block_location_remove(@parent_id, @id) # remove the block this method is invoked on...
167
+ parent_location_hash = Utils::BlockComponents.parent_location_add(check_parents ? @parent_id : target_block.parent_id, @id) # set parent location to alive
168
+ block_location_add_hash = Utils::BlockComponents.block_location_add(check_parents ? @parent_id : target_block.parent_id, @id, target_block.id, position_command)
169
+ last_edited_time_parent_hash = Utils::BlockComponents.last_edited_time(@parent_id)
170
+
171
+ if check_parents
172
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(@id)
173
+ operations = [
174
+ set_block_dead_hash,
175
+ block_location_remove_hash,
176
+ parent_location_hash,
177
+ block_location_add_hash,
178
+ last_edited_time_parent_hash,
179
+ last_edited_time_child_hash
180
+ ]
181
+ else
182
+ last_edited_time_new_parent_hash = Utils::BlockComponents.last_edited_time(target_block.parent_id)
183
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(@id)
184
+ @parent_id = target_block.parent_id
185
+ operations = [
186
+ set_block_dead_hash,
187
+ block_location_remove_hash,
188
+ parent_location_hash,
189
+ block_location_add_hash,
190
+ last_edited_time_parent_hash,
191
+ last_edited_time_new_parent_hash,
192
+ last_edited_time_child_hash
193
+ ]
194
+ end
195
+ request_body = build_payload(operations, request_ids)
196
+ response = HTTParty.post(
197
+ request_url,
198
+ body: request_body.to_json,
199
+ cookies: cookies,
200
+ headers: headers
201
+ )
202
+ 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}.
203
+ Please try again, and if issues persist open an issue in GitHub."; end
204
+
205
+ self
206
+ end
207
+
208
+ def create(block_type, block_title, target = nil, position = 'after')
209
+ # ! create a new block
210
+ # ! block_type -> the type of block to create : ``cls``
211
+ # ! block_title -> the title of the new block : ``str``
212
+ # ! target -> the block_id that the new block should be placed after. ``str``
213
+ # ! position -> 'after' or 'before'
214
+ positions_hash = {
215
+ 'after' => 'listAfter',
216
+ 'before' => 'listBefore'
217
+ }
218
+ unless positions_hash.keys.include?(position); raise "Invalid position. You said: #{position}, valid options are: #{positions_hash.keys.join(', ')}"; end
219
+
220
+ position_command = positions_hash[position]
221
+ blocks_with_emojis = [NotionAPI::PageBlock, NotionAPI::CalloutBlock]
222
+
223
+ cookies = Core.options['cookies']
224
+ headers = Core.options['headers']
225
+
226
+ new_block_id = extract_id(SecureRandom.hex(16))
227
+ request_id = extract_id(SecureRandom.hex(16))
228
+ transaction_id = extract_id(SecureRandom.hex(16))
229
+ space_id = extract_id(SecureRandom.hex(16))
230
+
231
+ request_ids = {
232
+ request_id: request_id,
233
+ transaction_id: transaction_id,
234
+ space_id: space_id
235
+ }
236
+
237
+ create_hash = Utils::BlockComponents.create(new_block_id, block_type.notion_type)
238
+ set_parent_alive_hash = Utils::BlockComponents.set_parent_to_alive(@id, new_block_id)
239
+ block_location_hash = Utils::BlockComponents.block_location_add(@id, @id, new_block_id, target, position_command)
240
+ last_edited_time_parent_hash = Utils::BlockComponents.last_edited_time(@id)
241
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(@id)
242
+ title_hash = Utils::BlockComponents.title(new_block_id, block_title)
243
+
244
+ operations = [
245
+ create_hash,
246
+ set_parent_alive_hash,
247
+ block_location_hash,
248
+ last_edited_time_parent_hash,
249
+ last_edited_time_child_hash,
250
+ title_hash
251
+ ]
252
+
253
+ if blocks_with_emojis.include?(block_type)
254
+ emoji_choices = ["😀", "😃", "😄", "😁", "😆", "😅", "🤣", "😂", "🙂", "🙃", "😉", "😊", "😇", "🥰", "😍", "😀", "😃"]
255
+ emoji = emoji_choices[rand(0...emoji_choices.length)]
256
+ emoji_icon_hash = Utils::BlockComponents.add_emoji_icon(new_block_id, emoji)
257
+ operations.push(emoji_icon_hash)
258
+ end
259
+
260
+
261
+ request_url = URLS[:UPDATE_BLOCK]
262
+ request_body = build_payload(operations, request_ids)
263
+ response = HTTParty.post(
264
+ request_url,
265
+ body: request_body.to_json,
266
+ cookies: cookies,
267
+ headers: headers
268
+ )
269
+ 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}.
270
+ Please try again, and if issues persist open an issue in GitHub."; end
271
+
272
+ block_type.new(new_block_id, block_title, @id)
273
+ end
274
+
275
+ private
276
+
277
+ def get(url_or_id)
278
+ # ! retrieve a Notion Block and return its instantiated class object.
279
+ # ! url_or_id -> the block ID or URL : ``str``
280
+ clean_id = extract_id(url_or_id)
281
+
282
+ request_body = {
283
+ pageId: clean_id,
284
+ chunkNumber: 0,
285
+ limit: 100,
286
+ verticalColumns: false
287
+ }
288
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
289
+ i = 0
290
+ while jsonified_record_response.empty? || jsonified_record_response['block'].empty?
291
+ return {} if i >= 10
292
+
293
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
294
+ i += 1
295
+ end
296
+ block_type = extract_type(clean_id, jsonified_record_response)
297
+ block_parent_id = extract_parent_id(clean_id, jsonified_record_response)
298
+
299
+ if block_type.nil?
300
+ {}
301
+ else
302
+ block_class = NotionAPI.const_get(BLOCK_TYPES[block_type].to_s)
303
+ if block_class == NotionAPI::CollectionView
304
+ block_collection_id = extract_collection_id(clean_id, jsonified_record_response)
305
+ block_view_id = extract_view_ids(clean_id, jsonified_record_response)
306
+ collection_title = extract_collection_title(clean_id, block_collection_id, jsonified_record_response)
307
+ block_class.new(clean_id, collection_title, block_parent_id, block_collection_id, block_view_id.join)
308
+ else
309
+ block_title = extract_title(clean_id, jsonified_record_response)
310
+ block_class.new(clean_id, block_title, block_parent_id)
311
+ end
312
+ end
313
+ end
314
+
315
+ def update_title(new_title, request_id, transaction_id, space_id)
316
+ # ! Helper method for sending POST request to change title of block.
317
+ # ! new_title -> new title for the block : ``str``
318
+ # ! request_id -> the unique ID for the request key. Generated using SecureRandom : ``str``
319
+ # ! transaction_id -> the unique ID for the transaction key. Generated using SecureRandom: ``str``
320
+ # ! transaction_id -> the unique ID for the space key. Generated using SecureRandom: ``str``
321
+ # setup cookies, headers, and grab/create static vars for request
322
+ cookies = Core.options['cookies']
323
+ headers = Core.options['headers']
324
+ request_url = URLS[:UPDATE_BLOCK]
325
+
326
+ # set unique IDs for request
327
+ request_ids = {
328
+ request_id: request_id,
329
+ transaction_id: transaction_id,
330
+ space_id: space_id
331
+ }
332
+
333
+ # build and set operations to send to Notion
334
+ title_hash = Utils::BlockComponents.title(@id, new_title)
335
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(@id)
336
+ operations = [
337
+ title_hash,
338
+ last_edited_time_child_hash
339
+ ]
340
+
341
+ request_body = build_payload(operations, request_ids) # defined in utils.rb
342
+
343
+ response = HTTParty.post(
344
+ request_url,
345
+ body: request_body.to_json,
346
+ cookies: cookies,
347
+ headers: headers
348
+ )
349
+ response.body
350
+ end
351
+ end
352
+ end
@@ -0,0 +1,16 @@
1
+ module NotionAPI
2
+
3
+ # good for just about anything (-:
4
+ class TextBlock < BlockTemplate
5
+ @notion_type = 'text'
6
+ @type = 'text'
7
+
8
+ def type
9
+ NotionAPI::TextBlock.notion_type
10
+ end
11
+
12
+ class << self
13
+ attr_reader :notion_type, :type
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,60 @@
1
+ module NotionAPI
2
+
3
+ # To-Do block: best for checklists and tracking to-dos.
4
+ class TodoBlock < BlockTemplate
5
+ @notion_type = 'to_do'
6
+ @type = 'to_do'
7
+
8
+ def type
9
+ NotionAPI::TodoBlock.notion_type
10
+ end
11
+
12
+ class << self
13
+ attr_reader :notion_type, :type
14
+ end
15
+
16
+ def checked=(checked_value)
17
+ # ! change the checked property of the Todo Block.
18
+ # ! checked_value -> boolean value used to determine whether the block should be checked [yes] or not [no] : ``str``
19
+ # set static variables for request
20
+ cookies = Core.options['cookies']
21
+ headers = Core.options['headers']
22
+ request_url = URLS[:UPDATE_BLOCK]
23
+
24
+ # set unique values for request
25
+ request_id = extract_id(SecureRandom.hex(16))
26
+ transaction_id = extract_id(SecureRandom.hex(16))
27
+ space_id = extract_id(SecureRandom.hex(16))
28
+ request_ids = {
29
+ request_id: request_id,
30
+ transaction_id: transaction_id,
31
+ space_id: space_id
32
+ }
33
+
34
+ if %w[yes no].include?(checked_value.downcase)
35
+ checked_hash = Utils::BlockComponents.checked_todo(@id, checked_value.downcase)
36
+ last_edited_time_parent_hash = Utils::BlockComponents.last_edited_time(@parent_id)
37
+ last_edited_time_child_hash = Utils::BlockComponents.last_edited_time(@id)
38
+
39
+ operations = [
40
+ checked_hash,
41
+ last_edited_time_parent_hash,
42
+ last_edited_time_child_hash
43
+ ]
44
+ request_body = build_payload(operations, request_ids)
45
+ response = HTTParty.post(
46
+ request_url,
47
+ body: request_body.to_json,
48
+ cookies: cookies,
49
+ headers: headers
50
+ )
51
+ 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}.
52
+ Please try again, and if issues persist open an issue in GitHub."; end
53
+
54
+ true
55
+ else
56
+ false
57
+ end
58
+ end
59
+ end
60
+ end