notion-ruby-client 0.0.5 → 0.1.0.pre.beta1

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +1 -0
  3. data/.gitignore +7 -0
  4. data/.rubocop.yml +9 -0
  5. data/CHANGELOG.md +32 -0
  6. data/CODE_OF_CONDUCT.md +128 -0
  7. data/Gemfile.lock +37 -11
  8. data/README.md +318 -47
  9. data/bin/console +31 -0
  10. data/lib/notion/api/endpoints/blocks.rb +35 -7
  11. data/lib/notion/api/endpoints/databases.rb +67 -17
  12. data/lib/notion/api/endpoints/pages.rb +7 -7
  13. data/lib/notion/api/endpoints/search.rb +41 -0
  14. data/lib/notion/api/endpoints/users.rb +6 -3
  15. data/lib/notion/api/endpoints.rb +3 -1
  16. data/lib/notion/api/errors/too_many_requests.rb +15 -0
  17. data/lib/notion/api/errors.rb +0 -2
  18. data/lib/notion/config.rb +2 -0
  19. data/lib/notion/faraday/request.rb +1 -0
  20. data/lib/notion/pagination/cursor.rb +5 -2
  21. data/lib/notion/version.rb +3 -2
  22. data/lib/notion-ruby-client.rb +2 -0
  23. data/notion-ruby-client.gemspec +3 -1
  24. data/spec/fixtures/notion/block.yml +146 -0
  25. data/spec/fixtures/notion/block_append_children.yml +76 -62
  26. data/spec/fixtures/notion/block_children.yml +80 -65
  27. data/spec/fixtures/notion/create_database.yml +149 -0
  28. data/spec/fixtures/notion/create_page.yml +76 -61
  29. data/spec/fixtures/notion/database.yml +78 -61
  30. data/spec/fixtures/notion/database_query.yml +81 -62
  31. data/spec/fixtures/notion/databases_list.yml +77 -60
  32. data/spec/fixtures/notion/page.yml +78 -61
  33. data/spec/fixtures/notion/paginated_block_children.yml +296 -242
  34. data/spec/fixtures/notion/paginated_database_query.yml +79 -62
  35. data/spec/fixtures/notion/paginated_databases_list.yml +78 -61
  36. data/spec/fixtures/notion/paginated_search.yml +301 -0
  37. data/spec/fixtures/notion/paginated_users_list.yml +143 -130
  38. data/spec/fixtures/notion/search.yml +160 -0
  39. data/spec/fixtures/notion/search_with_query.yml +152 -0
  40. data/spec/fixtures/notion/update_block.yml +148 -0
  41. data/spec/fixtures/notion/update_database.yml +152 -0
  42. data/spec/fixtures/notion/update_page.yml +79 -63
  43. data/spec/fixtures/notion/users.yml +69 -56
  44. data/spec/fixtures/notion/users_list.yml +143 -130
  45. data/spec/notion/api/endpoints/blocks_spec.rb +37 -11
  46. data/spec/notion/api/endpoints/databases_spec.rb +43 -9
  47. data/spec/notion/api/endpoints/pages_spec.rb +15 -13
  48. data/spec/notion/api/endpoints/search_spec.rb +26 -0
  49. data/spec/notion/api/endpoints/users_spec.rb +4 -4
  50. data/spec/notion/pagination/cursor_spec.rb +126 -0
  51. metadata +55 -8
  52. data/.rspec_status +0 -18
  53. data/notion-ruby-client-0.0.4.gem +0 -0
  54. data/scratchpad.rb +0 -22
  55. data/screenshots/create_notion_bot.png +0 -0
data/bin/console ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "dotenv/load"
6
+ require "notion-ruby-client"
7
+
8
+ # You can add fixtures and/or initialization code here to make experimenting
9
+ # with your gem easier. You can also use a different console, if you like.
10
+
11
+ # (If you use this, don't forget to add pry to your Gemfile!)
12
+ # require "pry"
13
+ # Pry.start
14
+
15
+ require "irb"
16
+
17
+ def reload!(print = true)
18
+ puts 'Reloading ...' if print
19
+ # Main project directory.
20
+ root_dir = File.expand_path('..', __dir__)
21
+ # Directories within the project that should be reloaded.
22
+ reload_dirs = %w{lib}
23
+ # Loop through and reload every file in all relevant project directories.
24
+ reload_dirs.each do |dir|
25
+ Dir.glob("#{root_dir}/#{dir}/**/*.rb").each { |f| load(f) }
26
+ end
27
+ # Return true when complete.
28
+ true
29
+ end
30
+
31
+ IRB.start(__FILE__)
@@ -4,6 +4,34 @@ module Notion
4
4
  module Api
