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,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'core'
4
+
5
+ module NotionAPI
6
+ # acts as the 'main interface' to the methods of this package.
7
+ class Client < Core
8
+ attr_reader :token_v2, :active_user_header
9
+
10
+ def initialize(token_v2, active_user_header = nil)
11
+ @token_v2 = token_v2
12
+ @active_user_header = active_user_header
13
+ super(token_v2, active_user_header)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'utils'
4
+ require 'httparty'
5
+
6
+ module NotionAPI
7
+ # the initial methods available to an instantiated Cloent object are defined
8
+ class Core
9
+ include Utils
10
+ @options = { 'cookies' => { :token_v2 => nil, 'x-active-user-header' => nil }, 'headers' => { 'Content-Type' => 'application/json' } }
11
+ @type_whitelist = 'divider'
12
+
13
+ class << self
14
+ attr_reader :options, :type_whitelist, :token_v2, :active_user_header
15
+ end
16
+
17
+ attr_reader :clean_id, :cookies, :headers
18
+
19
+ def initialize(token_v2, active_user_header)
20
+ @@token_v2 = token_v2
21
+ @@active_user_header = active_user_header
22
+ end
23
+
24
+ def get_page(url_or_id)
25
+ # ! retrieve a Notion Page Block and return its instantiated class object.
26
+ # ! url_or_id -> the block ID or URL : ``str``
27
+ clean_id = extract_id(url_or_id)
28
+
29
+ request_body = {
30
+ pageId: clean_id,
31
+ chunkNumber: 0,
32
+ limit: 100,
33
+ verticalColumns: false
34
+ }
35
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
36
+ i = 0
37
+ while jsonified_record_response.empty? || jsonified_record_response['block'].empty?
38
+ return {} if i >= 10
39
+
40
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
41
+ i += 1
42
+ end
43
+
44
+ block_id = clean_id
45
+ block_title = extract_title(clean_id, jsonified_record_response)
46
+ block_type = extract_type(clean_id, jsonified_record_response)
47
+ block_parent_id = extract_parent_id(clean_id, jsonified_record_response)
48
+
49
+ raise 'the URL or ID passed to the get_page method must be that of a Page Block.' if block_type != 'page'
50
+
51
+ PageBlock.new(block_id, block_title, block_parent_id)
52
+ end
53
+
54
+ def children(url_or_id = @id)
55
+ # ! retrieve the children of a block. If the block has no children, return []. If it does, return the instantiated class objects associated with each child.
56
+ # ! url_or_id -> the block ID or URL : ``str``
57
+
58
+ children_ids = children_ids(url_or_id)
59
+ if children_ids.empty?
60
+ []
61
+ else
62
+ children_class_instances = []
63
+ children_ids.each { |child| children_class_instances.push(get(child)) }
64
+ children_class_instances
65
+ end
66
+ end
67
+
68
+ def children_ids(url_or_id = @id)
69
+ # ! retrieve the children IDs of a block.
70
+ # ! url_or_id -> the block ID or URL : ``str``
71
+ clean_id = extract_id(url_or_id)
72
+ request_body = {
73
+ pageId: clean_id,
74
+ chunkNumber: 0,
75
+ limit: 100,
76
+ verticalColumns: false
77
+ }
78
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
79
+ i = 0
80
+ while jsonified_record_response.empty?
81
+ return {} if i >= 10
82
+
83
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
84
+ i += 1
85
+ end
86
+
87
+ jsonified_record_response['block'][clean_id]['value']['content'] || []
88
+ end
89
+
90
+ private
91
+
92
+ def get_notion_id(body)
93
+ # ! retrieves a users ID from the headers of a Notion response object.
94
+ # ! body -> the body to send in the request : ``Hash``
95
+ Core.options['cookies'][:token_v2] = @@token_v2
96
+ Core.options['headers']['x-notion-active-user-header'] = @@active_user_header
97
+ cookies = Core.options['cookies']
98
+ headers = Core.options['headers']
99
+ request_url = URLS[:GET_BLOCK]
100
+
101
+ response = HTTParty.post(
102
+ request_url,
103
+ body: body.to_json,
104
+ cookies: cookies,
105
+ headers: headers
106
+ )
107
+ response.headers['x-notion-user-id']
108
+ end
109
+
110
+ def get_last_page_block_id(url_or_id)
111
+ # ! retrieve and return the last child ID of a block.
112
+ # ! url_or_id -> the block ID or URL : ``str``
113
+ children_ids(url_or_id).empty? ? [] : children_ids(url_or_id)[-1]
114
+ end
115
+
116
+ def get_all_block_info(_clean_id, body)
117
+ # ! retrieves all info pertaining to a block Id.
118
+ # ! clean_id -> the block ID or URL cleaned : ``str``
119
+ Core.options['cookies'][:token_v2] = @@token_v2
120
+ Core.options['headers']['x-notion-active-user-header'] = @active_user_header
121
+ cookies = Core.options['cookies']
122
+ headers = Core.options['headers']
123
+
124
+ request_url = URLS[:GET_BLOCK]
125
+
126
+ response = HTTParty.post(
127
+ request_url,
128
+ body: body.to_json,
129
+ cookies: cookies,
130
+ headers: headers
131
+ )
132
+
133
+ JSON.parse(response.body)['recordMap']
134
+ end
135
+
136
+ def filter_nil_blocks(jsonified_record_response)
137
+ # ! removes any blocks that are empty [i.e. have no title / content]
138
+ # ! jsonified_record_responses -> parsed JSON representation of a notion response object : ``Json``
139
+ jsonified_record_response.empty? || jsonified_record_response['block'].empty? ? nil : jsonified_record_response['block']
140
+ end
141
+
142
+ def extract_title(clean_id, jsonified_record_response)
143
+ # ! extract title from core JSON Notion response object.
144
+ # ! clean_id -> the cleaned block ID: ``str``
145
+ # ! jsonified_record_response -> parsed JSON representation of a notion response object : ``Json``
146
+ filter_nil_blocks = filter_nil_blocks(jsonified_record_response)
147
+ if filter_nil_blocks.nil? || filter_nil_blocks[clean_id].nil? || filter_nil_blocks[clean_id]['value']['properties'].nil?
148
+ nil
149
+ else
150
+ # titles for images are called source, while titles for text-based blocks are called title, so lets dynamically grab it
151
+ # https://stackoverflow.com/questions/23765996/get-all-keys-from-ruby-hash/23766007
152
+ title_value = filter_nil_blocks[clean_id]['value']['properties'].keys[0]
153
+ Core.type_whitelist.include?(filter_nil_blocks[clean_id]['value']['type']) ? nil : jsonified_record_response['block'][clean_id]['value']['properties'][title_value].flatten[0]
154
+
155
+ end
156
+ end
157
+
158
+ def extract_collection_title(_clean_id, collection_id, jsonified_record_response)
159
+ # ! extract title from core JSON Notion response object.
160
+ # ! clean_id -> the cleaned block ID: ``str``
161
+ # ! collection_id -> the collection ID: ``str``
162
+ # ! jsonified_record_response -> parsed JSON representation of a notion response object : ``Json``
163
+ jsonified_record_response['collection'][collection_id]['value']['name'].flatten.join if jsonified_record_response['collection']
164
+ end
165
+
166
+ def extract_type(clean_id, jsonified_record_response)
167
+ # ! extract type from core JSON response object.
168
+ # ! clean_id -> the block ID or URL cleaned : ``str``
169
+ # ! jsonified_record_response -> parsed JSON representation of a notion response object : ``Json``
170
+ filter_nil_blocks = filter_nil_blocks(jsonified_record_response)
171
+ if filter_nil_blocks.nil?
172
+ nil
173
+ else
174
+ filter_nil_blocks[clean_id]['value']['type']
175
+
176
+ end
177
+ end
178
+
179
+ def extract_parent_id(clean_id, jsonified_record_response)
180
+ # ! extract parent ID from core JSON response object.
181
+ # ! clean_id -> the block ID or URL cleaned : ``str``
182
+ # ! jsonified_record_response -> parsed JSON representation of a notion response object : ``Json``
183
+ jsonified_record_response.empty? || jsonified_record_response['block'].empty? ? {} : jsonified_record_response['block'][clean_id]['value']['parent_id']
184
+ end
185
+
186
+ def extract_collection_id(clean_id, jsonified_record_response)
187
+ # ! extract the collection ID
188
+ # ! clean_id -> the block ID or URL cleaned : ``str``
189
+ # ! jsonified_record_response -> parsed JSON representation of a notion response object : ``Json``
190
+ jsonified_record_response['block'][clean_id]['value']['collection_id']
191
+ end
192
+
193
+ def extract_view_ids(clean_id, jsonified_record_response)
194
+ jsonified_record_response['block'][clean_id]['value']['view_ids'] || []
195
+ end
196
+
197
+ def extract_id(url_or_id)
198
+ # ! parse and clean the URL or ID object provided.
199
+ # ! url_or_id -> the block ID or URL : ``str``
200
+ http_or_https = url_or_id.match(/^(http|https)/) # true if http or https in url_or_id...
201
+ if (url_or_id.length == 36) && ((url_or_id.split('-').length == 5) && !http_or_https)
202
+ # passes if url_or_id is perfectly formatted already...
203
+ url_or_id
204
+ elsif (http_or_https && (url_or_id.split('-').last.length == 32)) || (!http_or_https && (url_or_id.length == 32))
205
+ # passes if either:
206
+ # 1. a URL is passed as url_or_id and the ID at the end is 32 characters long or
207
+ # 2. a URL is not passed and the ID length is 32 [aka unformatted]
208
+ pattern = [8, 13, 18, 23]
209
+ id = url_or_id.split('-').last
210
+ pattern.each { |index| id.insert(index, '-') }
211
+ id
212
+ else
213
+ raise ArgumentError, 'Expected a Notion page URL or a page ID. Please consult the documentation for further information.'
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Notion
4
+ class Markdown
5
+ # ! convert user input formatted with markdown (**hi** is bold, *hi* is italic, etc.) -> notion-understood stylings.
6
+ end
7
+ end
@@ -0,0 +1,518 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Utils
4
+ # ! defines utility functions and static variables for this application.
5
+ URLS = {
6
+ GET_BLOCK: 'https://www.notion.so/api/v3/loadPageChunk',
7
+ UPDATE_BLOCK: 'https://www.notion.so/api/v3/saveTransactions',
8
+ GET_COLLECTION: 'https://www.notion.so/api/v3/queryCollection'
9
+ }.freeze
10
+
11
+ class BlockComponents
12
+ # ! Each function defined here builds one component that is included in each request sent to Notions backend.
13
+ # ! Each request sent will contain multiple components.
14
+ def self.create(block_id, block_type)
15
+ # ! payload for creating a block.
16
+ # ! block_id -> id of the new block : ``str``
17
+ # ! block_type -> type of block to create : ``cls``
18
+ table = 'block'
19
+ path = []
20
+ command = 'update'
21
+ timestamp = DateTime.now.strftime('%Q')
22
+ {
23
+ id: block_id,
24
+ table: table,
25
+ path: path,
26
+ command: command,
27
+ args: {
28
+ id: block_id,
29
+ type: block_type,
30
+ properties: {},
31
+ created_time: timestamp,
32
+ last_edited_time: timestamp
33
+ }
34
+ }
35
+ end
36
+
37
+ def self.title(id, title)
38
+ # ! payload for updating the title of a block
39
+ # ! id -> the ID to update the title of : ``str``
40
+ table = 'block'
41
+ path = %w[properties title]
42
+ command = 'set'
43
+
44
+ {
45
+ id: id,
46
+ table: table,
47
+ path: path,
48
+ command: command,
49
+ args: [[title]]
50
+ }
51
+ end
52
+
53
+ def self.last_edited_time(id)
54
+ # ! payload for updating the last edited time
55
+ # ! id -> either the block ID or parent ID : ``str``
56
+ timestamp = DateTime.now.strftime('%Q')
57
+ table = 'block'
58
+ path = ['last_edited_time']
59
+ command = 'set'
60
+
61
+ {
62
+ table: table,
63
+ id: id,
64
+ path: path,
65
+ command: command,
66
+ args: timestamp
67
+ }
68
+ end
69
+
70
+ def self.convert_type(id, block_class_to_convert_to)
71
+ # ! payload for converting a block to a different type.
72
+ # ! id -> id of the block to convert : ``str``
73
+ # ! block_class_to_convert_to -> type to convert to block to: ``NotionAPI::<Block_Type>``
74
+ table = 'block'
75
+ path = []
76
+ command = 'update'
77
+
78
+ {
79
+ id: id,
80
+ table: table,
81
+ path: path,
82
+ command: command,
83
+ args: {
84
+ type: block_class_to_convert_to.notion_type
85
+ }
86
+ }
87
+ end
88
+
89
+ def self.set_parent_to_alive(block_parent_id, new_block_id)
90
+ # ! payload for setting a blocks parent ID to 'alive'
91
+ # ! block_parent_id -> the blocks parent ID : ``str``
92
+ # ! new_block_id -> the new block ID, who is a child of the parent : ``str``
93
+ table = 'block'
94
+ path = []
95
+ command = 'update'
96
+ parent_table = 'block'
97
+ alive = true
98
+ {
99
+ id: new_block_id,
100
+ table: table,
101
+ path: path,
102
+ command: command,
103
+ args: {
104
+ parent_id: block_parent_id,
105
+ parent_table: parent_table,
106
+ alive: alive
107
+ }
108
+ }
109
+ end
110
+
111
+ def self.set_block_to_dead(block_id)
112
+ # ! payload for setting a block to dead (alive == true)
113
+ # ! block_id -> the block ID to 'kill' : ``str``
114
+ table = 'block'
115
+ path = []
116
+ command = 'update'
117
+ alive = false
118
+
119
+ {
120
+ id: block_id,
121
+ table: table,
122
+ path: path,
123
+ command: command,
124
+ args: {
125
+ alive: alive
126
+ }
127
+ }
128
+ end
129
+
130
+ def self.duplicate(block_type, block_title, block_id, new_block_id, user_notion_id, contents)
131
+ # ! payload for duplicating a block. Most properties should be
132
+ # ! inherited from the block class the method is invoked on.
133
+ # ! block_type -> type of block that is being duplicated : ``cls``
134
+ # ! block_title -> title of block : ``str``
135
+ # ! block_id -> id of block: ``str``
136
+ # ! new_block_id -> id of new block : ``str``
137
+ # ! user_notion_id -> ID of notion user : ``str``
138
+ # ! contents -> The children of the block
139
+ timestamp = DateTime.now.strftime('%Q')
140
+ table = 'block'
141
+ path = []
142
+ command = 'update'
143
+
144
+ {
145
+ id: new_block_id,
146
+ table: table,
147
+ path: path,
148
+ command: command,
149
+ args: {
150
+ id: new_block_id,
151
+ version: 10,
152
+ type: block_type,
153
+ properties: {
154
+ title: [[block_title]]
155
+ },
156
+ content: contents, # root-level blocks
157
+ created_time: timestamp,
158
+ last_edited_time: timestamp,
159
+ created_by_table: 'notion_user',
160
+ created_by_id: user_notion_id,
161
+ last_edited_by_table: 'notion_user',
162
+ last_edited_by_id: user_notion_id,
163
+ copied_from: block_id
164
+ }
165
+ }
166
+ end
167
+
168
+ def self.parent_location_add(block_parent_id, block_id)
169
+ # ! payload for adding a parent
170
+ # ! block_parent_id -> the parent id of the block : ``str``
171
+ # ! block_id -> the id of the block : ``str``
172
+ table = 'block'
173
+ path = []
174
+ command = 'update'
175
+ parent_table = 'block'
176
+ alive = true
177
+
178
+ {
179
+ id: block_id,
180
+ table: table,
181
+ path: path,
182
+ command: command,
183
+ args: {
184
+ parent_id: block_parent_id,
185
+ parent_table: parent_table,
186
+ alive: alive
187
+ }
188
+ }
189
+ end
190
+
191
+ def self.block_location_add(block_parent_id, block_id, new_block_id = nil, target, command)
192
+ # ! payload for duplicating a block. Most properties should be
193
+ # ! inherited from the block class the method is invoked on.
194
+ # ! block_parent_id -> id of parent block : ``str``
195
+ # ! block_id -> id of block: ``str``
196
+ # ! new_block_id -> id of the new block: ``str``
197
+ # ! target -> the ID of the target block : ``str``
198
+ # ! command -> the position of the block, before or after, in relation to the target : ``str``
199
+ table = 'block'
200
+ path = ['content']
201
+
202
+ args = if command == 'listAfter'
203
+ {
204
+ after: target || block_id,
205
+ id: new_block_id || block_id
206
+ }
207
+ else
208
+ {
209
+ before: target || block_id,
210
+ id: new_block_id || block_id
211
+ }
212
+ end
213
+
214
+ {
215
+ table: table,
216
+ id: block_parent_id, # ID of the parent for the new block. It should be the block that the method is invoked on.
217
+ path: path,
218
+ command: command,
219
+ args: args
220
+ }
221
+ end
222
+
223
+ def self.block_location_remove(block_parent_id, block_id)
224
+ # ! removes a notion block
225
+ # ! block_parent_id -> the parent ID of the block to remove : ``str``
226
+ # ! block_id -> the ID of the block to remove : ``str``
227
+ table = 'block'
228
+ path = ['content']
229
+ command = 'listRemove'
230
+ {
231
+ table: table,
232
+ id: block_parent_id, # ID of the parent for the new block. It should be the block that the method is invoked on.
233
+ path: path,
234
+ command: command,
235
+ args: {
236
+ id: block_id
237
+ }
238
+ }
239
+ end
240
+
241
+ def self.checked_todo(block_id, standardized_check_val)
242
+ # ! payload for setting a "checked" value for TodoBlock.
243
+ # ! block_id -> the ID of the block to remove : ``str``
244
+ # ! standardized_check_val -> tyes/no value, determines the checked property of the block : ``str``
245
+ table = 'block'
246
+ path = ['properties']
247
+ command = 'update'
248
+ {
249
+ id: block_id,
250
+ table: table,
251
+ path: path,
252
+ command: command,
253
+ args: {
254
+ checked: [[standardized_check_val]]
255
+ }
256
+ }
257
+ end
258
+
259
+ def self.update_codeblock_language(block_id, coding_language)
260
+ # ! update the language for a codeblock
261
+ # ! block_id -> id of the code block
262
+ # ! coding_language -> language to change the block to.
263
+ table = 'block'
264
+ path = ['properties']
265
+ command = 'update'
266
+
267
+ {
268
+ id: block_id,
269
+ table: table,
270
+ path: path,
271
+ command: command,
272
+ args: {
273
+ language: [[coding_language]]
274
+ }
275
+ }
276
+ end
277
+ end
278
+
279
+ class CollectionViewComponents
280
+ def self.create_collection_view(new_block_id, collection_id, view_ids)
281
+ # ! payload for creating a collection view
282
+ # ! new_block_id -> id of the new block
283
+ # ! collection_id -> ID of the collection.
284
+ # ! view_ids -> id of the view
285
+ table = 'block'
286
+ command = 'update'
287
+ path = []
288
+ type = 'collection_view'
289
+ properties = {}
290
+ timestamp = DateTime.now.strftime('%Q')
291
+
292
+ {
293
+ id: new_block_id,
294
+ table: table,
295
+ path: path,
296
+ command: command,
297
+ args: {
298
+ id: new_block_id,
299
+ type: type,
300
+ collection_id: collection_id,
301
+ view_ids: [
302
+ view_ids
303
+ ],
304
+ properties: properties,
305
+ created_time: timestamp,
306
+ last_edited_time: timestamp
307
+ }
308
+ }
309
+ end
310
+
311
+ def self.set_collection_blocks_alive(new_block_id, collection_id)
312
+ # ! payload for setting the collection blocks to alive.
313
+ # ! new_block_id -> id of the new block
314
+ # ! collection_id -> ID of the collection.
315
+ table = 'block'
316
+ path = []
317
+ command = 'update'
318
+ parent_table = 'collection'
319
+ alive = true
320
+ type = 'page'
321
+ properties = {}
322
+ timestamp = DateTime.now.strftime('%Q')
323
+
324
+ {
325
+ id: new_block_id,
326
+ table: table,
327
+ path: path,
328
+ command: command,
329
+ args: {
330
+ id: new_block_id,
331
+ type: type,
332
+ parent_id: collection_id,
333
+ parent_table: parent_table,
334
+ alive: alive,
335
+ properties: properties,
336
+ created_time: timestamp,
337
+ last_edited_time: timestamp
338
+ }
339
+ }
340
+ end
341
+
342
+ def self.set_view_config(new_block_id, view_id, children_ids)
343
+ # ! payload for setting the configurations of the view.
344
+ # ! new_block_id -> id of the new block
345
+ # ! view_id -> id of the view
346
+ # ! children_ids -> IDs for the children of the collection.
347
+ table = 'collection_view'
348
+ path = []
349
+ command = 'update'
350
+ version = 0
351
+ type = 'table'
352
+ name = 'Default View'
353
+ parent_table = 'block'
354
+ alive = true
355
+
356
+ {
357
+ id: view_id,
358
+ table: table,
359
+ path: path,
360
+ command: command,
361
+ args: {
362
+ id: view_id,
363
+ version: version,
364
+ type: type,
365
+ name: name,
366
+ page_sort: children_ids,
367
+ parent_id: new_block_id,
368
+ parent_table: parent_table,
369
+ alive: alive
370
+ }
371
+ }
372
+ end
373
+
374
+ def self.set_collection_columns(collection_id, new_block_id, data)
375
+ # ! payload for setting the columns of the table.
376
+ # ! collection_id -> ID of the collection.
377
+ # ! new_block_id -> id of the new block
378
+ # ! data -> json data to insert into table.
379
+ col_names = data[0].keys
380
+
381
+ schema_conf = {}
382
+ col_names.each_with_index do |_name, i|
383
+ if i.zero?
384
+ schema_conf[:title] = { name: col_names[i], type: 'title' }
385
+ else
386
+ schema_conf[col_names[i]] = { name: col_names[i], type: 'text' }
387
+ end
388
+ end
389
+ {
390
+ id: collection_id,
391
+ table: 'collection',
392
+ path: [],
393
+ command: 'update',
394
+ args: {
395
+ id: collection_id,
396
+ schema: schema_conf,
397
+ parent_id: new_block_id,
398
+ parent_table: 'block',
399
+ alive: true
400
+ }
401
+ }
402
+ end
403
+
404
+ def self.set_collection_title(collection_title, collection_id)
405
+ # ! payload for setting the title of the collection.
406
+ # ! collection_title -> title of the collection.
407
+ # ! collection_id -> ID of the collection.
408
+ table = 'collection'
409
+ path = ['name']
410
+ command = 'set'
411
+
412
+ {
413
+ id: collection_id,
414
+ table: table,
415
+ path: path,
416
+ command: command,
417
+ args: [[collection_title]]
418
+ }
419
+ end
420
+
421
+ def self.insert_data(block_id, column, value)
422
+ # ! payload for inserting data into the table.
423
+ # ! block_id -> the ID of the block : ``str``
424
+ # ! column -> the name of the column to insert data into.
425
+ # ! value -> the value to insert into the column.
426
+ table = 'block'
427
+ path = [
428
+ 'properties',
429
+ column
430
+ ]
431
+ command = 'set'
432
+
433
+ {
434
+ id: block_id,
435
+ table: table,
436
+ path: path,
437
+ command: command,
438
+ args: [[value]]
439
+ }
440
+ end
441
+
442
+ def self.add_new_row(new_block_id)
443
+ # ! payload for adding a new row to the table.
444
+ # ! new_block_id -> the ID of the new row : ``str``
445
+ table = 'block'
446
+ path = []
447
+ command = 'set'
448
+ type = 'page'
449
+
450
+ {
451
+ id: new_block_id,
452
+ table: table,
453
+ path: path,
454
+ command: command,
455
+ args: {
456
+ type: type,
457
+ id: new_block_id,
458
+ version: 1
459
+ }
460
+ }
461
+ end
462
+
463
+ def self.query_collection(collection_id, view_id, search_query = '')
464
+ # ! payload for querying the table for data.
465
+ # ! collection_id -> the collection ID : ``str``
466
+ # ! view_id -> the view ID : ``str``
467
+ # ! search_query -> the query for searching the table : ``str``
468
+ query = {}
469
+ loader = {
470
+ type: 'table',
471
+ limit: 100,
472
+ searchQuery: search_query,
473
+ loadContentCover: true
474
+ }
475
+
476
+ {
477
+ collectionId: collection_id,
478
+ collectionViewId: view_id,
479
+ query: query,
480
+ loader: loader
481
+ }
482
+ end
483
+
484
+ def self.add_collection_property(collection_id, args)
485
+ # ! payload for adding a column to the table.
486
+ # ! collection_id -> the collection ID : ``str``
487
+ # ! args -> the definition of the column : ``str``
488
+ {
489
+ id: collection_id,
490
+ table: 'collection',
491
+ path: [],
492
+ command: 'update',
493
+ args: args
494
+ }
495
+ end
496
+ end
497
+
498
+ def build_payload(operations, request_ids)
499
+ # ! properly formats the payload for Notions backend.
500
+ # ! operations -> an array of hashes that define the operations to perform : ``Array[Hash]``
501
+ # ! request_ids -> the unique IDs for the request : ``str``
502
+ request_id = request_ids[:request_id]
503
+ transaction_id = request_ids[:transaction_id]
504
+ space_id = request_ids[:space_id]
505
+ payload = {
506
+ requestId: request_id,
507
+ transactions: [
508
+ {
509
+ id: transaction_id,
510
+ shardId: 955_090,
511
+ spaceId: space_id,
512
+ operations: operations
513
+ }
514
+ ]
515
+ }
516
+ payload
517
+ end
518
+ end