notion 1.0.1 → 1.0.6

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.
@@ -42,13 +42,27 @@ module NotionAPI
42
42
  end
43
43
 
44
44
  block_id = clean_id
45
- block_title = extract_title(clean_id, jsonified_record_response)
46
45
  block_type = extract_type(clean_id, jsonified_record_response)
47
46
  block_parent_id = extract_parent_id(clean_id, jsonified_record_response)
48
47
 
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)
48
+ raise 'the URL or ID passed to the get_page method must be that of a Page Block.' if !['collection_view_page', 'page'].include?(block_type)
49
+
50
+ if block_type == "page"
51
+ block_title = extract_title(clean_id, jsonified_record_response)
52
+ PageBlock.new(block_id, block_title, block_parent_id)
53
+ elsif block_type == "collection_view_page"
54
+ collection_id = extract_collection_id(block_id, jsonified_record_response)
55
+ block_title = extract_collection_title(clean_id, collection_id, jsonified_record_response)
56
+ view_id = extract_view_ids(block_id, jsonified_record_response)[0]
57
+ schema = extract_collection_schema(collection_id, view_id, jsonified_record_response)
58
+ column_mappings = schema.keys
59
+ column_names = column_mappings.map { |mapping| schema[mapping]['name']}
60
+
61
+ collection_view_page = CollectionViewPage.new(block_id, block_title, block_parent_id, collection_id, view_id)
62
+ collection_view_page.instance_variable_set(:@column_names, column_names)
63
+ CollectionView.class_eval{attr_reader :column_names}
64
+ collection_view_page
65
+ end
52
66
  end
53
67
 
54
68
  def children(url_or_id = @id)
@@ -113,11 +127,34 @@ module NotionAPI
113
127
  children_ids(url_or_id).empty? ? [] : children_ids(url_or_id)[-1]
114
128
  end
115
129
 
130
+ def get_block_props_and_format(clean_id, block_title)
131
+ request_body = {
132
+ pageId: clean_id,
133
+ chunkNumber: 0,
134
+ limit: 100,
135
+ verticalColumns: false
136
+ }
137
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
138
+ i = 0
139
+ while jsonified_record_response.empty?
140
+ return {:properties => {title: [[block_title]]}, :format => {}} if i >= 10
141
+
142
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
143
+ i += 1
144
+ end
145
+ properties = jsonified_record_response['block'][clean_id]['value']['properties']
146
+ formats = jsonified_record_response['block'][clean_id]['value']['format']
147
+ return {
148
+ :properties => properties,
149
+ :format => formats
150
+ }
151
+ end
152
+
116
153
  def get_all_block_info(_clean_id, body)
117
154
  # ! retrieves all info pertaining to a block Id.
118
155
  # ! clean_id -> the block ID or URL cleaned : ``str``
119
156
  Core.options['cookies'][:token_v2] = @@token_v2
120
- Core.options['headers']['x-notion-active-user-header'] = @active_user_header
157
+ Core.options['headers']['x-notion-active-user-header'] = @@active_user_header
121
158
  cookies = Core.options['cookies']
122
159
  headers = Core.options['headers']
123
160
 
@@ -193,7 +230,7 @@ module NotionAPI
193
230
  def extract_view_ids(clean_id, jsonified_record_response)
194
231
  jsonified_record_response['block'][clean_id]['value']['view_ids'] || []
195
232
  end
196
-
233
+
197
234
  def extract_id(url_or_id)
198
235
  # ! parse and clean the URL or ID object provided.
199
236
  # ! url_or_id -> the block ID or URL : ``str``
@@ -213,5 +250,47 @@ module NotionAPI
213
250
  raise ArgumentError, 'Expected a Notion page URL or a page ID. Please consult the documentation for further information.'
214
251
  end
215
252
  end
253
+
254
+ def extract_collection_schema(collection_id, view_id, response = {})
255
+ # ! retrieve the collection scehma. Useful for 'building' the backbone for a table.
256
+ # ! collection_id -> the collection ID : ``str``
257
+ # ! view_id -> the view ID : ``str``
258
+ cookies = Core.options['cookies']
259
+ headers = Core.options['headers']
260
+
261
+ if response.empty?
262
+ query_collection_hash = Utils::CollectionViewComponents.query_collection(collection_id, view_id, '')
263
+
264
+ request_url = URLS[:GET_COLLECTION]
265
+ response = HTTParty.post(
266
+ request_url,
267
+ body: query_collection_hash.to_json,
268
+ cookies: cookies,
269
+ headers: headers
270
+ )
271
+ response['recordMap']['collection'][collection_id]['value']['schema']
272
+ else
273
+ response['collection'][collection_id]['value']['schema']
274
+ end
275
+ end
276
+
277
+ def extract_collection_data(collection_id, view_id)
278
+ # ! retrieve the collection scehma. Useful for 'building' the backbone for a table.
279
+ # ! collection_id -> the collection ID : ``str``
280
+ # ! view_id -> the view ID : ``str``
281
+ cookies = Core.options['cookies']
282
+ headers = Core.options['headers']
283
+
284
+ query_collection_hash = Utils::CollectionViewComponents.query_collection(collection_id, view_id, '')
285
+
286
+ request_url = URLS[:GET_COLLECTION]
287
+ response = HTTParty.post(
288
+ request_url,
289
+ body: query_collection_hash.to_json,
290
+ cookies: cookies,
291
+ headers: headers
292
+ )
293
+ response['recordMap']
294
+ end
216
295
  end
