notion 1.0.6 → 1.1.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04355fc0473c8f446e79ec4904105c0fb4fcb8a436032099e59d41ce6abbc9fc
4
- data.tar.gz: df7cf2d629fcd8c9dcf076a3186c2d49deef0f73454c051a2ad5530ffa98501d
3
+ metadata.gz: a6dfa3bb58bd4850c061ed6cf0efbb11ad3548f9dbcfb773b04fdb36d99cf356
4
+ data.tar.gz: 1be5e4c8dc4975fc2de62bc802b296b283cff406a943d3f5fa88980ecfa07b65
5
5
  SHA512:
6
- metadata.gz: 2359d17e2408b50b04a3ae1c9b4015de93b4e7d92b06a5b64d6cc8af48380e2affa7e4eb17d20e35def404116bdecef838bd9ec19c9c39867c05116de02eca40
7
- data.tar.gz: febb7a95cc0bd70c59e921a6479b4e4f7c80b6ad1443366aef0658358877876a04b37f9993827e15fe6d9eb0386efb7fa082063b0e6758e8ddeead1bb9dd5aa2
6
+ metadata.gz: 6291929f81e39755067265260f3ff94d4e691f420713af7cb7a1b7505a9ab097bdd78720bcc2d344df555cc20b106203aae0bd09eea52a041e8c4a8057b9b4a8
7
+ data.tar.gz: de33fa730ee063bb6e54877ce959e1ca7f8bde1345ae90db9c1a368e8c08ad37dec54f473d99c38c6bc26f3e97e17741bba92e3b09cd1d6cb64b5fdce629723b
data/README.md CHANGED
@@ -310,8 +310,12 @@ From here, you can instantiate the Notion Client with the following code:
310
310
  )
311
311
  ```
312
312
  ### Retrieve a full-page Collection View
313
- Currently, either a "normal" Page URL or the Page Block ID is accepted to the `get_page` method. Therefore, if you pass the full URL to the CV Table, it will raise an error:
314
- ```text
315
- the URL or ID passed to the get_page method must be that of a Page Block.
313
+ A full-page collection view must have a URL that follows the below pattern:
314
+ https://www.notion.so/danmurphy/[page-id]?v=[view-id]
315
+ Then, it can be retrieved with the following code:
316
+ ```ruby
317
+ >>> @client = NotionAPI::Client.new(
318
+ "<insert_v2_token_here>"
319
+ )
320
+ >>> @client.get_page("https://www.notion.so/danmurphy/[page-id]?v=[view-id]")
316
321
  ```
317
- To avoid this, you must pass only the ID of the full-page collection-view to the `get_page` method. This is next up on the features list, so passing the full URL will be supported soon:smile:
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'utils'
4
- require 'httparty'
3
+ require_relative "utils"
4
+ require "httparty"
5
5
 
6
6
  module NotionAPI
7
7
  # the initial methods available to an instantiated Cloent object are defined
8
8
  class Core
9
9
  include Utils
10
- @options = { 'cookies' => { :token_v2 => nil, 'x-active-user-header' => nil }, 'headers' => { 'Content-Type' => 'application/json' } }
11
- @type_whitelist = 'divider'
10
+ @options = { "cookies" => { :token_v2 => nil, "x-active-user-header" => nil }, "headers" => { "Content-Type" => "application/json" } }
11
+ @type_whitelist = "divider"
12
12
 
13
13
  class << self
14
14
  attr_reader :options, :type_whitelist, :token_v2, :active_user_header
@@ -30,39 +30,16 @@ module NotionAPI
30
30
  pageId: clean_id,
31
31
  chunkNumber: 0,
32
32
  limit: 100,
33
- verticalColumns: false
33
+ verticalColumns: false,
34
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
35
+ jsonified_record_response = get_all_block_info(request_body)
39
36
 
40
- jsonified_record_response = get_all_block_info(clean_id, request_body)
41
- i += 1
42
- end
43
-
44
- block_id = clean_id
45
37
  block_type = extract_type(clean_id, jsonified_record_response)
