notion 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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