5
5
  module Endpoints
6
6
  module Blocks
7
+ #
8
+ # Retrieves a Block object using the ID specified.
9
+ #
10
+ # @option options [id] :block_id
11
+ # Block to get children info on.
12
+ def block(options = {})
13
+ throw ArgumentError.new('Required arguments :block_id missing') if options[:block_id].nil?
14
+ get("blocks/#{options[:block_id]}")
15
+ end
16
+
17
+ #
18
+ # Updates the content for the specified block_id based on
19
+ # the block type. Supported fields based on the block object
20
+ # type (see Block object for available fields and the
21
+ # expected input for each field).
22
+ #
23
+ # @option options [id] :block_id
24
+ # Block to get children info on.
25
+ #
26
+ # @option options [string] {type}
27
+ # The block object type value with the properties to be
28
+ # updated. Currently only text (for supported block types)
29
+ # and checked (for to_do blocks) fields can be updated.
30
+ def update_block(options = {})
31
+ throw ArgumentError.new('Required arguments :block_id missing') if options[:block_id].nil?
32
+ patch("blocks/#{options[:block_id]}", options.except(:block_id))
33
+ end
34
+
7
35
  #
8
36
  # Returns a paginated array of Block objects contained in the
9
37
  # block of the requested path using the ID specified.
@@ -14,16 +42,16 @@ module Notion
14
42
  #
15
43
  # Returns a 400 or 429 HTTP response if the request exceeds Notion's Request limits.
16
44
  #
17
- # @option options [id] :id
45
+ # @option options [id] :block_id
18
46
  # Block to get children info on.
19
47
  def block_children(options = {})
20
- throw ArgumentError.new('Required arguments :id missing') if options[:id].nil?
48
+ throw ArgumentError.new('Required arguments :block_id missing') if options[:block_id].nil?
21
49
  if block_given?
22
50
  Pagination::Cursor.new(self, :block_children, options).each do |page|
23
51
  yield page
24
52
  end
25
53
  else
26
- get("blocks/#{options[:id]}/children", options)
54
+ get("blocks/#{options[:block_id]}/children", options.except(:block_id))
27
55
  end
28
56
  end
29
57
 
@@ -38,14 +66,14 @@ module Notion
38
66
  #
39
67
  # Returns a 400 or 429 HTTP response if the request exceeds Notion's Request limits.
40
68
  #
41
- # @option options [id] :id
42
- # Block to get children info on.
69
+ # @option options [id] :block_id
70
+ # Block to append children to.
43
71
  #
44
72
  # @option options [[Object]] :children
45
73
  # Children blocks to append
46
74
  def block_append_children(options = {})
47
- throw ArgumentError.new('Required arguments :id missing') if options[:id].nil?
48
- patch("blocks/#{options[:id]}/children", options)
75
+ throw ArgumentError.new('Required arguments :block_id missing') if options[:block_id].nil?
76
+ patch("blocks/#{options[:block_id]}/children", options.except(:block_id))
49
77
  end
50
78
  end
51
79
  end
@@ -4,20 +4,6 @@ module Notion
4
4
  module Api
5
5
  module Endpoints
6
6
  module Databases
7
- #
8
- # Retrieves a Database object using the ID specified in the request.
9
- #
10
- # Returns a 404 HTTP response if the database doesn't exist, or if the bot
11
- # doesn't have access to the database. Returns a 429 HTTP response if the
12
- # request exceeds Notion's Request limits.
13
- #
14
- # @option options [id] :id
15
- # Database to get info on.
16
- def database(options = {})
17
- throw ArgumentError.new('Required arguments :id missing') if options[:id].nil?
18
- get("databases/#{options[:id]}")
19
- end
20
-
21
7
  #
22
8
  # Gets a paginated array of Page object s contained in the requested database,
23
9
  # filtered and ordered according to the filter and sort objects provided in the request.
@@ -30,7 +16,7 @@ module Notion
30
16
  # database properties and can be combined. The order of the sorts in the request