46
38
  block_parent_id = extract_parent_id(clean_id, jsonified_record_response)
47
39
 
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
40
+ raise ArgumentError, "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)
41
+
42
+ get_instantiated_instance_for(block_type, clean_id, block_parent_id, jsonified_record_response)
66
43
  end
67
44
 
68
45
  def children(url_or_id = @id)
@@ -87,18 +64,12 @@ module NotionAPI
87
64
  pageId: clean_id,
88
65
  chunkNumber: 0,
89
66
  limit: 100,
90
- verticalColumns: false
67
+ verticalColumns: false,
91
68
  }
92
- jsonified_record_response = get_all_block_info(clean_id, request_body)
93
- i = 0
94
- while jsonified_record_response.empty?
95
- return {} if i >= 10
69
+ jsonified_record_response = get_all_block_info(request_body)
96
70
 
97
- jsonified_record_response = get_all_block_info(clean_id, request_body)
98
- i += 1
99
- end
100
-
101
- jsonified_record_response['block'][clean_id]['value']['content'] || []
71
+ # if no content, returns empty list
72
+ jsonified_record_response["block"][clean_id]["value"]["content"] || []
102
73
  end
103
74
 
104
75
  private
@@ -106,19 +77,19 @@ module NotionAPI
106
77
  def get_notion_id(body)
107
78
  # ! retrieves a users ID from the headers of a Notion response object.
108
79
  # ! body -> the body to send in the request : ``Hash``
109
- Core.options['cookies'][:token_v2] = @@token_v2
110
- Core.options['headers']['x-notion-active-user-header'] = @@active_user_header
111
- cookies = Core.options['cookies']
112
- headers = Core.options['headers']
80
+ Core.options["cookies"][:token_v2] = @@token_v2
81
+ Core.options["headers"]["x-notion-active-user-header"] = @@active_user_header
82
+ cookies = Core.options["cookies"]
83
+ headers = Core.options["headers"]
113
84
  request_url = URLS[:GET_BLOCK]
114
85
 
115
86
  response = HTTParty.post(
116
87
  request_url,
117
88
  body: body.to_json,
118
89
  cookies: cookies,
119
- headers: headers
90
+ headers: headers,
120
91
  )
121
- response.headers['x-notion-user-id']
92
+ response.headers["x-notion-user-id"]
122
93
  end
123
94
 
124
95
  def get_last_page_block_id(url_or_id)
@@ -132,48 +103,53 @@ module NotionAPI
132
103
  pageId: clean_id,
133
104
  chunkNumber: 0,
134
105
  limit: 100,
135
- verticalColumns: false
106
+ verticalColumns: false,
136
107
  }
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
108
+ jsonified_record_response = get_all_block_info(request_body)
141
109
 
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']
110
+ properties = jsonified_record_response["block"][clean_id]["value"]["properties"]
111
+ formats = jsonified_record_response["block"][clean_id]["value"]["format"]
147
112
  return {
148
- :properties => properties,
149
- :format => formats
150
- }
113
+ :properties => properties,
114
+ :format => formats,
115
+ }
151
116
  end
152
117
 
153
- def get_all_block_info(_clean_id, body)
118
+ def get_all_block_info(body, i = 0)
154
119
  # ! retrieves all info pertaining to a block Id.
155
120
  # ! clean_id -> the block ID or URL cleaned : ``str``
156
- Core.options['cookies'][:token_v2] = @@token_v2
157
- Core.options['headers']['x-notion-active-user-header'] = @@active_user_header
158
- cookies = Core.options['cookies']
159
- headers = Core.options['headers']
160
-
121
+ Core.options["cookies"][:token_v2] = @@token_v2
122
+ Core.options["headers"]["x-notion-active-user-header"] = @@active_user_header
123
+ cookies = Core.options["cookies"]
124
+ headers = Core.options["headers"]
161
125
  request_url = URLS[:GET_BLOCK]
162
126
 
