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 +4 -4
- data/README.md +8 -4
- data/lib/notion_api/core.rb +128 -99
- data/lib/notion_api/notion_types/code_block.rb +13 -13
- data/lib/notion_api/notion_types/collection_view_blocks.rb +72 -36
- data/lib/notion_api/notion_types/column_list_block.rb +13 -13
- data/lib/notion_api/notion_types/divider_block.rb +13 -13
- data/lib/notion_api/notion_types/header_block.rb +13 -13
- data/lib/notion_api/notion_types/image_block.rb +59 -12
- data/lib/notion_api/notion_types/latex_block.rb +13 -13
- data/lib/notion_api/notion_types/numbered_block.rb +13 -13
- data/lib/notion_api/notion_types/page_block.rb +117 -124
- data/lib/notion_api/notion_types/quote_block.rb +13 -13
- data/lib/notion_api/notion_types/sub_header_block.rb +13 -13
- data/lib/notion_api/notion_types/sub_sub_header.rb +13 -13
- data/lib/notion_api/notion_types/table_of_contents_block.rb +3 -3
- data/lib/notion_api/notion_types/template.rb +325 -328
- data/lib/notion_api/notion_types/text_block.rb +13 -13
- data/lib/notion_api/notion_types/todo_block.rb +56 -56
- data/lib/notion_api/notion_types/toggle_block.rb +13 -13
- data/lib/notion_api/utils.rb +175 -76
- data/lib/notion_api/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6dfa3bb58bd4850c061ed6cf0efbb11ad3548f9dbcfb773b04fdb36d99cf356
|
4
|
+
data.tar.gz: 1be5e4c8dc4975fc2de62bc802b296b283cff406a943d3f5fa88980ecfa07b65
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
314
|
-
|
315
|
-
|
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:
|
data/lib/notion_api/core.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require
|
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 = {
|
11
|
-
@type_whitelist =
|
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(
|
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
|
49
|
-
|
50
|
-
|
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(
|
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
|
-
|
98
|
-
|
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[
|
110
|
-
Core.options[
|
111
|
-
cookies = Core.options[
|
112
|
-
headers = Core.options[
|
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[
|
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(
|
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
|
-
|
143
|
-
|
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
|
-
|
149
|
-
|
150
|
-
|
113
|
+
:properties => properties,
|
114
|
+
:format => formats,
|
115
|
+
}
|
151
116
|
end
|
152
117
|
|
153
|
-
def get_all_block_info(
|
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[
|
157
|
-
Core.options[
|
158
|
-
cookies = Core.options[
|
159
|
-
headers = Core.options[
|
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)[
|
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[
|
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][
|
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][
|
190
|
-
Core.type_whitelist.include?(filter_nil_blocks[clean_id][
|
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[
|
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][
|
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[
|
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[
|
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[
|
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
|
-
|
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(
|
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
|
-
|
247
|
-
|
248
|
-
|
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,
|
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[
|
259
|
-
headers = Core.options[
|
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[
|
254
|
+
response["recordMap"]["collection"][collection_id]["value"]["schema"]
|
272
255
|
else
|
273
|
-
response[
|
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[
|
282
|
-
headers = Core.options[
|
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[
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
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
|
-
|
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(
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|