31
17
  # matter, with earlier sorts taking precedence over later ones.
32
18
  #
33
- # @option options [id] :id
19
+ # @option options [id] :database_id
34
20
  # Database to query.
35
21
  #
36
22
  # @option options [Object] :filter
@@ -44,17 +30,78 @@ module Notion
44
30
  # to a start_cursor attribute returned by a previous request's next_cursor.
45
31
  # Default value fetches the first "page" of the collection.
46
32
  # See pagination for more detail.
33
+ #
34
+ # @option options [integer] :page_size
35
+ # The number of items from the full list desired in the response. Maximum: 100
47
36
  def database_query(options = {})
48
- throw ArgumentError.new('Required arguments :id missing') if options[:id].nil?
37
+ throw ArgumentError.new('Required arguments :database_id missing') if options[:database_id].nil?
49
38
  if block_given?
50
39
  Pagination::Cursor.new(self, :database_query, options).each do |page|
51
40
  yield page
52
41
  end
53
42
  else
54
- post("databases/#{options[:id]}/query", options)
43
+ post("databases/#{options[:database_id]}/query", options.except(:database_id))
55
44
  end
56
45
  end
57
46
 
47
+ #
48
+ # Creates a new database in the specified page.
49
+ #
50
+ # @option options [Object] :parent
51
+ # Parent of the database, which is always going to be a page.
52
+ #
53
+ # @option options [Object] :title
54
+ # Title of this database.
55
+ #
56
+ # @option options [Object] :properties
57
+ # Property schema of database.
58
+ # The keys are the names of properties as they appear in Notion and the values are
59
+ # property schema objects. Property Schema Object is a metadata that controls
60
+ # how a database property behaves, e.g. {"checkbox": {}}.
61
+ # Each database must have exactly one database property schema object of type "title".
62
+ def create_database(options = {})
63
+ throw ArgumentError.new('Required arguments :parent.page_id missing') if options.dig(:parent, :page_id).nil?
64
+ throw ArgumentError.new('Required arguments :title missing') if options.dig(:title).nil?
65
+ throw ArgumentError.new('Required arguments :properties missing') if options.dig(:properties).nil?
66
+ post('databases', options)
67
+ end
68
+
69
+ #
70
+ # Updates an existing database as specified by the parameters.
71
+ #
72
+ # @option options [id] :database_id
73
+ # Database to update.
74
+ #
75
+ # @option options [Object] :title
76
+ # Title of database as it appears in Notion. An array of rich text objects.
77
+ # If omitted, the database title will remain unchanged.
78
+ #
79
+ # @option options [Object] :properties
80
+ # Updates to the property schema of a database.
81
+ # If updating an existing property, the keys are the names or IDs
82
+ # of the properties as they appear in Notion and the values
83
+ # are property schema objects. If adding a new property, the key is
84
+ # the name of the database property and the value is a property schema object.
85
+ #
86
+ def update_database(options = {})
87
+ throw ArgumentError.new('Required arguments :database_id missing') if options.dig(:database_id).nil?
88
+ patch("databases/#{options[:database_id]}", options.except(:database_id))
89
+ end
90
+
91
+ #
92
+ # Retrieves a Database object using the ID specified in the request.
93
+ #
94
+ # Returns a 404 HTTP response if the database doesn't exist, or if the bot
95
+ # doesn't have access to the database. Returns a 429 HTTP response if the
96
+ # request exceeds Notion's Request limits.
97
+ #
98
+ # @option options [id] :database_id
99
+ # Database to get info on.
100
+ def database(options = {})
101
+ throw ArgumentError.new('Required arguments :database_id missing') if options[:database_id].nil?
102
+ get("databases/#{options[:database_id]}")
103
+ end
104
+
58
105
  #
59
106
  # Returns a paginated list of Databases objects for the workspace.
60
107
  #
@@ -63,6 +110,9 @@ module Notion
63
110
  # to a start_cursor attribute returned by a previous request's next_cursor.
64
111
  # Default value fetches the first "page" of the collection.
65
112
  # See pagination for more detail.
113
+ #
114
+ # @option options [integer] :page_size
115
+ # The number of items from the full list desired in the response. Maximum: 100
66
116
  def databases_list(options = {})
67
117
  if block_given?
68
118
  Pagination::Cursor.new(self, :databases_list, options).each do |page|