163
127
  response = HTTParty.post(
164
128
  request_url,
165
129
  body: body.to_json,
166
130
  cookies: cookies,
167
- headers: headers
131
+ headers: headers,
168
132
  )
169
133
 
170
- JSON.parse(response.body)['recordMap']
134
+ jsonified_record_response = JSON.parse(response.body)["recordMap"]
135
+ response_invalid = (!jsonified_record_response || jsonified_record_response.empty? || jsonified_record_response["block"].empty?)
136
+
137
+ if i < 10 && response_invalid
138
+ i = i + 1
139
+ return get_all_block_info(body, i)
140
+ else
141
+ if i == 10 && response_invalid
142
+ raise InvalidClientInstantiationError, "Attempted to retrieve block 10 times and received an empty response each time. Please make sure you have a valid token_v2 value set. If you do, then try setting the 'active_user_header' variable as well."
143
+ else
144
+ return jsonified_record_response
145
+ end
146
+ end
171
147
  end
172
148
 
173
149
  def filter_nil_blocks(jsonified_record_response)
174
150
  # ! removes any blocks that are empty [i.e. have no title / content]
175
151
  # ! jsonified_record_responses -> parsed JSON representation of a notion response object : ``Json``
176
- jsonified_record_response.empty? || jsonified_record_response['block'].empty? ? nil : jsonified_record_response['block']
152
+ jsonified_record_response.empty? || jsonified_record_response["block"].empty? ? nil : jsonified_record_response["block"]
177
153
  end
178
154
 
179
155
  def extract_title(clean_id, jsonified_record_response)
@@ -181,14 +157,13 @@ module NotionAPI
181
157
  # ! clean_id -> the cleaned block ID: ``str``
182
158
  # ! jsonified_record_response -> parsed JSON representation of a notion response object : ``Json``
183
159
  filter_nil_blocks = filter_nil_blocks(jsonified_record_response)
184
- if filter_nil_blocks.nil? || filter_nil_blocks[clean_id].nil? || filter_nil_blocks[clean_id]['value']['properties'].nil?
160
+ if filter_nil_blocks.nil? || filter_nil_blocks[clean_id].nil? || filter_nil_blocks[clean_id]["value"]["properties"].nil?
185
161
  nil
186
162
  else
187
163
  # titles for images are called source, while titles for text-based blocks are called title, so lets dynamically grab it
188
164
  # https://stackoverflow.com/questions/23765996/get-all-keys-from-ruby-hash/23766007
189
- title_value = filter_nil_blocks[clean_id]['value']['properties'].keys[0]
190
- Core.type_whitelist.include?(filter_nil_blocks[clean_id]['value']['type']) ? nil : jsonified_record_response['block'][clean_id]['value']['properties'][title_value].flatten[0]
191
-
165
+ title_value = filter_nil_blocks[clean_id]["value"]["properties"].keys[0]
166
+ Core.type_whitelist.include?(filter_nil_blocks[clean_id]["value"]["type"]) ? nil : jsonified_record_response["block"][clean_id]["value"]["properties"][title_value].flatten[0]
192
167
  end
193
168
  end
194
169
 
@@ -197,7 +172,7 @@ module NotionAPI
197
172
  # ! clean_id -> the cleaned block ID: ``str``
198
173
  # ! collection_id -> the collection ID: ``str``
199
174
  # ! jsonified_record_response -> parsed JSON representation of a notion response object : ``Json``
200
- jsonified_record_response['collection'][collection_id]['value']['name'].flatten.join if jsonified_record_response['collection'] and jsonified_record_response['collection'][collection_id]['value']['name']
175
+ jsonified_record_response["collection"][collection_id]["value"]["name"].flatten.join if jsonified_record_response["collection"] and jsonified_record_response["collection"][collection_id]["value"]["name"]
201
176
  end
202
177
 
203
178
  def extract_type(clean_id, jsonified_record_response)
@@ -208,8 +183,7 @@ module NotionAPI
208
183
  if filter_nil_blocks.nil?
209
184
  nil
