Skip to content

Now supports reading and writing data to properties dynamically! #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 47 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,23 @@
- Check out the [Gem](https://rubygems.org/gems/notion)!

## Table of Contents
- [Getting Started](#getting-started)
* [Installation](#installation)
- [Retrieving a Page](#retrieving-a-page)
- [Retrieving a Block within the Page](#retrieving-a-block-within-the-page)
* [Get a Block](#get-a-block)
* [Get a Collection View](#get-a-collection-view)
- [Creating New Blocks](#creating-new-blocks)
* [Create a block whose parent is the page](#create-a-block-whose-parent-is-the-page)
* [Create a block whose parent is another block](#create-a-block-whose-parent-is-another-block)
- [Creating New Collections](#creating-new-collections)
- [Troubleshooting](#troubleshooting)
* [No results returned when attempting to get a page](#no-results-returned-when-attempting-to-get-a-page)
- [Unofficial Notion Client for Ruby.](#unofficial-notion-client-for-ruby)
- [Table of Contents](#table-of-contents)
- [Getting Started](#getting-started)
- [Installation](#installation)
- [Retrieving a Page](#retrieving-a-page)
- [Retrieving a CollectionView Page](#retrieving-a-collectionview-page)
- [Retrieving a Block within the Page](#retrieving-a-block-within-the-page)
- [Get a Block](#get-a-block)
- [Get a Collection View](#get-a-collection-view)
- [Creating New Blocks](#creating-new-blocks)
- [Create a block whose parent is the page](#create-a-block-whose-parent-is-the-page)
- [Create a block whose parent is another block](#create-a-block-whose-parent-is-another-block)
- [Creating New Collections](#creating-new-collections)
- [Updating Collection View Cells](#updating-collection-view-cells)
- [Troubleshooting](#troubleshooting)
- [No results returned when attempting to get a page](#no-results-returned-when-attempting-to-get-a-page)
- [Retrieve a full-page Collection View](#retrieve-a-full-page-collection-view)

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

## Retrieving a CollectionView Page
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)).
## Retrieving a Block within the Page
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).

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

## Updating Collection View Cells
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:
| emoji | description | category | aliases | tags | unicode_version | ios_version |
|-------|--------------|---------------------|---------|---------|-----------------|-------------|
| 😉 | "winking face" | "Smileys & Emotion" | "wink" | "flirt" | "6.0" | "6.0" |

If you wanted to update the unicode and ios versions, you could use the following code:
```ruby
>>> collection_view = @page.get_collection("1234567") # the ID of the collection block is 1234567
>>> rows = collection_view.rows
>>> row[0].unicode_version = "updated version here!"
>>> row[0].ios_version = "I was updated too!"
```
Now, your Collection View will look like this:
| emoji | description | category | aliases | tags | unicode_version | ios_version |
|-------|--------------|---------------------|---------|---------|-----------------|-------------|
| 😉 | "winking face" | "Smileys & Emotion" | "wink" | "flirt" | "updated version here!" | "I was updated too!" |

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.

**One important thing to be aware of:**
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.
## Troubleshooting
### No results returned when attempting to get a page
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.
Expand All @@ -280,4 +309,9 @@ From here, you can instantiate the Notion Client with the following code:
"<insert_x_notion_active_user_header_here>"
)
```

### Retrieve a full-page Collection View
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:
```text
the URL or ID passed to the get_page method must be that of a Page Block.
```
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:
30 changes: 17 additions & 13 deletions lib/notion_api/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def get_page(url_or_id)
collection_id = extract_collection_id(block_id, jsonified_record_response)
block_title = extract_collection_title(clean_id, collection_id, jsonified_record_response)
view_id = extract_view_ids(block_id, jsonified_record_response)[0]
schema = extract_collection_schema(collection_id, view_id)
schema = extract_collection_schema(collection_id, view_id, jsonified_record_response)
column_mappings = schema.keys
column_names = column_mappings.map { |mapping| schema[mapping]['name']}

Expand Down Expand Up @@ -250,24 +250,28 @@ def extract_id(url_or_id)
raise ArgumentError, 'Expected a Notion page URL or a page ID. Please consult the documentation for further information.'
end
end
# TODO: lets update this to not have to make a full isolated request [if possible]
def extract_collection_schema(collection_id, view_id)

def extract_collection_schema(collection_id, view_id, response = {})
# ! retrieve the collection scehma. Useful for 'building' the backbone for a table.
# ! collection_id -> the collection ID : ``str``
# ! view_id -> the view ID : ``str``
cookies = Core.options['cookies']
headers = Core.options['headers']

query_collection_hash = Utils::CollectionViewComponents.query_collection(collection_id, view_id, '')

request_url = URLS[:GET_COLLECTION]
response = HTTParty.post(
request_url,
body: query_collection_hash.to_json,
cookies: cookies,
headers: headers
)
response['recordMap']['collection'][collection_id]['value']['schema']
if response.empty?
query_collection_hash = Utils::CollectionViewComponents.query_collection(collection_id, view_id, '')

request_url = URLS[:GET_COLLECTION]
response = HTTParty.post(
request_url,
body: query_collection_hash.to_json,
cookies: cookies,
headers: headers
)
response['recordMap']['collection'][collection_id]['value']['schema']
else
response['collection'][collection_id]['value']['schema']
end
end

def extract_collection_data(collection_id, view_id)
Expand Down
30 changes: 21 additions & 9 deletions lib/notion_api/notion_types/collection_view_blocks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def row(row_id)
column_mappings = schema.keys
column_names = column_mappings.map { |mapping| schema[mapping]["name"] }

collection_row = CollectionViewRow.new(row_id, @id, @collection_id, @view_id)
collection_row = CollectionViewRow.new(row_id, @parent_id, @collection_id, @view_id)
collection_row.instance_variable_set(:@column_names, column_names)
CollectionViewRow.class_eval { attr_reader :column_names }

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

row.column_names.each_with_index do |column, i|
column_hash.keys.each_with_index do |column, i|
# loop over the column names...
# set instance variables for each column, allowing the dev to 'read' the column value
cleaned_column = column.split(" ").join("_").downcase.to_sym
cleaned_column = column_hash[column].split(" ").join("_").downcase.to_sym

# p row_data["value"]["properties"][column_mappings[i]], !(row_data["value"]["properties"][column] or row_data["value"]["properties"][column_mappings[i]])
if row_data["value"]["properties"].nil? or row_data["value"]["properties"][column].nil?
value = ""
else
value = row_data["value"]["properties"][column][0][0]
end

p row_data["value"]["properties"][column_mappings[i]], !(row_data["value"]["properties"][column] or row_data["value"]["properties"][column_mappings[i]])
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])
row.instance_variable_set("@#{cleaned_column}", value)
CollectionViewRow.class_eval { attr_reader cleaned_column }
# then, define singleton methods for each column that are used to update the table cell
row.define_singleton_method("#{cleaned_column}=") do |new_value|
# neat way to get the name of the currently invoked method...
parsed_method = __method__.to_s[0...-1]
parsed_method = __method__.to_s[0...-1].split("_").join(" ")
cookies = Core.options["cookies"]
headers = Core.options["headers"]

Expand All @@ -237,7 +244,8 @@ def create_singleton_methods_and_instance_variables(row, row_data)
transaction_id: transaction_id,
space_id: space_id,
}
update_property_value = Utils::CollectionViewComponents.update_property_value(@id, parsed_method, new_value)

update_property_value = Utils::CollectionViewComponents.update_property_value(@id, column_hash.key(parsed_method), new_value)

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

# set the instance variable to the updated value!
_ = row.instance_variable_set("@#{parsed_method}".downcase, new_value)
_ = row.instance_variable_set("@#{__method__.to_s[0...-1]}", new_value)
row
end
end
Expand All @@ -271,6 +279,10 @@ def type
NotionAPI::CollectionViewRow.notion_type
end

def inspect
"CollectionViewRow - id: #{self.id} - parent id: #{self.parent_id}"
end

class << self
attr_reader :notion_type, :type, :parent_id
end
Expand Down
2 changes: 1 addition & 1 deletion lib/notion_api/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module NotionAPI
VERSION = '1.0.4'
VERSION = '1.0.5'
end
6 changes: 0 additions & 6 deletions spec/blocks_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,27 +116,21 @@
subject { @new_row_info }

it "should have a col_one method that equals json_data['col one']." do
p @new_row_info
expect(@new_row_info.col_one).to eq(@json_data["Col One"])
end
it "should have a col_one method that equals json_data['col two']." do
p @new_row_info
expect(@new_row_info.col_two).to eq(@json_data["Col Two"])
end
it "should have a col_one method that equals json_data['col three']." do
p @new_row_info
expect(@new_row_info.col_three).to eq(@json_data["Col Three"])
end
it "should have a col_one method that equals json_data['col four']." do
p @new_row_info
expect(@new_row_info.col_four).to eq(@json_data["Col Four"])
end
it "should have a col_one method that equals json_data['col five']." do
p @new_row_info
expect(@new_row_info.col_five).to eq(@json_data["Col Five"])
end
it "should have a col_one method that equals json_data['col six']." do
p @new_row_info
expect(@new_row_info.col_six).to eq(@json_data["Col Six"])
end
end
Expand Down
5 changes: 5 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
require "notion_api"
require_relative "spec_variables"

# TODO: missing testing concepts:
# 1. tables and other CVs with missing data (i.e. how do the methods perform when there is a null cell)
# 2. tables and other CVs with entirely blank rows (i.e. how do the methods perform when there is an entirely null cell)
# 3. CV page IDs different from regular Page IDs, so that should be fixed.

RSpec.configure do |conf|
conf.before(:example) do

Expand Down