notion 1.0.1 → 1.0.6

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