210
185
  else
211
- filter_nil_blocks[clean_id]['value']['type']
212
-
186
+ filter_nil_blocks[clean_id]["value"]["type"]
213
187
  end
214
188
  end
215
189
 
@@ -217,37 +191,46 @@ module NotionAPI
217
191
  # ! extract parent ID from core JSON response object.
218
192
  # ! clean_id -> the block ID or URL cleaned : ``str``
219
193
  # ! jsonified_record_response -> parsed JSON representation of a notion response object : ``Json``
220
- jsonified_record_response.empty? || jsonified_record_response['block'].empty? ? {} : jsonified_record_response['block'][clean_id]['value']['parent_id']
194
+ jsonified_record_response.empty? || jsonified_record_response["block"].empty? ? {} : jsonified_record_response["block"][clean_id]["value"]["parent_id"]
221
195
  end
222
196
 
223
197
  def extract_collection_id(clean_id, jsonified_record_response)
224
198
  # ! extract the collection ID
225
199
  # ! clean_id -> the block ID or URL cleaned : ``str``
226
200
  # ! jsonified_record_response -> parsed JSON representation of a notion response object : ``Json``
227
- jsonified_record_response['block'][clean_id]['value']['collection_id']
201
+ jsonified_record_response["block"][clean_id]["value"]["collection_id"]
228
202
  end
229
203
 
230
204
  def extract_view_ids(clean_id, jsonified_record_response)
231
- jsonified_record_response['block'][clean_id]['value']['view_ids'] || []
205
+ jsonified_record_response["block"][clean_id]["value"]["view_ids"] || []
232
206
  end
233
-
207
+
234
208
  def extract_id(url_or_id)
235
209
  # ! parse and clean the URL or ID object provided.
236
210
  # ! url_or_id -> the block ID or URL : ``str``
237
211
  http_or_https = url_or_id.match(/^(http|https)/) # true if http or https in url_or_id...
238
- if (url_or_id.length == 36) && ((url_or_id.split('-').length == 5) && !http_or_https)
212
+ collection_view_match = url_or_id.match(/(\?v=)/)
213
+
214
+ if (url_or_id.length == 36) && ((url_or_id.split("-").length == 5) && !http_or_https)
239
215
  # passes if url_or_id is perfectly formatted already...
240
216
  url_or_id
241
- elsif (http_or_https && (url_or_id.split('-').last.length == 32)) || (!http_or_https && (url_or_id.length == 32))
217
+ elsif (http_or_https && (url_or_id.split("-").last.length == 32)) || (!http_or_https && (url_or_id.length == 32)) || (collection_view_match)
242
218
  # passes if either:
243
219
  # 1. a URL is passed as url_or_id and the ID at the end is 32 characters long or
244
220
  # 2. a URL is not passed and the ID length is 32 [aka unformatted]
245
221
  pattern = [8, 13, 18, 23]
246
- id = url_or_id.split('-').last
247
- pattern.each { |index| id.insert(index, '-') }
248
- id
222
+ if collection_view_match
223
+ id_without_view = url_or_id.split("?")[0]
224
+ clean_id = id_without_view.split("/").last
225
+ pattern.each { |index| clean_id.insert(index, "-") }
226
+ clean_id
227
+ else
228
+ id = url_or_id.split("-").last
229
+ pattern.each { |index| id.insert(index, "-") }
230
+ id
231
+ end
249
232
  else
250
- raise ArgumentError, 'Expected a Notion page URL or a page ID. Please consult the documentation for further information.'
233
+ raise ArgumentError, "Expected a Notion page URL or a page ID. Please consult the documentation for further information."
251
234
  end
252
235
  end
253
236
 
@@ -255,42 +238,88 @@ module NotionAPI
255
238
  # ! retrieve the collection scehma. Useful for 'building' the backbone for a table.
256
239
  # ! collection_id -> the collection ID : ``str``
257
240
  # ! view_id -> the view ID : ``str``
