Skip to content

Commit 89dfc6a

Browse files
Merge pull request #16 from danmurphy1217/add-update-row-functionality
Now supports reading and writing data to properties dynamically!
2 parents e29a132 + 500fdd1 commit 89dfc6a

File tree

6 files changed

+91
-42
lines changed

6 files changed

+91
-42
lines changed

README.md

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,23 @@
88
- Check out the [Gem](https://rubygems.org/gems/notion)!
99

1010
## Table of Contents
11-
- [Getting Started](#getting-started)
12-
* [Installation](#installation)
13-
- [Retrieving a Page](#retrieving-a-page)
14-
- [Retrieving a Block within the Page](#retrieving-a-block-within-the-page)
15-
* [Get a Block](#get-a-block)
16-
* [Get a Collection View](#get-a-collection-view)
17-
- [Creating New Blocks](#creating-new-blocks)
18-
* [Create a block whose parent is the page](#create-a-block-whose-parent-is-the-page)
19-
* [Create a block whose parent is another block](#create-a-block-whose-parent-is-another-block)
20-
- [Creating New Collections](#creating-new-collections)
21-
- [Troubleshooting](#troubleshooting)
22-
* [No results returned when attempting to get a page](#no-results-returned-when-attempting-to-get-a-page)
11+
- [Unofficial Notion Client for Ruby.](#unofficial-notion-client-for-ruby)
12+
- [Table of Contents](#table-of-contents)
13+
- [Getting Started](#getting-started)
14+
- [Installation](#installation)
15+
- [Retrieving a Page](#retrieving-a-page)
16+
- [Retrieving a CollectionView Page](#retrieving-a-collectionview-page)
17+
- [Retrieving a Block within the Page](#retrieving-a-block-within-the-page)
18+
- [Get a Block](#get-a-block)
19+
- [Get a Collection View](#get-a-collection-view)
20+
- [Creating New Blocks](#creating-new-blocks)
21+
- [Create a block whose parent is the page](#create-a-block-whose-parent-is-the-page)
22+
- [Create a block whose parent is another block](#create-a-block-whose-parent-is-another-block)
23+
- [Creating New Collections](#creating-new-collections)
24+
- [Updating Collection View Cells](#updating-collection-view-cells)
25+
- [Troubleshooting](#troubleshooting)
26+
- [No results returned when attempting to get a page](#no-results-returned-when-attempting-to-get-a-page)
27+
- [Retrieve a full-page Collection View](#retrieve-a-full-page-collection-view)
2328

2429
## Getting Started
2530
### Installation
@@ -60,6 +65,8 @@ The following attributes can be read from any block class instance:
6065
To update the title of the page:
6166
![Update the title of a page](https://github.com/danmurphy1217/notion-ruby/blob/master/gifs/change_title.gif)
6267

68+
## Retrieving a CollectionView Page
69+
This is achieved by passing the ID of the Collection View to the `get_page` method. Currently, the full URL of a Collection View Page is not supported (next up on the features list!). Once you retrieve the Collection View Page, all of the methods exposed to a normal Collection View instance are available (such as `.rows`, `.row(<row_id>)`, and all else outlined in [Updating a Collection](#updating-collection-view-cells)).
6370
## Retrieving a Block within the Page
6471
Now that you have retrieved a Notion Page, you have full access to the blocks on that page. You can retrieve a specific block or collection view, retrieve all children IDs (array of children IDs), or retrieve all children (array of children class instances).
6572

@@ -268,6 +275,28 @@ The first argument passed to `create_collection` determines which type of collec
268275
4. timeline
269276
5. gallery
270277

278+
## Updating Collection View Cells
279+
When you retrieve a `CollectionViewRow` instance with `.row(<row_id>)` or a list of `CollectionViewRow` instances with `.rows`, a handful of methods are created. Each row instance has access attributes that represent the properties in the Notion Collection View. So, let's say we are working with the following Notion Collection View:
280+
| emoji | description | category | aliases | tags | unicode_version | ios_version |
281+
|-------|--------------|---------------------|---------|---------|-----------------|-------------|
282+
| 😉 | "winking face" | "Smileys & Emotion" | "wink" | "flirt" | "6.0" | "6.0" |
283+
284+
If you wanted to update the unicode and ios versions, you could use the following code:
285+
```ruby
286+
>>> collection_view = @page.get_collection("1234567") # the ID of the collection block is 1234567
287+
>>> rows = collection_view.rows
288+
>>> row[0].unicode_version = "updated version here!"
289+
>>> row[0].ios_version = "I was updated too!"
290+
```
291+
Now, your Collection View will look like this:
292+
| emoji | description | category | aliases | tags | unicode_version | ios_version |
293+
|-------|--------------|---------------------|---------|---------|-----------------|-------------|
294+
| 😉 | "winking face" | "Smileys & Emotion" | "wink" | "flirt" | "updated version here!" | "I was updated too!" |
295+
296+
You can also add new rows with the `.add_row({<data!>})` method and add new properties with the `.add_property("name_of_property", "type_of_property")` method.
297+
298+
**One important thing to be aware of:**
299+
When adding a row with `.add_row`, the hash of data passed must be in the same order as it appears in your Notion Collection View.
271300
## Troubleshooting
272301
### No results returned when attempting to get a page
273302
If an empty hash is returned when you attempt to retrieve a Notion page, you'll need to include the `x-notion-active-user-header` when instantiating the Notion Client.
@@ -280,4 +309,9 @@ From here, you can instantiate the Notion Client with the following code:
280309
"<insert_x_notion_active_user_header_here>"
281310
)
282311
```
283-
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.
316+
```
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:

lib/notion_api/core.rb

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def get_page(url_or_id)
5454
collection_id = extract_collection_id(block_id, jsonified_record_response)
5555
block_title = extract_collection_title(clean_id, collection_id, jsonified_record_response)
5656
view_id = extract_view_ids(block_id, jsonified_record_response)[0]
57-
schema = extract_collection_schema(collection_id, view_id)
57+
schema = extract_collection_schema(collection_id, view_id, jsonified_record_response)
5858
column_mappings = schema.keys
5959
column_names = column_mappings.map { |mapping| schema[mapping]['name']}
6060

@@ -250,24 +250,28 @@ def extract_id(url_or_id)
250250
raise ArgumentError, 'Expected a Notion page URL or a page ID. Please consult the documentation for further information.'
251251
end
252252
end
253-
# TODO: lets update this to not have to make a full isolated request [if possible]
254-
def extract_collection_schema(collection_id, view_id)
253+
254+
def extract_collection_schema(collection_id, view_id, response = {})
255255
# ! retrieve the collection scehma. Useful for 'building' the backbone for a table.
256256
# ! collection_id -> the collection ID : ``str``
257257
# ! view_id -> the view ID : ``str``
258258
cookies = Core.options['cookies']
259259
headers = Core.options['headers']
260260

261-
query_collection_hash = Utils::CollectionViewComponents.query_collection(collection_id, view_id, '')
262-
263-
request_url = URLS[:GET_COLLECTION]
264-
response = HTTParty.post(
265-
request_url,
266-
body: query_collection_hash.to_json,
267-
cookies: cookies,
268-
headers: headers
269-
)
270-
response['recordMap']['collection'][collection_id]['value']['schema']
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
271275
end
272276

273277
def extract_collection_data(collection_id, view_id)

lib/notion_api/notion_types/collection_view_blocks.rb

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def row(row_id)
149149
column_mappings = schema.keys
150150
column_names = column_mappings.map { |mapping| schema[mapping]["name"] }
151151

152-
collection_row = CollectionViewRow.new(row_id, @id, @collection_id, @view_id)
152+
collection_row = CollectionViewRow.new(row_id, @parent_id, @collection_id, @view_id)
153153
collection_row.instance_variable_set(:@column_names, column_names)
154154
CollectionViewRow.class_eval { attr_reader :column_names }
155155

@@ -211,20 +211,27 @@ def create_singleton_methods_and_instance_variables(row, row_data)
211211
collection_data = extract_collection_data(@collection_id, @view_id)
212212
schema = collection_data["collection"][collection_id]["value"]["schema"]
213213
column_mappings = schema.keys
214-
column_names = column_mappings.map { |mapping| schema[mapping]["name"] }
214+
column_hash = {}
215+
column_names = column_mappings.map { |mapping| column_hash[mapping] = schema[mapping]["name"].downcase }
215216

216-
row.column_names.each_with_index do |column, i|
217+
column_hash.keys.each_with_index do |column, i|
217218
# loop over the column names...
218219
# set instance variables for each column, allowing the dev to 'read' the column value
219-
cleaned_column = column.split(" ").join("_").downcase.to_sym
220+
cleaned_column = column_hash[column].split(" ").join("_").downcase.to_sym
221+
222+
# p row_data["value"]["properties"][column_mappings[i]], !(row_data["value"]["properties"][column] or row_data["value"]["properties"][column_mappings[i]])
223+
if row_data["value"]["properties"].nil? or row_data["value"]["properties"][column].nil?
224+
value = ""
225+
else
226+
value = row_data["value"]["properties"][column][0][0]
227+
end
220228

221-
p row_data["value"]["properties"][column_mappings[i]], !(row_data["value"]["properties"][column] or row_data["value"]["properties"][column_mappings[i]])
222-
row.instance_variable_set("@#{cleaned_column}", !(row_data["value"]["properties"][column] or row_data["value"]["properties"][column_mappings[i]]) ? row_data["value"]["properties"]["title"][0][0] : row_data["value"]["properties"][column_mappings[i]][0][0])
229+
row.instance_variable_set("@#{cleaned_column}", value)
223230
CollectionViewRow.class_eval { attr_reader cleaned_column }
224231
# then, define singleton methods for each column that are used to update the table cell
225232
row.define_singleton_method("#{cleaned_column}=") do |new_value|
226233
# neat way to get the name of the currently invoked method...
227-
parsed_method = __method__.to_s[0...-1]
234+
parsed_method = __method__.to_s[0...-1].split("_").join(" ")
228235
cookies = Core.options["cookies"]
229236
headers = Core.options["headers"]
230237

@@ -237,7 +244,8 @@ def create_singleton_methods_and_instance_variables(row, row_data)
237244
transaction_id: transaction_id,
238245
space_id: space_id,
239246
}
240-
update_property_value = Utils::CollectionViewComponents.update_property_value(@id, parsed_method, new_value)
247+
248+
update_property_value = Utils::CollectionViewComponents.update_property_value(@id, column_hash.key(parsed_method), new_value)
241249

242250
operations = [
243251
update_property_value,
@@ -255,7 +263,7 @@ def create_singleton_methods_and_instance_variables(row, row_data)
255263
Please try again, and if issues persist open an issue in GitHub.";end
256264

257265
# set the instance variable to the updated value!
258-
_ = row.instance_variable_set("@#{parsed_method}".downcase, new_value)
266+
_ = row.instance_variable_set("@#{__method__.to_s[0...-1]}", new_value)
259267
row
260268
end
261269
end
@@ -271,6 +279,10 @@ def type
271279
NotionAPI::CollectionViewRow.notion_type
272280
end
273281

282+
def inspect
283+
"CollectionViewRow - id: #{self.id} - parent id: #{self.parent_id}"
284+
end
285+
274286
class << self
275287
attr_reader :notion_type, :type, :parent_id
276288
end

lib/notion_api/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module NotionAPI
2-
VERSION = '1.0.4'
2+
VERSION = '1.0.5'
33
end

spec/blocks_spec.rb

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,27 +116,21 @@
116116
subject { @new_row_info }
117117

118118
it "should have a col_one method that equals json_data['col one']." do
119-
p @new_row_info
120119
expect(@new_row_info.col_one).to eq(@json_data["Col One"])
121120
end
122121
it "should have a col_one method that equals json_data['col two']." do
123-
p @new_row_info
124122
expect(@new_row_info.col_two).to eq(@json_data["Col Two"])
125123
end
126124
it "should have a col_one method that equals json_data['col three']." do
127-
p @new_row_info
128125
expect(@new_row_info.col_three).to eq(@json_data["Col Three"])
129126
end
130127
it "should have a col_one method that equals json_data['col four']." do
131-
p @new_row_info
132128
expect(@new_row_info.col_four).to eq(@json_data["Col Four"])
133129
end
134130
it "should have a col_one method that equals json_data['col five']." do
135-
p @new_row_info
136131
expect(@new_row_info.col_five).to eq(@json_data["Col Five"])
137132
end
138133
it "should have a col_one method that equals json_data['col six']." do
139-
p @new_row_info
140134
expect(@new_row_info.col_six).to eq(@json_data["Col Six"])
141135
end
142136
end

spec/spec_helper.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
require "notion_api"
22
require_relative "spec_variables"
33

4+
# TODO: missing testing concepts:
5+
# 1. tables and other CVs with missing data (i.e. how do the methods perform when there is a null cell)
6+
# 2. tables and other CVs with entirely blank rows (i.e. how do the methods perform when there is an entirely null cell)
7+
# 3. CV page IDs different from regular Page IDs, so that should be fixed.
8+
49
RSpec.configure do |conf|
510
conf.before(:example) do
611

0 commit comments

Comments
 (0)