notion_rb 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +14 -0
- data/.ruby-version +1 -0
- data/.travis.yml +14 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +115 -0
- data/LICENSE.txt +21 -0
- data/README.md +92 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/notion_rb.rb +31 -0
- data/lib/notion_rb/api/base.rb +56 -0
- data/lib/notion_rb/api/create.rb +74 -0
- data/lib/notion_rb/api/destroy.rb +41 -0
- data/lib/notion_rb/api/get.rb +58 -0
- data/lib/notion_rb/api/query_collection.rb +30 -0
- data/lib/notion_rb/api/restore.rb +42 -0
- data/lib/notion_rb/api/update.rb +45 -0
- data/lib/notion_rb/block.rb +97 -0
- data/lib/notion_rb/utils/block_cache.rb +58 -0
- data/lib/notion_rb/utils/converter.rb +23 -0
- data/lib/notion_rb/utils/parser.rb +100 -0
- data/lib/notion_rb/utils/types.rb +44 -0
- data/lib/notion_rb/utils/uuid_validator.rb +32 -0
- data/lib/notion_rb/version.rb +5 -0
- data/notion_rb.gemspec +41 -0
- metadata +216 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NotionRb
|
4
|
+
module Api
|
5
|
+
class Base
|
6
|
+
BASE_URL = 'https://www.notion.so/'
|
7
|
+
API_BASE_URL = BASE_URL + 'api/v3/'
|
8
|
+
SIGNED_URL_PREFIX = 'https://www.notion.so/signed/'
|
9
|
+
S3_URL_PREFIX = 'https://s3-us-west-2.amazonaws.com/secure.notion-static.com/'
|
10
|
+
S3_URL_PREFIX_ENCODED = 'https://s3.us-west-2.amazonaws.com/secure.notion-static.com/'
|
11
|
+
REDEFINE_EXCEPTION = 'Define methods on subclass'
|
12
|
+
|
13
|
+
def initialize(params)
|
14
|
+
@notion_id = params[:notion_id]
|
15
|
+
@token_v2 = NotionRb.config[:token_v2]
|
16
|
+
@converter = NotionRb::Utils::Converter.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def call
|
20
|
+
response
|
21
|
+
end
|
22
|
+
|
23
|
+
def success?
|
24
|
+
response.code == '200'
|
25
|
+
rescue Mechanize::ResponseCodeError
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def agent
|
32
|
+
return @agent if @agent
|
33
|
+
|
34
|
+
@agent = Mechanize.new
|
35
|
+
cookie = Mechanize::Cookie.new(domain: 'www.notion.so',
|
36
|
+
name: 'token_v2',
|
37
|
+
value: @token_v2,
|
38
|
+
path: '/',
|
39
|
+
expires: (Date.today + 1).to_s)
|
40
|
+
@agent.tap { |agent| agent.cookie_jar << cookie }
|
41
|
+
end
|
42
|
+
|
43
|
+
def response
|
44
|
+
@response ||= agent.post("#{API_BASE_URL}#{url}", params.to_json, 'Content-Type' => 'application/json')
|
45
|
+
end
|
46
|
+
|
47
|
+
def url
|
48
|
+
raise REDEFINE_EXCEPTION
|
49
|
+
end
|
50
|
+
|
51
|
+
def params
|
52
|
+
raise REDEFINE_EXCEPTION
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NotionRb
|
4
|
+
module Api
|
5
|
+
class Create < Base
|
6
|
+
def initialize(params)
|
7
|
+
super
|
8
|
+
|
9
|
+
@parent_id = params[:parent_id]
|
10
|
+
@notion_id = SecureRandom.uuid
|
11
|
+
@created_at = (Time.now.to_i / 100) * 100_000
|
12
|
+
end
|
13
|
+
|
14
|
+
def block_uuid
|
15
|
+
success? && @notion_id
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def url
|
21
|
+
'submitTransaction'
|
22
|
+
end
|
23
|
+
|
24
|
+
def params
|
25
|
+
{
|
26
|
+
requestId: SecureRandom.uuid,
|
27
|
+
transactions: [{
|
28
|
+
id: SecureRandom.uuid,
|
29
|
+
operations: [{
|
30
|
+
id: @notion_id,
|
31
|
+
table: 'block',
|
32
|
+
path: [],
|
33
|
+
command: 'set',
|
34
|
+
args: { type: 'text', id: @notion_id, version: 1 }
|
35
|
+
}, {
|
36
|
+
id: @notion_id,
|
37
|
+
table: 'block',
|
38
|
+
path: [],
|
39
|
+
command: 'update',
|
40
|
+
args: { parent_id: @parent_id, parent_table: 'block', alive: true }
|
41
|
+
}, {
|
42
|
+
id: @parent_id,
|
43
|
+
table: 'block',
|
44
|
+
path: ['content'],
|
45
|
+
command: 'listAfter',
|
46
|
+
args: { id: @notion_id }
|
47
|
+
}, {
|
48
|
+
id: @notion_id,
|
49
|
+
table: 'block',
|
50
|
+
path: %w[
|
51
|
+
properties
|
52
|
+
title
|
53
|
+
],
|
54
|
+
command: 'set',
|
55
|
+
args: []
|
56
|
+
}, {
|
57
|
+
id: @notion_id,
|
58
|
+
table: 'block',
|
59
|
+
path: ['created_time'],
|
60
|
+
command: 'set',
|
61
|
+
args: @created_at
|
62
|
+
}, {
|
63
|
+
id: @notion_id,
|
64
|
+
table: 'block',
|
65
|
+
path: ['last_edited_time'],
|
66
|
+
command: 'set',
|
67
|
+
args: @created_at
|
68
|
+
}]
|
69
|
+
}]
|
70
|
+
}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NotionRb
|
4
|
+
module Api
|
5
|
+
class Destroy < Base
|
6
|
+
def initialize(params)
|
7
|
+
super
|
8
|
+
|
9
|
+
@parent_id = params[:parent_id]
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def url
|
15
|
+
'submitTransaction'
|
16
|
+
end
|
17
|
+
|
18
|
+
def params
|
19
|
+
{
|
20
|
+
requestId: SecureRandom.uuid,
|
21
|
+
transactions: [{
|
22
|
+
id: SecureRandom.uuid,
|
23
|
+
operations: [{
|
24
|
+
id: @notion_id,
|
25
|
+
table: 'block',
|
26
|
+
path: [],
|
27
|
+
command: 'update',
|
28
|
+
args: { alive: false }
|
29
|
+
}, {
|
30
|
+
id: @parent_id,
|
31
|
+
table: 'block',
|
32
|
+
path: ['content'],
|
33
|
+
command: 'listRemove',
|
34
|
+
args: { id: @notion_id }
|
35
|
+
}]
|
36
|
+
}]
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NotionRb
|
4
|
+
module Api
|
5
|
+
class Get < Base
|
6
|
+
def initialize(_params)
|
7
|
+
super
|
8
|
+
|
9
|
+
@blocks = []
|
10
|
+
@data = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def blocks
|
14
|
+
return @blocks if @blocks.any?
|
15
|
+
|
16
|
+
call
|
17
|
+
@blocks
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
body = JSON.parse(response.body)
|
22
|
+
@collection_rows = body.dig('result', 'blockIds')
|
23
|
+
data = body.dig('recordMap', 'block').merge(body.dig('recordMap', 'collection') || {})
|
24
|
+
data.values.each do |value|
|
25
|
+
convert_values(value.dig('value'))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def url
|
32
|
+
'loadPageChunk'
|
33
|
+
end
|
34
|
+
|
35
|
+
def params
|
36
|
+
{
|
37
|
+
chunkNumber: 0,
|
38
|
+
cursor: { stack: [] },
|
39
|
+
limit: 50,
|
40
|
+
pageId: @notion_id,
|
41
|
+
verticalColumns: false
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def convert_values(value)
|
46
|
+
add_collection_data(value)
|
47
|
+
@blocks << @converter.convert(value)
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_collection_data(value)
|
51
|
+
return unless value.key? 'schema'
|
52
|
+
|
53
|
+
value['content'] = @collection_rows if @collection_rows
|
54
|
+
value['type'] = 'collection'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NotionRb
|
4
|
+
module Api
|
5
|
+
class QueryCollection < Get
|
6
|
+
def initialize(params)
|
7
|
+
super
|
8
|
+
|
9
|
+
@collection_id = params[:collection_id]
|
10
|
+
@view_id = params[:view_id]
|
11
|
+
@schema = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def url
|
17
|
+
'queryCollection'
|
18
|
+
end
|
19
|
+
|
20
|
+
def params
|
21
|
+
{
|
22
|
+
collectionId: @collection_id,
|
23
|
+
collectionViewId: @view_id,
|
24
|
+
query: { agregrate: [{}], filter: [], sort: [], filter_operation: 'and' },
|
25
|
+
loader: { type: 'table', limit: 70, userTimeZone: 'America/Santiago', userLocale: 'en', loadContentCover: true }
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NotionRb
|
4
|
+
module Api
|
5
|
+
class Restore < Base
|
6
|
+
def initialize(params)
|
7
|
+
super
|
8
|
+
|
9
|
+
@parent_id = params[:parent_id]
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def url
|
16
|
+
'submitTransaction'
|
17
|
+
end
|
18
|
+
|
19
|
+
def params
|
20
|
+
{
|
21
|
+
requestId: SecureRandom.uuid,
|
22
|
+
transactions: [{
|
23
|
+
id: SecureRandom.uuid,
|
24
|
+
operations: [{
|
25
|
+
id: @notion_id,
|
26
|
+
table: 'block',
|
27
|
+
path: [],
|
28
|
+
command: 'update',
|
29
|
+
args: { parent_id: @parent_id, parent_table: 'block', alive: true }
|
30
|
+
}, {
|
31
|
+
id: @parent_id,
|
32
|
+
table: 'block',
|
33
|
+
path: ['content'],
|
34
|
+
command: 'listAfter',
|
35
|
+
args: { id: @notion_id }
|
36
|
+
}]
|
37
|
+
}]
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NotionRb
|
4
|
+
module Api
|
5
|
+
class Update < Base
|
6
|
+
def initialize(params)
|
7
|
+
super
|
8
|
+
|
9
|
+
@title = params[:title]
|
10
|
+
@block_type = params[:block_type]
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def url
|
16
|
+
'submitTransaction'
|
17
|
+
end
|
18
|
+
|
19
|
+
def params
|
20
|
+
{
|
21
|
+
requestId: SecureRandom.uuid,
|
22
|
+
transactions: [{
|
23
|
+
id: SecureRandom.uuid,
|
24
|
+
operations: [{
|
25
|
+
id: @notion_id,
|
26
|
+
table: 'block',
|
27
|
+
path: %w[properties title],
|
28
|
+
command: 'set',
|
29
|
+
args: [[@title]]
|
30
|
+
}]
|
31
|
+
}, {
|
32
|
+
id: SecureRandom.uuid,
|
33
|
+
operations: [{
|
34
|
+
id: @notion_id,
|
35
|
+
table: 'block',
|
36
|
+
path: [],
|
37
|
+
command: 'update',
|
38
|
+
args: { type: @block_type }
|
39
|
+
}]
|
40
|
+
}]
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NotionRb
|
4
|
+
class Block
|
5
|
+
include NotionRb::Utils::UuidValidator
|
6
|
+
include NotionRb::Utils::Types
|
7
|
+
|
8
|
+
attr_accessor :uuid
|
9
|
+
|
10
|
+
def initialize(url_or_uuid)
|
11
|
+
@block_container = NotionRb::Utils::BlockCache.instance
|
12
|
+
@uuid = parse_uuid url_or_uuid
|
13
|
+
get_resource
|
14
|
+
end
|
15
|
+
|
16
|
+
def title
|
17
|
+
@block[:title]
|
18
|
+
end
|
19
|
+
|
20
|
+
def title=(title)
|
21
|
+
@block[:title] = title
|
22
|
+
save
|
23
|
+
end
|
24
|
+
|
25
|
+
def type
|
26
|
+
@block[:block_type]
|
27
|
+
end
|
28
|
+
|
29
|
+
def type=(type)
|
30
|
+
return unless valid_block_type?(type)
|
31
|
+
|
32
|
+
@block[:block_type] = type
|
33
|
+
save
|
34
|
+
end
|
35
|
+
|
36
|
+
def metadata
|
37
|
+
metadata = @block[:metadata].except(:properties)
|
38
|
+
add_parent_metadata(metadata)
|
39
|
+
end
|
40
|
+
|
41
|
+
def parent
|
42
|
+
@parent ||= self.class.new(@block[:parent_id])
|
43
|
+
end
|
44
|
+
|
45
|
+
def children
|
46
|
+
if type == 'collection_view_page'
|
47
|
+
@children ||= [self.class.new(metadata[:collection_id])]
|
48
|
+
else
|
49
|
+
@children ||= @block_container.children(@uuid).map { |child_uuid| self.class.new(child_uuid) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def create_child
|
54
|
+
creator = NotionRb::Api::Create.new(parent_id: @uuid)
|
55
|
+
return nil unless creator.success?
|
56
|
+
|
57
|
+
@block[:children] << creator.block_uuid
|
58
|
+
self.class.new(creator.block_uuid)
|
59
|
+
end
|
60
|
+
|
61
|
+
def save
|
62
|
+
# TODO: add validations if needed
|
63
|
+
post_resource
|
64
|
+
end
|
65
|
+
|
66
|
+
def destroy
|
67
|
+
NotionRb::Api::Destroy.new(notion_id: @uuid, parent_id: @block[:parent_id]).success?
|
68
|
+
end
|
69
|
+
|
70
|
+
def restore
|
71
|
+
NotionRb::Api::Restore.new(notion_id: @uuid, parent_id: @block[:parent_id]).success?
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def get_resource
|
77
|
+
@block = @block_container.find(@uuid)
|
78
|
+
@block_container.add_collection_view_blocks(@block)
|
79
|
+
end
|
80
|
+
|
81
|
+
def post_resource
|
82
|
+
# TODO: detect changes if any before post
|
83
|
+
NotionRb::Api::Update.new(notion_id: @uuid, title: @block[:title], block_type: @block[:block_type]).success?
|
84
|
+
end
|
85
|
+
|
86
|
+
def add_parent_metadata(metadata)
|
87
|
+
return metadata unless parent.type == 'collection'
|
88
|
+
|
89
|
+
parent_schema = parent.metadata[:schema]
|
90
|
+
@block.dig(:metadata, :properties).each do |key, value|
|
91
|
+
schema = parent_schema[key]
|
92
|
+
metadata.merge!(schema['name'] => value[0][0])
|
93
|
+
end
|
94
|
+
metadata
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|