258
- cookies = Core.options['cookies']
259
- headers = Core.options['headers']
241
+ cookies = Core.options["cookies"]
242
+ headers = Core.options["headers"]
260
243
 
261
244
  if response.empty?
262
- query_collection_hash = Utils::CollectionViewComponents.query_collection(collection_id, view_id, '')
245
+ query_collection_hash = Utils::CollectionViewComponents.query_collection(collection_id, view_id, "")
263
246
 
264
247
  request_url = URLS[:GET_COLLECTION]
265
248
  response = HTTParty.post(
266
249
  request_url,
267
250
  body: query_collection_hash.to_json,
268
251
  cookies: cookies,
269
- headers: headers
252
+ headers: headers,
270
253
  )
271
- response['recordMap']['collection'][collection_id]['value']['schema']
254
+ response["recordMap"]["collection"][collection_id]["value"]["schema"]
272
255
  else
273
- response['collection'][collection_id]['value']['schema']
256
+ response["collection"][collection_id]["value"]["schema"]
274
257
  end
275
258
  end
276
-
259
+
277
260
  def extract_collection_data(collection_id, view_id)
278
261
  # ! retrieve the collection scehma. Useful for 'building' the backbone for a table.
279
262
  # ! collection_id -> the collection ID : ``str``
280
263
  # ! view_id -> the view ID : ``str``
281
- cookies = Core.options['cookies']
282
- headers = Core.options['headers']
264
+ cookies = Core.options["cookies"]
265
+ headers = Core.options["headers"]
283
266
 
284
- query_collection_hash = Utils::CollectionViewComponents.query_collection(collection_id, view_id, '')
267
+ query_collection_hash = Utils::CollectionViewComponents.query_collection(collection_id, view_id, "")
285
268
 
286
269
  request_url = URLS[:GET_COLLECTION]
287
270
  response = HTTParty.post(
288
271
  request_url,
289
272
  body: query_collection_hash.to_json,
290
273
  cookies: cookies,
291
- headers: headers
274
+ headers: headers,
292
275
  )
293
- response['recordMap']
276
+ response["recordMap"]
277
+ end
278
+
279
+ def extract_page_information(page_meta = {})
280
+ # ! helper method for extracting information about a page block
281
+ # ! page_meta -> hash containing data points useful for the extraction of a page blocks information.
282
+ # ! This should include clean_id, jsonified_record_response, and parent_id
283
+ clean_id = page_meta.fetch(:clean_id)
284
+ jsonified_record_response = page_meta.fetch(:jsonified_record_response)
285
+ block_parent_id = page_meta.fetch(:parent_id)
286
+
287
+ block_title = extract_title(clean_id, jsonified_record_response)
288
+ PageBlock.new(clean_id, block_title, block_parent_id)
289
+ end
290
+
291
+ def extract_collection_view_page_information(page_meta = {})
292
+ # ! helper method for extracting information about a Collection View page block
293
+ # ! page_meta -> hash containing data points useful for the extraction of a page blocks information.
294
+ # ! This should include clean_id, jsonified_record_response, and parent_id
295
+ clean_id = page_meta.fetch(:clean_id)
296
+ jsonified_record_response = page_meta.fetch(:jsonified_record_response)
297
+ block_parent_id = page_meta.fetch(:parent_id)
298
+
299
+ collection_id = extract_collection_id(clean_id, jsonified_record_response)
300
+ block_title = extract_collection_title(clean_id, collection_id, jsonified_record_response)
301
+ view_id = extract_view_ids(clean_id, jsonified_record_response)[0]
302
+ schema = extract_collection_schema(collection_id, view_id, jsonified_record_response)
303
+ column_names = NotionAPI::CollectionView.extract_collection_view_column_names(schema)
304
+
305
+ collection_view_page = CollectionViewPage.new(clean_id, block_title, block_parent_id, collection_id, view_id)
306
+ collection_view_page.instance_variable_set(:@column_names, column_names)
307
+ CollectionView.class_eval { attr_reader :column_names }
308
+ collection_view_page
309
+ end
310
+
311
+ def get_instantiated_instance_for(block_type, clean_id, parent_id, jsonified_record_response)
312
+ case block_type
313
+ when "page" then extract_page_information(clean_id: clean_id, parent_id: parent_id, jsonified_record_response: jsonified_record_response)
314
+ when "collection_view_page" then extract_collection_view_page_information(clean_id: clean_id, parent_id: parent_id, jsonified_record_response: jsonified_record_response)
315
+ end
316
+ end
317
+
318
+ class InvalidClientInstantiationError < StandardError
319
+ def initialize(msg = "Custom exception that is raised when an invalid property type is passed as a mapping.", exception_type = "instantiation_type")
320
+ @exception_type = exception_type
321
+ super(msg)
322
+ end
294
323
  end