@@ -8,15 +8,15 @@ module Notion
8
8
  # Retrieves a 📄Page object using the ID specified in the request path.
9
9
  # Note that this version of the API only exposes page properties, not page content
10
10
  #
11
- # @option options [id] :id
11
+ # @option options [id] :page_id
12
12
  # Page to get info on.
13
13
  #
14
14
  # @option options [bool] :archived
15
15
  # Set to true to retrieve an archived page; must be false or omitted to
16
16
  # retrieve a page that has not been archived. Defaults to false.
17
17
  def page(options = {})
18
- throw ArgumentError.new('Required arguments :id missing') if options[:id].nil?
19
- get("pages/#{options[:id]}?archived=#{options[:archived]}")
18
+ throw ArgumentError.new('Required arguments :page_id missing') if options[:page_id].nil?
19
+ get("pages/#{options[:page_id]}")
20
20
  end
21
21
 
22
22
  #
@@ -36,7 +36,7 @@ module Notion
36
36
  # specific to the property type, e.g. {"checkbox": true}.
37
37
  #
38
38
  # @option options [Object] :children
39
- # An optional array of Block objects representing the Page’s conent
39
+ # An optional array of Block objects representing the Page’s content
40
40
  def create_page(options = {})
41
41
  throw ArgumentError.new('Required arguments :parent.database_id missing') if options.dig(:parent, :database_id).nil?
42
42
  post("pages", options)
@@ -50,7 +50,7 @@ module Notion
50
50
  # Note that this iteration of the API will only expose page properties, not page
51
51
  # content, as described in the data model.
52
52
  #
53
- # @option options [id] :id
53
+ # @option options [id] :page_id
54
54
  # Page to get info on.
55
55
  #
56
56
  # @option options [Object] :properties
@@ -60,8 +60,8 @@ module Notion
60
60
  # appears in Notion, or property ID. value object Object containing a value
61
61
  # specific to the property type, e.g. {"checkbox": true}.
62
62
  def update_page(options = {})
63
- throw ArgumentError.new('Required arguments :id missing') if options[:id].nil?
64
- patch("pages/#{options[:id]}", options)
63
+ throw ArgumentError.new('Required arguments :page_id missing') if options[:page_id].nil?
64
+ patch("pages/#{options[:page_id]}", options.except(:page_id))
65
65
  end
66
66
  end
67
67
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Notion
4
+ module Api
5
+ module Endpoints
6
+ module Search
7
+ #
8
+ # Searches all pages and child pages that are shared with the integration.
9
+ # The results may include databases.
10
+ #
11
+ # @option options [string] :query
12
+ # When supplied, limits which pages are returned by comparing the query to the page title.
13
+ #
14
+ # @option options [Object] :filter
15
+ # When supplied, filters the results based on the provided criteria.
16
+ #
17
+ # @option options [[Object]] :sorts
18
+ # When supplied, sorts the results based on the provided criteria.
19
+ # Limitation: Currently only a single sort is allowed and is limited to last_edited_time.
20
+ #
21
+ # @option options [UUID] :start_cursor
22
+ # Paginate through collections of data by setting the cursor parameter
23
+ # to a start_cursor attribute returned by a previous request's next_cursor.
24
+ # Default value fetches the first "page" of the collection.
25
+ # See pagination for more detail.
26
+ #
27
+ # @option options [integer] :page_size
28
+ # The number of items from the full list desired in the response. Maximum: 100
29
+ def search(options = {})
30
+ if block_given?
31
+ Pagination::Cursor.new(self, :search, options).each do |page|
32
+ yield page
33
+ end
34
+ else
35
+ post('search', options)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -7,11 +7,11 @@ module Notion
7
7
  #
8
8
  # Retrieves a User object using the ID specified in the request.
9
9
  #
10
- # @option options [id] :id
10
+ # @option options [id] :user_id
11
11
  # User to get info on.
12
12
  def user(options = {})
13
- throw ArgumentError.new('Required arguments :id missing') if options[:id].nil?
14
- get("users/#{options[:id]}")
13
+ throw ArgumentError.new('Required arguments :user_id missing') if options[:user_id].nil?
14
+ get("users/#{options[:user_id]}")
15
15
  end
16
16
 
17
17
  #
@@ -22,6 +22,9 @@ module Notion
22
22
  # to a start_cursor attribute returned by a previous request's next_cursor.
