notionrb 1.3.0

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.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.devcontainer/Dockerfile +5 -0
  3. data/.devcontainer/boot.sh +1 -0
  4. data/.devcontainer/devcontainer.json +30 -0
  5. data/.github/workflows/ci.yml +15 -0
  6. data/.github/workflows/rubocop.yml +17 -0
  7. data/.gitignore +7 -0
  8. data/.rspec +2 -0
  9. data/.rubocop.yml +55 -0
  10. data/.rubocop_todo.yml +86 -0
  11. data/CHANGELOG.md +144 -0
  12. data/CODE_OF_CONDUCT.md +128 -0
  13. data/CONTRIBUTING.md +51 -0
  14. data/Gemfile +4 -0
  15. data/Gemfile.lock +95 -0
  16. data/LICENSE.md +21 -0
  17. data/README.md +677 -0
  18. data/Rakefile +18 -0
  19. data/bin/console +31 -0
  20. data/lib/notion/api/endpoints/blocks.rb +100 -0
  21. data/lib/notion/api/endpoints/comments.rb +61 -0
  22. data/lib/notion/api/endpoints/data_sources.rb +102 -0
  23. data/lib/notion/api/endpoints/databases.rb +134 -0
  24. data/lib/notion/api/endpoints/pages.rb +95 -0
  25. data/lib/notion/api/endpoints/search.rb +41 -0
  26. data/lib/notion/api/endpoints/users.rb +48 -0
  27. data/lib/notion/api/endpoints.rb +23 -0
  28. data/lib/notion/api/error.rb +6 -0
  29. data/lib/notion/api/errors/internal_error.rb +12 -0
  30. data/lib/notion/api/errors/notion_error.rb +15 -0
  31. data/lib/notion/api/errors/too_many_requests.rb +15 -0
  32. data/lib/notion/api/errors.rb +23 -0
  33. data/lib/notion/client.rb +28 -0
  34. data/lib/notion/config.rb +54 -0
  35. data/lib/notion/faraday/connection.rb +37 -0
  36. data/lib/notion/faraday/request.rb +45 -0
  37. data/lib/notion/faraday/response/raise_error.rb +31 -0
  38. data/lib/notion/faraday/response/wrap_error.rb +22 -0
  39. data/lib/notion/logger.rb +14 -0
  40. data/lib/notion/messages/message.rb +17 -0
  41. data/lib/notion/pagination/cursor.rb +53 -0
  42. data/lib/notion/version.rb +5 -0
  43. data/lib/notion-ruby-client.rb +28 -0
  44. data/lib/notion.rb +2 -0
  45. data/lib/notion_ruby_client.rb +2 -0
  46. data/notionrb.gemspec +31 -0
  47. data/spec/fixtures/notion/block.yml +146 -0
  48. data/spec/fixtures/notion/block_append_children.yml +149 -0
  49. data/spec/fixtures/notion/block_children.yml +152 -0
  50. data/spec/fixtures/notion/create_data_source.yml +29 -0
  51. data/spec/fixtures/notion/create_database.yml +149 -0
  52. data/spec/fixtures/notion/create_discussion_comment.yml +110 -0
  53. data/spec/fixtures/notion/create_page.yml +152 -0
  54. data/spec/fixtures/notion/create_page_comment.yml +110 -0
  55. data/spec/fixtures/notion/create_page_with_data_source.yml +29 -0
  56. data/spec/fixtures/notion/create_page_with_parent_page.yml +128 -0
  57. data/spec/fixtures/notion/data_source.yml +27 -0
  58. data/spec/fixtures/notion/data_source_query.yml +29 -0
  59. data/spec/fixtures/notion/database.yml +150 -0
  60. data/spec/fixtures/notion/database_query.yml +154 -0
  61. data/spec/fixtures/notion/delete_block.yml +145 -0
  62. data/spec/fixtures/notion/page.yml +150 -0
  63. data/spec/fixtures/notion/page_property_item.yml +143 -0
  64. data/spec/fixtures/notion/paginated_block_children.yml +575 -0
  65. data/spec/fixtures/notion/paginated_data_source_query.yml +29 -0
  66. data/spec/fixtures/notion/paginated_database_query.yml +152 -0
  67. data/spec/fixtures/notion/paginated_databases_list.yml +150 -0
  68. data/spec/fixtures/notion/paginated_search.yml +301 -0
  69. data/spec/fixtures/notion/paginated_users_list.yml +146 -0
  70. data/spec/fixtures/notion/retrieve_comments.yml +106 -0
  71. data/spec/fixtures/notion/search.yml +160 -0
  72. data/spec/fixtures/notion/search_with_query.yml +152 -0
  73. data/spec/fixtures/notion/update_block.yml +148 -0
  74. data/spec/fixtures/notion/update_data_source.yml +29 -0
  75. data/spec/fixtures/notion/update_database.yml +152 -0
  76. data/spec/fixtures/notion/update_page.yml +152 -0
  77. data/spec/fixtures/notion/users.yml +145 -0
  78. data/spec/fixtures/notion/users_list.yml +146 -0
  79. data/spec/fixtures/notion/users_me.yml +144 -0
  80. data/spec/notion/api/endpoints/blocks_spec.rb +72 -0
  81. data/spec/notion/api/endpoints/comments_spec.rb +49 -0
  82. data/spec/notion/api/endpoints/data_sources_spec.rb +61 -0
  83. data/spec/notion/api/endpoints/databases_spec.rb +70 -0
  84. data/spec/notion/api/endpoints/pages_spec.rb +127 -0
  85. data/spec/notion/api/endpoints/search_spec.rb +26 -0
  86. data/spec/notion/api/endpoints/users_spec.rb +31 -0
  87. data/spec/notion/config_spec.rb +16 -0
  88. data/spec/notion/pagination/cursor_spec.rb +126 -0
  89. data/spec/notion/version_spec.rb +8 -0
  90. data/spec/spec_helper.rb +22 -0
  91. data/spec/support/token.rb +10 -0
  92. data/spec/support/vcr.rb +16 -0
  93. metadata +358 -0
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ RSpec.describe Notion::Api::Endpoints::Databases do
5
+ let(:client) { Notion::Client.new }
6
+ let(:database_id) { 'dd428e9dd3fe4171870da7a1902c748b' }
7
+ let(:page_id) { 'c7fd1abe811444eabe779632ea33e581' }
8
+ let(:title) do
9
+ [
10
+ {
11
+ "text": {
12
+ "content": 'Orbit 💜 Notion'
13
+ }
14
+ }
15
+ ]
16
+ end
17
+ let(:properties) do
18
+ {
19
+ "Name": {
20
+ "title": {}
21
+ }
22
+ }
23
+ end
24
+
25
+ context 'databases' do
26
+ it 'queries', vcr: { cassette_name: 'database_query' } do
27
+ response = client.database_query(database_id: database_id)
28
+ expect(response.results.length).to be >= 1
29
+ end
30
+
31
+ it 'paginated queries', vcr: { cassette_name: 'paginated_database_query' } do
32
+ pages = []
33
+ client.database_query(database_id: database_id, page_size: 1) do |page|
34
+ pages.concat page.results
35
+ end
36
+ expect(pages.size).to be >= 1
37
+ end
38
+
39
+ it 'creates', vcr: { cassette_name: 'create_database' } do
40
+ response = client.create_database(
41
+ parent: { page_id: page_id },
42
+ title: title,
43
+ properties: properties
44
+ )
45
+ expect(response.title.first.plain_text).to eql 'Orbit 💜 Notion'
46
+ end
47
+
48
+ it 'creates with an explicit initial_data_source', vcr: { cassette_name: 'create_database' } do
49
+ response = client.create_database(
50
+ parent: { page_id: page_id },
51
+ title: title,
52
+ initial_data_source: { properties: properties }
53
+ )
54
+ expect(response.title.first.plain_text).to eql 'Orbit 💜 Notion'
55
+ end
56
+
57
+ it 'updates', vcr: { cassette_name: 'update_database' } do
58
+ response = client.update_database(
59
+ database_id: database_id,
60
+ title: title
61
+ )
62
+ expect(response.title.first.plain_text).to eql 'Orbit 💜 Notion'
63
+ end
64
+
65
+ it 'retrieves', vcr: { cassette_name: 'database' } do
66
+ response = client.database(database_id: database_id)
67
+ expect(response.title.first.plain_text).to eql 'Orbit 💜 Notion'
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ RSpec.describe Notion::Api::Endpoints::Pages do
5
+ let(:client) { Notion::Client.new }
6
+ let(:database_id) { 'dd428e9d-d3fe-4171-870d-a7a1902c748b' }
7
+ let(:page_id) { 'c7fd1abe-8114-44ea-be77-9632ea33e581' }
8
+ let(:property_id) { '%5BIFC' }
9
+ let(:properties) do
10
+ {
11
+ "Name": {
12
+ "title": [
13
+ {
14
+ "text": {
15
+ "content": 'Another Notion page'
16
+ }
17
+ }
18
+ ]
19
+ }
20
+ }
21
+ end
22
+ let(:children) do
23
+ [
24
+ {
25
+ "object": 'block',
26
+ "type": 'heading_2',
27
+ "heading_2": {
28
+ "text": [{ "type": 'text', "text": { "content": 'A heading' } }]
29
+ }
30
+ }
31
+ ]
32
+ end
33
+
34
+ context 'pages' do
35
+ it 'retrieves', vcr: { cassette_name: 'page' } do
36
+ response = client.page(page_id: page_id)
37
+ expect(response.properties.Name.type).to eql 'title'
38
+ expect(response.properties.Name.title.first.plain_text).to eql 'Nicolas Goutay'
39
+ end
40
+
41
+ it 'creates', vcr: { cassette_name: 'create_page' } do
42
+ response = client.create_page(
43
+ parent: { database_id: database_id },
44
+ properties: properties,
45
+ children: children
46
+ )
47
+ expect(response.properties.Name.title.first.plain_text).to eql 'Another Notion page'
48
+ end
49
+
50
+ context 'when creating under a data source' do
51
+ let(:data_source_id) { '6a91c56ebc5e46699e34952e09ec53c0' }
52
+
53
+ it 'creates', vcr: { cassette_name: 'create_page_with_data_source' } do
54
+ response = client.create_page(
55
+ parent: { data_source_id: data_source_id },
56
+ properties: properties,
57
+ children: []
58
+ )
59
+ expect(response.parent.data_source_id).to eql '6a91c56e-bc5e-4669-9e34-952e09ec53c0'
60
+ expect(response.properties.Name.title.first.plain_text).to eql 'Another Notion page'
61
+ end
62
+ end
63
+
64
+ context 'when creating under parent page' do
65
+ let(:parent_page) { '0593a719-ff2e-44aa-a14a-2bf169429284' }
66
+ let(:properties) do
67
+ {
68
+ "title": [
69
+ {
70
+ "text": {
71
+ "content": 'Another Notion page'
72
+ }
73
+ }
74
+ ]
75
+ }
76
+ end
77
+ let(:children) do
78
+ [
79
+ {
80
+ "object": 'block',
81
+ "type": 'heading_2',
82
+ "heading_2": {
83
+ rich_text: [
84
+ {
85
+ type: 'text',
86
+ text: { content: 'My Heading 2' }
87
+ }
88
+ ]
89
+ }
90
+ }
91
+ ]
92
+ end
93
+
94
+ it 'creates', vcr: { cassette_name: 'create_page_with_parent_page' } do
95
+ response = client.create_page(
96
+ parent: { page_id: parent_page },
97
+ properties: properties,
98
+ children: children
99
+ )
100
+ expect(response.parent.page_id).to eql parent_page
101
+ expect(response.properties.title.title.first.plain_text).to eql 'Another Notion page'
102
+ end
103
+ end
104
+
105
+ it 'updates', vcr: { cassette_name: 'update_page' } do
106
+ properties = {
107
+ "Name": [
108
+ {
109
+ "text": {
110
+ "content": 'Nicolas Goutay'
111
+ }
112
+ }
113
+ ]
114
+ }
115
+ response = client.update_page(
116
+ page_id: page_id,
117
+ properties: properties
118
+ )
119
+ expect(response.properties.Name.title.first.plain_text).to eql 'Nicolas Goutay'
120
+ end
121
+
122
+ it 'retrieves a page property item', vcr: { cassette_name: 'page_property_item' } do
123
+ response = client.page_property_item(page_id: page_id, property_id: property_id)
124
+ expect(response.email).to eql 'nicolas@orbit.love'
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ RSpec.describe Notion::Api::Endpoints::Search do
5
+ let(:client) { Notion::Client.new }
6
+
7
+ context 'search' do
8
+ it 'searches', vcr: { cassette_name: 'search' } do
9
+ response = client.search
10
+ expect(response.results.size).to eq 2
11
+ end
12
+
13
+ it 'searches with a query', vcr: { cassette_name: 'search_with_query' } do
14
+ response = client.search(query: 'Nicolas')
15
+ expect(response.results.size).to eq 1
16
+ end
17
+
18
+ it 'paginated search', vcr: { cassette_name: 'paginated_search' } do
19
+ results = []
20
+ client.search(page_size: 2) do |page|
21
+ results.concat page.results
22
+ end
23
+ expect(results.size).to eq 2
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ RSpec.describe Notion::Api::Endpoints::Users do
5
+ let(:client) { Notion::Client.new }
6
+
7
+ context 'users' do
8
+ it 'me', vcr: { cassette_name: 'users_me' } do
9
+ response = client.me
10
+ expect(response.name).to eql 'Notion to Orbit'
11
+ end
12
+
13
+ it 'lists', vcr: { cassette_name: 'users_list' } do
14
+ response = client.users_list
15
+ expect(response.results.size).to be 1
16
+ end
17
+
18
+ it 'paginated list', vcr: { cassette_name: 'paginated_users_list' } do
19
+ members = []
20
+ client.users_list(page_size: 1) do |page|
21
+ members.concat page.results
22
+ end
23
+ expect(members.size).to eq 1
24
+ end
25
+
26
+ it 'retrieves', vcr: { cassette_name: 'users' } do
27
+ response = client.user(user_id: 'a8da8d30-c858-4c3d-87ad-3f2d477bd98d')
28
+ expect(response.name).to eql 'Nicolas Goutay'
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ describe Notion::Config do
5
+ describe '#configure' do
6
+ before do
7
+ Notion.configure do |config|
8
+ config.token = 'a token'
9
+ end
10
+ end
11
+
12
+ it 'sets token' do
13
+ expect(Notion.config.token).to eq 'a token'
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ RSpec.describe Notion::Api::Pagination::Cursor do
5
+ let(:client) { Notion::Client.new }
6
+
7
+ context 'default cursor' do
8
+ let(:cursor) { described_class.new(client, 'users_list', {}) }
9
+
10
+ it 'provides a default page_size' do
11
+ expect(client).to receive(:users_list).with(page_size: 100)
12
+ cursor.first
13
+ end
14
+
15
+ it 'handles blank response metadata' do
16
+ expect(client).to receive(:users_list).once.and_return(Notion::Messages::Message.new)
17
+ cursor.to_a
18
+ end
19
+
20
+ it 'handles nil response metadata' do
21
+ expect(client).to(
22
+ receive(:users_list).once.and_return(Notion::Messages::Message.new(response: nil))
23
+ )
24
+ cursor.to_a
25
+ end
26
+
27
+ it 'paginates with a cursor inside response metadata' do
28
+ expect(client).to receive(:users_list).twice.and_return(
29
+ Notion::Messages::Message.new(has_more: true, next_cursor: 'next'),
30
+ Notion::Messages::Message.new
31
+ )
32
+ expect(cursor).not_to receive(:sleep)
33
+ cursor.to_a
34
+ end
35
+
36
+ context 'with rate limiting' do
37
+ let(:error) { Notion::Api::Errors::TooManyRequests.new({}) }
38
+
39
+ context 'with default max retries' do
40
+ it 'sleeps after a TooManyRequestsError' do
41
+ expect(client).to(
42
+ receive(:users_list)
43
+ .with(page_size: 100)
44
+ .ordered
45
+ .and_return(Notion::Messages::Message.new({ has_more: true, next_cursor: 'next' }))
46
+ )
47
+ expect(client).to(
48
+ receive(:users_list).with(page_size: 100, start_cursor: 'next').and_raise(error)
49
+ )
50
+ expect(cursor).to receive(:sleep).once.ordered.with(10)
51
+ expect(client).to(
52
+ receive(:users_list)
53
+ .with(page_size: 100, start_cursor: 'next')
54
+ .ordered
55
+ .and_return(Notion::Messages::Message.new)
56
+ )
57
+ cursor.to_a
58
+ end
59
+ end
60
+
61
+ context 'with a custom max_retries' do
62
+ let(:cursor) { described_class.new(client, 'users_list', max_retries: 4) }
63
+
64
+ it 'raises the error after hitting the max retries' do
65
+ expect(client).to(
66
+ receive(:users_list)
67
+ .with(page_size: 100)
68
+ .and_return(Notion::Messages::Message.new({ has_more: true, next_cursor: 'next' }))
69
+ )
70
+ expect(client).to(
71
+ receive(:users_list).with(page_size: 100, start_cursor: 'next').exactly(5).times.and_raise(error)
72
+ )
73
+ expect(cursor).to receive(:sleep).exactly(4).times.with(10)
74
+ expect { cursor.to_a }.to raise_error(error)
75
+ end
76
+ end
77
+
78
+ context 'with a custom retry_after' do
79
+ let(:cursor) { described_class.new(client, 'users_list', retry_after: 5) }
80
+
81
+ it 'sleeps for retry_after seconds after a TooManyRequestsError' do
82
+ expect(client).to(
83
+ receive(:users_list)
84
+ .with(page_size: 100)
85
+ .ordered
86
+ .and_return(Notion::Messages::Message.new({ has_more: true, next_cursor: 'next' }))
87
+ )
88
+ expect(client).to(
89
+ receive(:users_list).with(page_size: 100, start_cursor: 'next').ordered.and_raise(error)
90
+ )
91
+ expect(cursor).to receive(:sleep).once.ordered.with(5)
92
+ expect(client).to(
93
+ receive(:users_list)
94
+ .with(page_size: 100, start_cursor: 'next')
95
+ .ordered
96
+ .and_return(Notion::Messages::Message.new)
97
+ )
98
+ cursor.to_a
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ context 'with a custom page_size' do
105
+ let(:cursor) { described_class.new(client, 'users_list', page_size: 42) }
106
+
107
+ it 'overrides default page_size' do
108
+ expect(client).to receive(:users_list).with(page_size: 42)
109
+ cursor.first
110
+ end
111
+ end
112
+
113
+ context 'with a custom sleep_interval' do
114
+ let(:cursor) { described_class.new(client, 'users_list', sleep_interval: 3) }
115
+
116
+ it 'sleeps between requests' do
117
+ expect(client).to receive(:users_list).exactly(3).times.and_return(
118
+ Notion::Messages::Message.new(has_more: true, next_cursor: 'next_a'),
119
+ Notion::Messages::Message.new(has_more: true, next_cursor: 'next_b'),
120
+ Notion::Messages::Message.new
121
+ )
122
+ expect(cursor).to receive(:sleep).with(3).twice
123
+ cursor.to_a
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ describe Notion do
5
+ it 'has a version' do
6
+ expect(Notion::VERSION).not_to be nil
7
+ end
8
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ require 'rubygems'
5
+ require 'rspec'
6
+ require 'timecop'
7
+
8
+ require 'notion_ruby_client'
9
+
10
+
11
+ Dir[File.join(File.dirname(__FILE__), 'support', '**/*.rb')].each do |file|
12
+ require file
13
+ end
14
+
15
+ Notion.configure do |config|
16
+ config.token = ENV['NOTION_API_TOKEN']
17
+ end
18
+
19
+ RSpec.configure do |config|
20
+ # Enable flags like --only-failures and --next-failure
21
+ config.example_status_persistence_file_path = '.rspec_status'
22
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ RSpec.configure do |config|
3
+ config.before do
4
+ @old_token = Notion::Config.token
5
+ end
6
+ config.after do
7
+ Notion::Config.reset
8
+ Notion::Config.token = @old_token
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ require 'vcr'
3
+ require 'webmock/rspec'
4
+
5
+ VCR.configure do |config|
6
+ config.cassette_library_dir = 'spec/fixtures/notion'
7
+ config.hook_into :webmock
8
+ # config.default_cassette_options = { record: :new_episodes }
9
+ config.configure_rspec_metadata!
10
+ config.before_record do |i|
11
+ if ENV.key?('NOTION_API_TOKEN')
12
+ i.request.headers['Authorization'].first.gsub!(ENV['NOTION_API_TOKEN'], '<NOTION_API_TOKEN>')
13
+ end
14
+ i.response.body.force_encoding('UTF-8')
15
+ end
16
+ end