295
324
  end
296
325
  end
@@ -1,16 +1,16 @@
1
1
  module NotionAPI
2
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
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
15
14
  end
16
- end
15
+ end
16
+ end
@@ -33,7 +33,9 @@ module NotionAPI
33
33
  transaction_id = extract_id(SecureRandom.hex(16))
34
34
  space_id = extract_id(SecureRandom.hex(16))
35
35
  new_block_id = extract_id(SecureRandom.hex(16))
36
- schema = extract_collection_schema(@collection_id, @view_id)
36
+ collection_data = extract_collection_data(collection_id, view_id)
37
+ last_row_id = collection_data["collection_view"][@view_id]["value"]["page_sort"][-1]
38
+ schema = collection_data["collection"][collection_id]["value"]["schema"]
37
39
  keys = schema.keys
38
40
  col_map = {}
39
41
  keys.map { |key| col_map[schema[key]["name"]] = key }
@@ -47,17 +49,27 @@ module NotionAPI
47
49
  instantiate_row = Utils::CollectionViewComponents.add_new_row(new_block_id)
48
50
  set_block_alive = Utils::CollectionViewComponents.set_collection_blocks_alive(new_block_id, @collection_id)
49
51
  new_block_edited_time = Utils::BlockComponents.last_edited_time(new_block_id)
50
- parent_edited_time = Utils::BlockComponents.last_edited_time(@parent_id)
52
+ page_sort = Utils::BlockComponents.row_location_add(last_row_id, new_block_id, @view_id)
51
53
 
52
54
  operations = [
53
55
  instantiate_row,
54
56
  set_block_alive,
55
57
  new_block_edited_time,
56
- parent_edited_time,
58
+ page_sort,
57
59
  ]
58
60
 
59
61
  data.keys.each_with_index do |col_name, j|
60
62
  unless col_map.keys.include?(col_name.to_s); raise ArgumentError, "Column '#{col_name.to_s}' does not exist." end
63
+ if %q[select multi_select].include?(schema[col_map[col_name.to_s]]["type"])
64
+ options = schema[col_map[col_name.to_s]]["options"].nil? ? [] : schema[col_map[col_name.to_s]]["options"].map { |option| option["value"] }
65
+ multi_select_multi_options = data[col_name].split(",")
66
+ multi_select_multi_options.each do |option|
67
+ if !options.include?(option.strip)
68
+ create_new_option = Utils::CollectionViewComponents.add_new_option(col_map[col_name.to_s], option.strip, @collection_id)
69
+ operations.push(create_new_option)
70
+ end
71
+ end
72
+ end
61
73
  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
74
  operations.push(child_component)
63
75
  end
@@ -74,7 +86,18 @@ module NotionAPI
74
86
  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
87
  Please try again, and if issues persist open an issue in GitHub."; end
76
88
 