217
296
  end
@@ -0,0 +1,17 @@
1
+ require_relative "template"
2
+
3
+ module NotionAPI
4
+ # Bullet list block: best for an unordered list
5
+ class BulletedBlock < BlockTemplate
6
+ @notion_type = "bulleted_list"
7
+ @type = "bulleted_list"
8
+
9
+ def type
10
+ NotionAPI::BulletedBlock.notion_type
11
+ end
12
+
13
+ class << self
14
+ attr_reader :notion_type, :type
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module NotionAPI
2
+
3
+ # same as quote... works similarly to page block
4
+ class CalloutBlock < BlockTemplate
5
+ @notion_type = 'callout'
6
+ @type = 'callout'
7
+
8
+ def type
9
+ NotionAPI::CalloutBlock.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
+ # Code block: used to store code, should be assigned a coding language.
4
+ class CodeBlock < BlockTemplate
5
+ @notion_type = 'code'
6
+ @type = 'code'
7
+
8
+ def type
9
+ NotionAPI::CodeBlock.notion_type
10
+ end
11
+
12
+ class << self
13
+ attr_reader :notion_type, :type
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,316 @@
1
+ module NotionAPI
2
+ # collection views such as tables and timelines.
3
+ class CollectionView < Core
4
+ attr_reader :id, :title, :parent_id, :collection_id, :view_id
5
+
6
+ @notion_type = "collection_view"
7
+ @type = "collection_view"
8
+
9
+ def type
10
+ NotionAPI::CollectionView.notion_type
11
+ end
12
+
13
+ class << self
14
+ attr_reader :notion_type, :type
15
+ end
16
+
17
+ def initialize(id, title, parent_id, collection_id, view_id)
18
+ @id = id
19
+ @title = title
20
+ @parent_id = parent_id
21
+ @collection_id = collection_id
22
+ @view_id = view_id
23
+ end
24
+
25
+ def add_row(data)
26
+ # ! add new row to Collection View table.
27
+ # ! data -> data to add to table : ``hash``
28
+
29
+ cookies = Core.options["cookies"]
30
+ headers = Core.options["headers"]
31
+
32
+ request_id = extract_id(SecureRandom.hex(16))
33
+ transaction_id = extract_id(SecureRandom.hex(16))
34
+ space_id = extract_id(SecureRandom.hex(16))
35
+ new_block_id = extract_id(SecureRandom.hex(16))
36
+ schema = extract_collection_schema(@collection_id, @view_id)
37
+ keys = schema.keys
38
+ col_map = {}
39
+ keys.map { |key| col_map[schema[key]["name"]] = key }
40
+
41
+ request_ids = {
42
+ request_id: request_id,
43
+ transaction_id: transaction_id,
44
+ space_id: space_id,
45
+ }
46
+
47
+ instantiate_row = Utils::CollectionViewComponents.add_new_row(new_block_id)
48
+ set_block_alive = Utils::CollectionViewComponents.set_collection_blocks_alive(new_block_id, @collection_id)
49
+ new_block_edited_time = Utils::BlockComponents.last_edited_time(new_block_id)
50
+ parent_edited_time = Utils::BlockComponents.last_edited_time(@parent_id)
51
+
52
+ operations = [
53
+ instantiate_row,
54
+ set_block_alive,
55
+ new_block_edited_time,
56
+ parent_edited_time,
57
+ ]
58
+
59
+ data.keys.each_with_index do |col_name, j|
60
+ unless col_map.keys.include?(col_name.to_s); raise ArgumentError, "Column '#{col_name.to_s}' does not exist." end
61
+ child_component = Utils::CollectionViewComponents.insert_data(new_block_id, col_map[col_name.to_s], data[col_name], schema[col_map[col_name.to_s]]["type"])
62
+ operations.push(child_component)
63
+ end
64
+
65
+ request_url = URLS[:UPDATE_BLOCK]
66
+ request_body = build_payload(operations, request_ids)
67
+ response = HTTParty.post(
68
+ request_url,
69
+ body: request_body.to_json,
70
+ cookies: cookies,
71
+ headers: headers,
72
+ )
73
+
74
+ 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}.
75
+ Please try again, and if issues persist open an issue in GitHub."; end
76
+
77
+ NotionAPI::CollectionViewRow.new(new_block_id, @parent_id, @collection_id, @view_id)
78
+ end
79
+
80
+ def add_property(name, type)
81
+ # ! add a property (column) to the table.
82
+ # ! name -> name of the property : ``str``
83
+ # ! type -> type of the property : ``str``
84
+ cookies = Core.options["cookies"]
85
+ headers = Core.options["headers"]
86
+
87
+ request_id = extract_id(SecureRandom.hex(16))
88
+ transaction_id = extract_id(SecureRandom.hex(16))
89
+ space_id = extract_id(SecureRandom.hex(16))
90
+
91
+ request_ids = {
92
+ request_id: request_id,
93
+ transaction_id: transaction_id,
94
+ space_id: space_id,
95
+ }
96
+
97
+ # create updated schema
98
+ schema = extract_collection_schema(@collection_id, @view_id)
99
+ schema[name] = {
100
+ name: name,
101
+ type: type,
102
+ }
103
+ new_schema = {
104
+ schema: schema,
105
+ }
106
+
107
+ add_collection_property = Utils::CollectionViewComponents.add_collection_property(@collection_id, new_schema)
108
+
109
+ operations = [
110
+ add_collection_property,
111
+ ]
112
+
113
+ request_url = URLS[:UPDATE_BLOCK]
114
+ request_body = build_payload(operations, request_ids)
115
+ response = HTTParty.post(
116
+ request_url,
117
+ body: request_body.to_json,
118
+ cookies: cookies,
119
+ headers: headers,
120
+ )
121
+ 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}.
122
+ Please try again, and if issues persist open an issue in GitHub."; end
123
+
124
+ true
125
+ end
126
+
127
+ def row(row_id)
128
+ # ! retrieve a row from a CollectionView Table.
129
+ # ! row_id -> the ID for the row to retrieve: ``str``
130
+ clean_id = extract_id(row_id)
131
+
132
+ request_body = {
133
+ pageId: clean_id,
134
+ chunkNumber: 0,
135
+ limit: 100,
136
+ verticalColumns: false,
137
+ }
138
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
139
+
140
+ i = 0
141
+ while jsonified_record_response.empty? || jsonified_record_response["block"].empty?
142
+ return {} if i >= 10
143
+
144
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
145
+ i += 1
146
+ end
147
+
148
+ collection_data = extract_collection_data(@collection_id, @view_id)
149
+ schema = collection_data["collection"][collection_id]["value"]["schema"]
150
+ column_mappings = schema.keys
151
+ column_names = column_mappings.map { |mapping| schema[mapping]["name"] }
152
+
153
+ collection_row = CollectionViewRow.new(row_id, @parent_id, @collection_id, @view_id)
154
+ collection_row.instance_variable_set(:@column_names, column_names)
155
+ CollectionViewRow.class_eval { attr_reader :column_names }
156
+
157
+ row_data = collection_data["block"][collection_row.id]
158
+ create_singleton_methods_and_instance_variables(collection_row, row_data)
159
+
160
+ collection_row
161
+ end
162
+
163
+ def row_ids
164
+ # ! retrieve all Collection View table rows.
165
+ clean_id = extract_id(@id)
166
+
167
+ request_body = {
168
+ pageId: clean_id,
169
+ chunkNumber: 0,
170
+ limit: 100,
171
+ verticalColumns: false,
172
+ }
173
+
174
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
175
+ i = 0
176
+ while jsonified_record_response.empty? || jsonified_record_response["block"].empty?
177
+ return {} if i >= 10
178
+
179
+ jsonified_record_response = get_all_block_info(clean_id, request_body)
180
+ i += 1
181
+ end
182
+
183
+ jsonified_record_response["collection_view"][@view_id]["value"]["page_sort"]
184
+ end
185
+
186
+ def rows
187
+ # ! returns all rows as instantiated class instances.
188
+ row_id_array = row_ids
189
+ parent_id = @parent_id
190
+ collection_id = @collection_id
191
+ view_id = @view_id
192
+ collection_data = extract_collection_data(@collection_id, @view_id)
193
+ schema = collection_data["collection"][collection_id]["value"]["schema"]
194
+ column_mappings = schema.keys
195
+ column_names = column_mappings.map { |mapping| schema[mapping]["name"] }
196
+
197
+ row_instances = row_id_array.map { |row_id| NotionAPI::CollectionViewRow.new(row_id, parent_id, collection_id, view_id) }
198
+ clean_row_instances = row_instances.filter { |row| collection_data["block"][row.id] }
199
+ clean_row_instances.each { |row| row.instance_variable_set(:@column_names, column_names) }
200
+ CollectionViewRow.class_eval { attr_reader :column_names }
201
+
202
+ clean_row_instances.each do |collection_row|
203
+ row_data = collection_data["block"][collection_row.id]
204
+ create_singleton_methods_and_instance_variables(collection_row, row_data)
205
+ end
206
+ clean_row_instances
207
+ end
208
+ def create_singleton_methods_and_instance_variables(row, row_data)
209
+ # ! creates singleton methods for each property in a CollectionView.
210
+ # ! row -> the block ID of the 'row' to retrieve: ``str``
211
+ # ! row_data -> the data corresponding to that row, should be key-value pairs where the keys are the columns: ``hash``
212
+ collection_data = extract_collection_data(@collection_id, @view_id)
213
+ schema = collection_data["collection"][collection_id]["value"]["schema"]
214
+ column_mappings = schema.keys
215
+ column_hash = {}
216
+ column_names = column_mappings.map { |mapping| column_hash[mapping] = schema[mapping]["name"].downcase }
217
+
218
+ column_hash.keys.each_with_index do |column, i|
219
+ # loop over the column names...
220
+ # set instance variables for each column, allowing the dev to 'read' the column value
221
+ cleaned_column = column_hash[column].split(" ").join("_").downcase.to_sym
222
+
223
+ # p row_data["value"]["properties"][column_mappings[i]], !(row_data["value"]["properties"][column] or row_data["value"]["properties"][column_mappings[i]])
224
+ if row_data["value"]["properties"].nil? or row_data["value"]["properties"][column].nil?
225
+ value = ""
226
+ else
227
+ value = row_data["value"]["properties"][column][0][0]
228
+ end
229
+
230
+ row.instance_variable_set("@#{cleaned_column}", value)
231
+ CollectionViewRow.class_eval { attr_reader cleaned_column }
232
+ # then, define singleton methods for each column that are used to update the table cell
233
+ row.define_singleton_method("#{cleaned_column}=") do |new_value|
234
+ # neat way to get the name of the currently invoked method...
235
+ parsed_method = __method__.to_s[0...-1].split("_").join(" ")
236
+ cookies = Core.options["cookies"]
237
+ headers = Core.options["headers"]
238
+
239
+ request_id = extract_id(SecureRandom.hex(16))
240
+ transaction_id = extract_id(SecureRandom.hex(16))
241
+ space_id = extract_id(SecureRandom.hex(16))
242
+
243
+ request_ids = {
244
+ request_id: request_id,
245
+ transaction_id: transaction_id,
246
+ space_id: space_id,
247
+ }
248
+
249
+ update_property_value = Utils::CollectionViewComponents.update_property_value(@id, column_hash.key(parsed_method), new_value)
250
+
251
+ operations = [
252
+ update_property_value,
253
+ ]
254
+
255
+ request_url = URLS[:UPDATE_BLOCK]
256
+ request_body = build_payload(operations, request_ids)
257
+ response = HTTParty.post(
258
+ request_url,
259
+ body: request_body.to_json,
260
+ cookies: cookies,
261
+ headers: headers,
262
+ )
263
+ 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}.
264
+ Please try again, and if issues persist open an issue in GitHub.";end
265
+
266
+ # set the instance variable to the updated value!
267
+ _ = row.instance_variable_set("@#{__method__.to_s[0...-1]}", new_value)
268
+ row
269
+ end
270
+ end
271
+ end
272
+ end
273
+
274
+ # class that represents each row in a CollectionView
275
+ class CollectionViewRow < Core
276
+ @notion_type = "table_row"
277
+ @type = "table_row"
278
+
279
+ def type
280
+ NotionAPI::CollectionViewRow.notion_type
281
+ end
282
+
283
+ def inspect
284
+ "CollectionViewRow - id: #{self.id} - parent id: #{self.parent_id}"
285
+ end
286
+
287
+ class << self
288
+ attr_reader :notion_type, :type, :parent_id
289
+ end
290
+
291
+ attr_reader :parent_id, :id
292
+
293
+ def initialize(id, parent_id, collection_id, view_id)
294
+ @id = id
295
+ @parent_id = parent_id
296
+ @collection_id = collection_id
297
+ @view_id = view_id
298
+ end
299
+ end
300
+
301
+ # class that represents a CollectionViewPage, inheriting all properties from CollectionView
302
+ class CollectionViewPage < CollectionView
303
+ @notion_type = "collection_view_page"
304
+ @type = "collection_view_page"
305
+
306
+ def type
307
+ NotionAPI::CollectionViewRow.notion_type
308
+ end
309
+
310
+ class << self
311
+ attr_reader :notion_type, :type, :parent_id
312
+ end
313
+
314
+ attr_reader :parent_id, :id
315
+ end
316
+ end