23
23
  # Default value fetches the first "page" of the collection.
24
24
  # See pagination for more detail.
25
+ #
26
+ # @option options [integer] :page_size
27
+ # The number of items from the full list desired in the response. Maximum: 100
25
28
  def users_list(options = {})
26
29
  if block_given?
27
30
  Pagination::Cursor.new(self, :users_list, options).each do |page|
@@ -4,6 +4,7 @@ require_relative 'endpoints/blocks'
4
4
  require_relative 'endpoints/databases'
5
5
  require_relative 'endpoints/pages'
6
6
  require_relative 'endpoints/users'
7
+ require_relative 'endpoints/search'
7
8
 
8
9
  module Notion
9
10
  module Api
@@ -12,6 +13,7 @@ module Notion
12
13
  include Databases
13
14
  include Pages
14
15
  include Users
16
+ include Search
15
17
  end
16
18
  end
17
- end
19
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module Notion
3
+ module Api
4
+ module Errors
5
+ class TooManyRequests < ::Faraday::Error
6
+ attr_reader :response
7
+
8
+ def initialize(response)
9
+ @response = response
10
+ super 'Too many requests'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -8,7 +8,6 @@ module Notion
8
8
  class InternalError < NotionError; end
9
9
  class InvalidRequest < NotionError; end
10
10
  class ObjectNotFound < NotionError; end
11
- class TooManyRequests < NotionError; end
12
11
  class Unauthorized < NotionError; end
13
12
 
14
13
  ERROR_CLASSES = {
@@ -17,7 +16,6 @@ module Notion
17
16
  'internal_error' => InternalError,
18
17
  'invalid_request' => InvalidRequest,
19
18
  'object_not_found' => ObjectNotFound,
20
- 'rate_limited' => TooManyRequests,
21
19
  'unauthorized' => Unauthorized
22
20
  }.freeze
23
21
  end
data/lib/notion/config.rb CHANGED
@@ -15,6 +15,7 @@ module Notion
15
15
  open_timeout
16
16
  default_page_size
17
17
  default_max_retries
18
+ default_retry_after
18
19
  adapter
19
20
  ].freeze
20
21
 
@@ -32,6 +33,7 @@ module Notion
32
33
  self.open_timeout = nil
33
34
  self.default_page_size = 100
34
35
  self.default_max_retries = 100
36
+ self.default_retry_after = 10
35
37
  self.adapter = ::Faraday.default_adapter
36
38
  end
37
39
 
@@ -27,6 +27,7 @@ module Notion
27
27
  def request(method, path, options)
28
28
  response = connection.send(method) do |request|
29
29
  request.headers['Authorization'] = "Bearer #{token}"
30
+ request.headers['Notion-Version'] = Notion::NOTION_REQUEST_VERSION
30
31
  case method
31
32
  when :get, :delete
32
33
  request.url(path, options)
@@ -9,6 +9,7 @@ module Notion
9
9
  attr_reader :verb
10
10
  attr_reader :sleep_interval
11
11
  attr_reader :max_retries
12
+ attr_reader :retry_after
12
13
  attr_reader :params
13
14
 
14
15
  def initialize(client, verb, params = {})
@@ -17,13 +18,15 @@ module Notion
17
18
  @params = params.dup
18
19
  @sleep_interval = @params.delete(:sleep_interval)
19
20
  @max_retries = @params.delete(:max_retries) || client.default_max_retries
21
+ @retry_after = @params.delete(:retry_after) || client.default_retry_after
20
22
  end
21
23
 
22
24
  def each
23
25
  next_cursor = nil
24
26
  retry_count = 0
25
27
  loop do
26
- query = next_cursor.nil? ? params : params.merge(start_cursor: next_cursor)
28
+ query = { page_size: client.default_page_size }.merge(params)
29
+ query = query.merge(start_cursor: next_cursor) unless next_cursor.nil?
27
30
  begin
28
31
  response = client.send(verb, query)
29
32
  rescue Notion::Api::Errors::TooManyRequests => e
@@ -31,7 +34,7 @@ module Notion
31
34
 
32
35
  client.logger.debug("#{self.class}##{__method__}") { e.to_s }
33
36
  retry_count += 1
34
- sleep(e.retry_after)
37
+ sleep(retry_after)
35
38
  next
36
39
  end
37
40
  yield response