77
- NotionAPI::CollectionViewRow.new(new_block_id, @parent_id, @collection_id, @view_id)
89
+ collection_row = NotionAPI::CollectionViewRow.new(new_block_id, @parent_id, @collection_id, @view_id)
90
+
91
+ properties = {}
92
+ data.keys.each do |col|
93
+ properties[col_map[col.to_s]] = [[data[col]]]
94
+ end
95
+
96
+ collection_data["block"][collection_row.id] = { "role" => "editor", "value" => { "id" => collection_row.id, "version" => 12, "type" => "page", "properties" => properties, "created_time" => 1607253360000, "last_edited_time" => 1607253360000, "parent_id" => "dde513c6-2428-4a5d-a830-7a67fdbf6b48", "parent_table" => "collection", "alive" => true, "created_by_table" => "notion_user", "created_by_id" => "0c5f02f3-495d-4b73-b1c5-9f6fe03a8c26", "last_edited_by_table" => "notion_user", "last_edited_by_id" => "0c5f02f3-495d-4b73-b1c5-9f6fe03a8c26", "shard_id" => 955090, "space_id" => "f687f7de-7f4c-4a86-b109-941a8dae92d2" } }
97
+ row_data = collection_data["block"][collection_row.id]
98
+ create_singleton_methods_and_instance_variables(collection_row, row_data)
99
+
100
+ collection_row
78
101
  end
79
102
 
80
103
  def add_property(name, type)
@@ -135,20 +158,11 @@ module NotionAPI
135
158
  limit: 100,
136
159
  verticalColumns: false,
137
160
  }
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
161
+ jsonified_record_response = get_all_block_info(request_body)
147
162
 
148
163
  collection_data = extract_collection_data(@collection_id, @view_id)
149
164
  schema = collection_data["collection"][collection_id]["value"]["schema"]
150
- column_mappings = schema.keys
151
- column_names = column_mappings.map { |mapping| schema[mapping]["name"] }
165
+ column_names = NotionAPI::CollectionView.extract_collection_view_column_names(schema)
152
166
 
153
167
  collection_row = CollectionViewRow.new(row_id, @parent_id, @collection_id, @view_id)
154
168
  collection_row.instance_variable_set(:@column_names, column_names)
@@ -171,29 +185,20 @@ module NotionAPI
171
185
  verticalColumns: false,
172
186
  }
173
187
 
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
188
+ jsonified_record_response = get_all_block_info(request_body)
182
189
 
183
190
  jsonified_record_response["collection_view"][@view_id]["value"]["page_sort"]
184
191
  end
185
192
 
186
193
  def rows
187
194
  # ! returns all rows as instantiated class instances.
188
- row_id_array = row_ids
195
+ row_id_array = row_ids()
189
196
  parent_id = @parent_id
190
197
  collection_id = @collection_id
191
198
  view_id = @view_id
192
199
  collection_data = extract_collection_data(@collection_id, @view_id)
193
200
  schema = collection_data["collection"][collection_id]["value"]["schema"]
194
- column_mappings = schema.keys
195
- column_names = column_mappings.map { |mapping| schema[mapping]["name"] }
196
-
201
+ column_names = NotionAPI::CollectionView.extract_collection_view_column_names(schema)
197
202
  row_instances = row_id_array.map { |row_id| NotionAPI::CollectionViewRow.new(row_id, parent_id, collection_id, view_id) }
198
203
  clean_row_instances = row_instances.filter { |row| collection_data["block"][row.id] }
199
204
  clean_row_instances.each { |row| row.instance_variable_set(:@column_names, column_names) }
@@ -205,6 +210,7 @@ module NotionAPI
205
210
  end
206
211
  clean_row_instances
207
212
  end
213
+
208
214
  def create_singleton_methods_and_instance_variables(row, row_data)
209
215
  # ! creates singleton methods for each property in a CollectionView.
210
216
  # ! row -> the block ID of the 'row' to retrieve: ``str``
@@ -214,22 +220,24 @@ module NotionAPI
214
220
  column_mappings = schema.keys
215
221
  column_hash = {}
216
222
  column_names = column_mappings.map { |mapping| column_hash[mapping] = schema[mapping]["name"].downcase }
217
-
223
+
218
224
  column_hash.keys.each_with_index do |column, i|
219
225
  # loop over the column names...
220
226
  # 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
227
+ cleaned_column = clean_property_names(column_hash, column)
222
228
 
223
- # p row_data["value"]["properties"][column_mappings[i]], !(row_data["value"]["properties"][column] or row_data["value"]["properties"][column_mappings[i]])
224
229
  if row_data["value"]["properties"].nil? or row_data["value"]["properties"][column].nil?
225
230
  value = ""
226
231
  else
227
- value = row_data["value"]["properties"][column][0][0]
232
+ value = row_data["value"]["properties"][column][0][0]
233
+ if ["‣"].include?(value.to_s)
234
+ value = row_data["value"]["properties"][column][0][1].flatten[-1]
235
+ end
228
236
  end
229
237
 
230
238
  row.instance_variable_set("@#{cleaned_column}", value)
231
239
  CollectionViewRow.class_eval { attr_reader cleaned_column }
232
- # then, define singleton methods for each column that are used to update the table cell
240
+ # then, define singleton methods for each column that are used to update the table cell
233
241
  row.define_singleton_method("#{cleaned_column}=") do |new_value|
234
242
  # neat way to get the name of the currently invoked method...
235
243
  parsed_method = __method__.to_s[0...-1].split("_").join(" ")
@@ -246,12 +254,23 @@ module NotionAPI
246
254
  space_id: space_id,
247
255
  }
248
256
 
249
- update_property_value = Utils::CollectionViewComponents.update_property_value(@id, column_hash.key(parsed_method), new_value)
257
+ update_property_value_hash = Utils::CollectionViewComponents.update_property_value(@id, column_hash.key(parsed_method), new_value, schema[column_hash.key(parsed_method)]["type"])
250
258
 
251
259
  operations = [
252
- update_property_value,
260
+ update_property_value_hash,
253
261
  ]
254
262
 
263
+ if %q[select multi_select].include?(schema[column_hash.key(parsed_method)]["type"])
264
+ options = schema[column_hash.key(parsed_method)]["options"].nil? ? [] : schema[column_hash.key(parsed_method)]["options"].map { |option| option["value"] }
265
+ multi_select_multi_options = new_value.split(",")
266
+ multi_select_multi_options.each do |option|
267
+ if !options.include?(option.strip)
268
+ create_new_option = Utils::CollectionViewComponents.add_new_option(column_hash.key(parsed_method), option.strip, @collection_id)
269
+ operations.push(create_new_option)
270
+ end
271
+ end
272
+ end
273
+
255
274
  request_url = URLS[:UPDATE_BLOCK]
256
275
  request_body = build_payload(operations, request_ids)
257
276
  response = HTTParty.post(
@@ -261,14 +280,31 @@ module NotionAPI
261
280
  headers: headers,
262
281
  )
263
282
  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
-
283
+ Please try again, and if issues persist open an issue in GitHub."; end
284
+
266
285
  # set the instance variable to the updated value!
267
286
  _ = row.instance_variable_set("@#{__method__.to_s[0...-1]}", new_value)
268
287
  row
269
288
  end
270
289
  end
271
290
  end
291
+
292
+ def clean_property_names(prop_hash, prop_notion_name)
293
+ # ! standardize property names by splitting the words in the property name into an array, removing non-alphanumeric
294
+ # ! characters, downcasing, and then re-joining the array with underscores.
295
+ # ! prop_hash -> hash of property notion names and property textual names: ``str``
296
+ # ! prop_notion_name -> the four-character long name of the notion property: ``str``
297
+
298
+ prop_hash[prop_notion_name].split(" ").map { |word| word.gsub(/[^a-z0-9]/i, "").downcase }.join("_").to_sym
299
+ end
300
+
301
+ def self.extract_collection_view_column_names(schema)
302
+ # ! extract the column names of a Collection View
303
+ # ! schema: the schema of the collection view
304
+ column_mappings = column_mappings = schema.keys
305
+ column_names = column_mappings.map { |mapping| schema[mapping]["name"] }
306
+ column_names
307
+ end
272
308
  end
273
309
 
274
310
  # class that represents each row in a CollectionView