notion-ruby-client 0.0.1 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +14 -0
  3. data/.rspec +2 -0
  4. data/.rspec_status +18 -0
  5. data/.rubocop.yml +46 -0
  6. data/.rubocop_todo.yml +86 -0
  7. data/CHANGELOG.md +26 -0
  8. data/CODE_OF_CONDUCT.md +128 -0
  9. data/Gemfile.lock +95 -0
  10. data/README.md +192 -3
  11. data/Rakefile +18 -0
  12. data/lib/notion-ruby-client.rb +6 -8
  13. data/lib/notion/api/endpoints.rb +6 -0
  14. data/lib/notion/api/endpoints/blocks.rb +53 -0
  15. data/lib/notion/api/endpoints/databases.rb +78 -0
  16. data/lib/notion/api/endpoints/pages.rb +69 -0
  17. data/lib/notion/api/endpoints/users.rb +7 -3
  18. data/lib/notion/api/errors.rb +7 -1
  19. data/lib/notion/api/errors/notion_error.rb +2 -2
  20. data/lib/notion/config.rb +3 -1
  21. data/lib/notion/faraday/request.rb +8 -2
  22. data/lib/notion/faraday/response/raise_error.rb +3 -4
  23. data/lib/notion/pagination/cursor.rb +2 -2
  24. data/lib/notion/version.rb +3 -2
  25. data/notion-ruby-client-0.0.4.gem +0 -0
  26. data/spec/fixtures/notion/block_append_children.yml +135 -0
  27. data/spec/fixtures/notion/block_children.yml +137 -0
  28. data/spec/fixtures/notion/create_page.yml +137 -0
  29. data/spec/fixtures/notion/database.yml +133 -0
  30. data/spec/fixtures/notion/database_query.yml +135 -0
  31. data/spec/fixtures/notion/databases_list.yml +133 -0
  32. data/spec/fixtures/notion/page.yml +133 -0
  33. data/spec/fixtures/notion/paginated_block_children.yml +521 -0
  34. data/spec/fixtures/notion/paginated_database_query.yml +135 -0
  35. data/spec/fixtures/notion/paginated_databases_list.yml +133 -0
  36. data/spec/fixtures/notion/paginated_users_list.yml +133 -0
  37. data/spec/fixtures/notion/update_page.yml +136 -0
  38. data/spec/fixtures/notion/users.yml +132 -0
  39. data/spec/fixtures/notion/users_list.yml +133 -0
  40. data/spec/notion/api/endpoints/blocks_spec.rb +40 -0
  41. data/spec/notion/api/endpoints/databases_spec.rb +40 -0
  42. data/spec/notion/api/endpoints/pages_spec.rb +64 -0
  43. data/spec/notion/api/endpoints/users_spec.rb +26 -0
  44. data/spec/notion/config_spec.rb +16 -0
  45. data/spec/notion/version_spec.rb +8 -0
  46. data/spec/spec_helper.rb +22 -0
  47. data/spec/support/token.rb +10 -0
  48. data/spec/support/vcr.rb +16 -0
  49. metadata +67 -8
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ require 'bundler/gem_tasks'
5
+
6
+ Bundler.setup :default, :development
7
+
8
+ require 'rspec/core'
9
+ require 'rspec/core/rake_task'
10
+
11
+ RSpec::Core::RakeTask.new(:spec) do |spec|
12
+ spec.pattern = FileList['spec/**/*_spec.rb']
13
+ end
14
+
15
+ require 'rubocop/rake_task'
16
+ RuboCop::RakeTask.new
17
+
18
+ task default: %i[spec rubocop]
@@ -1,18 +1,16 @@
1
1
  # frozen_string_literal: true
2
+ require 'faraday'
3
+ require 'faraday_middleware'
4
+ require 'json'
5
+ require 'logger'
6
+ require 'hashie'
7
+
2
8
  require_relative 'notion/version'
3
9
  require_relative 'notion/logger'
4
10
  require_relative 'notion/config'
5
11
 
6
12
  # Messages
7
- require 'hashie'
8
13
  require_relative 'notion/messages/message'
9
- require_relative 'notion/messages/formatting'
10
-
11
- # Web API
12
- require 'faraday'
13
- require 'faraday_middleware'
14
- require 'json'
15
- require 'logger'
16
14
 
17
15
  require_relative 'notion/config'
18
16
  require_relative 'notion/api/errors/notion_error'
@@ -1,10 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'endpoints/blocks'
4
+ require_relative 'endpoints/databases'
5
+ require_relative 'endpoints/pages'
3
6
  require_relative 'endpoints/users'
4
7
 
5
8
  module Notion
6
9
  module Api
7
10
  module Endpoints
11
+ include Blocks
12
+ include Databases
13
+ include Pages
8
14
  include Users
9
15
  end
10
16
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Notion
4
+ module Api
5
+ module Endpoints
6
+ module Blocks
7
+ #
8
+ # Returns a paginated array of Block objects contained in the
9
+ # block of the requested path using the ID specified.
10
+ #
11
+ # Returns a 404 HTTP response if any of the following are true:
12
+ # - the ID does not exist
13
+ # - the bot doesn't have access to the block with the given ID
14
+ #
15
+ # Returns a 400 or 429 HTTP response if the request exceeds Notion's Request limits.
16
+ #
17
+ # @option options [id] :id
18
+ # Block to get children info on.
19
+ def block_children(options = {})
20
+ throw ArgumentError.new('Required arguments :id missing') if options[:id].nil?
21
+ if block_given?
22
+ Pagination::Cursor.new(self, :block_children, options).each do |page|
23
+ yield page
24
+ end
25
+ else
26
+ get("blocks/#{options[:id]}/children", options)
27
+ end
28
+ end
29
+
30
+ #
31
+ # Creates and appends new children blocks to the parent block
32
+ # in the requested path using the ID specified. Returns the Block
33
+ # object being appended to.
34
+ #
35
+ # Returns a 404 HTTP response if any of the following are true:
36
+ # - the ID does not exist
37
+ # - the bot doesn't have access to the block with the given ID
38
+ #
39
+ # Returns a 400 or 429 HTTP response if the request exceeds Notion's Request limits.
40
+ #
41
+ # @option options [id] :id
42
+ # Block to get children info on.
43
+ #
44
+ # @option options [[Object]] :children
45
+ # Children blocks to append
46
+ def block_append_children(options = {})
47
+ throw ArgumentError.new('Required arguments :id missing') if options[:id].nil?
48
+ patch("blocks/#{options[:id]}/children", options)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Notion
4
+ module Api
5
+ module Endpoints
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
+ #
22
+ # Gets a paginated array of Page object s contained in the requested database,
23
+ # filtered and ordered according to the filter and sort objects provided in the request.
24
+ #
25
+ # Filters are similar to the filters provided in the Notion UI. Filters operate
26
+ # on database properties and can be combined. If no filter is provided, all the
27
+ # pages in the database will be returned with pagination.
28
+ #
29
+ # Sorts are similar to the sorts provided in the Notion UI. Sorts operate on
30
+ # database properties and can be combined. The order of the sorts in the request
31
+ # matter, with earlier sorts taking precedence over later ones.
32
+ #
33
+ # @option options [id] :id
34
+ # Database to query.
35
+ #
36
+ # @option options [Object] :filter
37
+ # When supplied, limits which pages are returned based on the provided criteria.
38
+ #
39
+ # @option options [[Object]] :sorts
40
+ # When supplied, sorts the results based on the provided criteria.
41
+ #
42
+ # @option options [UUID] :start_cursor
43
+ # Paginate through collections of data by setting the cursor parameter
44
+ # to a start_cursor attribute returned by a previous request's next_cursor.
45
+ # Default value fetches the first "page" of the collection.
46
+ # See pagination for more detail.
47
+ def database_query(options = {})
48
+ throw ArgumentError.new('Required arguments :id missing') if options[:id].nil?
49
+ if block_given?
50
+ Pagination::Cursor.new(self, :database_query, options).each do |page|
51
+ yield page
52
+ end
53
+ else
54
+ post("databases/#{options[:id]}/query", options)
55
+ end
56
+ end
57
+
58
+ #
59
+ # Returns a paginated list of Databases objects for the workspace.
60
+ #
61
+ # @option options [UUID] :start_cursor
62
+ # Paginate through collections of data by setting the cursor parameter
63
+ # to a start_cursor attribute returned by a previous request's next_cursor.
64
+ # Default value fetches the first "page" of the collection.
65
+ # See pagination for more detail.
66
+ def databases_list(options = {})
67
+ if block_given?
68
+ Pagination::Cursor.new(self, :databases_list, options).each do |page|
69
+ yield page
70
+ end
71
+ else
72
+ get('databases', options)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Notion
4
+ module Api
5
+ module Endpoints
6
+ module Pages
7
+ #
8
+ # Retrieves a 📄Page object using the ID specified in the request path.
9
+ # Note that this version of the API only exposes page properties, not page content
10
+ #
11
+ # @option options [id] :id
12
+ # Page to get info on.
13
+ #
14
+ # @option options [bool] :archived
15
+ # Set to true to retrieve an archived page; must be false or omitted to
16
+ # retrieve a page that has not been archived. Defaults to false.
17
+ def page(options = {})
18
+ throw ArgumentError.new('Required arguments :id missing') if options[:id].nil?
19
+ get("pages/#{options[:id]}?archived=#{options[:archived]}")
20
+ end
21
+
22
+ #
23
+ # Creates a new page in the specified database.
24
+ # Later iterations of the API will support creating pages outside databases.
25
+ # Note that this iteration of the API will only expose page properties, not
26
+ # page content, as described in the data model.
27
+ #
28
+ # @option options [Object] :parent
29
+ # Parent of the page, which is always going to be a database in this version of the API.
30
+ #
31
+ # @option options [Object] :properties
32
+ # Properties of this page.
33
+ # The schema for the page's keys and values is described by the properties of
34
+ # the database this page belongs to. key string Name of a property as it
35
+ # appears in Notion, or property ID. value object Object containing a value
36
+ # specific to the property type, e.g. {"checkbox": true}.
37
+ #
38
+ # @option options [Object] :children
39
+ # An optional array of Block objects representing the Page’s conent
40
+ def create_page(options = {})
41
+ throw ArgumentError.new('Required arguments :parent.database_id missing') if options.dig(:parent, :database_id).nil?
42
+ post("pages", options)
43
+ end
44
+
45
+ #
46
+ # Updates a page by setting the values of any properties specified in the
47
+ # JSON body of the request. Properties that are not set via parameters will
48
+ # remain unchanged.
49
+ #
50
+ # Note that this iteration of the API will only expose page properties, not page
51
+ # content, as described in the data model.
52
+ #
53
+ # @option options [id] :id
54
+ # Page to get info on.
55
+ #
56
+ # @option options [Object] :properties
57
+ # Properties of this page.
58
+ # The schema for the page's keys and values is described by the properties of
59
+ # the database this page belongs to. key string Name of a property as it
60
+ # appears in Notion, or property ID. value object Object containing a value
61
+ # specific to the property type, e.g. {"checkbox": true}.
62
+ def update_page(options = {})
63
+ throw ArgumentError.new('Required arguments :id missing') if options[:id].nil?
64
+ patch("pages/#{options[:id]}", options)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -16,15 +16,19 @@ module Notion
16
16
 
17
17
  #
18
18
  # Returns a paginated list of User objects for the workspace.
19
- # @option options [Object] :start_cursor
20
- # Paginate through collections of data by setting the cursor parameter to a start_cursor attribute returned by a previous request's next_cursor. Default value fetches the first "page" of the collection. See pagination for more detail.
19
+ #
20
+ # @option options [UUID] :start_cursor
21
+ # Paginate through collections of data by setting the cursor parameter
22
+ # to a start_cursor attribute returned by a previous request's next_cursor.
23
+ # Default value fetches the first "page" of the collection.
24
+ # See pagination for more detail.
21
25
  def users_list(options = {})
22
26
  if block_given?
23
27
  Pagination::Cursor.new(self, :users_list, options).each do |page|
24
28
  yield page
25
29
  end
26
30
  else
27
- get("users?start_cursor=#{options[:start_cursor]}")
31
+ get("users", options)
28
32
  end
29
33
  end
30
34
  end
@@ -3,16 +3,22 @@
3
3
  module Notion
4
4
  module Api
5
5
  module Errors
6
+ class BadRequest < NotionError; end
7
+ class Forbidden < NotionError; end
6
8
  class InternalError < NotionError; end
7
9
  class InvalidRequest < NotionError; end
8
10
  class ObjectNotFound < NotionError; end
9
11
  class TooManyRequests < NotionError; end
12
+ class Unauthorized < NotionError; end
10
13
 
11
14
  ERROR_CLASSES = {
15
+ 'bad_request' => BadRequest,
16
+ 'forbidden' => Forbidden,
12
17
  'internal_error' => InternalError,
13
18
  'invalid_request' => InvalidRequest,
14
19
  'object_not_found' => ObjectNotFound,
15
- 'rate_limited' => TooManyRequests
20
+ 'rate_limited' => TooManyRequests,
21
+ 'unauthorized' => Unauthorized
16
22
  }.freeze
17
23
  end
18
24
  end
@@ -5,8 +5,8 @@ module Notion
5
5
  class NotionError < ::Faraday::Error
6
6
  attr_reader :response
7
7
 
8
- def initialize(message, response = nil)
9
- super message
8
+ def initialize(message, details, response = nil)
9
+ super("#{message} #{details}")
10
10
  @response = response
11
11
  end
12
12
  end
data/lib/notion/config.rb CHANGED
@@ -47,4 +47,6 @@ module Notion
47
47
  Config
48
48
  end
49
49
  end
50
- end
50
+ end
51
+
52
+ Notion::Config.reset
@@ -6,6 +6,10 @@ module Notion
6
6
  request(:get, path, options)
7
7
  end
8
8
 
9
+ def patch(path, options = {})
10
+ request(:patch, path, options)
11
+ end
12
+
9
13
  def post(path, options = {})
10
14
  request(:post, path, options)
11
15
  end
@@ -23,12 +27,14 @@ module Notion
23
27
  def request(method, path, options)
24
28
  response = connection.send(method) do |request|
25
29
  request.headers['Authorization'] = "Bearer #{token}"
30
+ request.headers['Notion-Version'] = Notion::NOTION_REQUEST_VERSION
26
31
  case method
27
32
  when :get, :delete
28
33
  request.url(path, options)
29
- when :post, :put
34
+ when :post, :put, :patch
35
+ request.headers['Content-Type'] = 'application/json'
30
36
  request.path = path
31
- request.body = options unless options.empty?
37
+ request.body = options.to_json unless options.empty?
32
38
  end
33
39
  request.options.merge!(options.delete(:request)) if options.key?(:request)
34
40
  end
@@ -6,19 +6,18 @@ module Notion
6
6
  def on_complete(env)
7
7
  raise Notion::Api::Errors::TooManyRequests, env.response if env.status == 429
8
8
 
9
- return unless env.success?
9
+ return if env.success?
10
10
 
11
11
  body = env.body
12
12
  return unless body
13
- return if env.status == 200
14
13
 
15
14
  error_code = body['code']
16
15
  error_message = body['message']
17
- error_message = body['details']
16
+ error_details = body['details']
18
17
 
19
18
  error_class = Notion::Api::Errors::ERROR_CLASSES[error_code]
20
19
  error_class ||= Notion::Api::Errors::NotionError
21
- raise error_class.new(error_message, env.response)
20
+ raise error_class.new(error_message, error_details, env.response)
22
21
  end
23
22
 
24
23
  def call(env)
@@ -23,10 +23,10 @@ module Notion
23
23
  next_cursor = nil
24
24
  retry_count = 0
25
25
  loop do
26
- query = { limit: client.default_page_size }.merge(params).merge(start_cursor: next_cursor)
26
+ query = next_cursor.nil? ? params : params.merge(start_cursor: next_cursor)
27
27
  begin
28
28
  response = client.send(verb, query)
29
- rescue Notion::Api::Errors::TooManyRequestsError => e
29
+ rescue Notion::Api::Errors::TooManyRequests => e
30
30
  raise e if retry_count >= max_retries
31
31
 
32
32
  client.logger.debug("#{self.class}##{__method__}") { e.to_s }
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module Notion
3
- VERSION = '0.0.1'
4
- end
3
+ VERSION = '0.0.6'
4
+ NOTION_REQUEST_VERSION = '2021-05-11'
5
+ end
Binary file
@@ -0,0 +1,135 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: patch
5
+ uri: https://api.notion.com/v1/blocks/723578f1-6e51-450c-8ee1-09158c19fd4c/children
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"id":"723578f1-6e51-450c-8ee1-09158c19fd4c","children":[{"object":"block","type":"heading_2","heading_2":{"text":[{"type":"text","text":{"content":"Another
9
+ Heading"}}]}}]}'
10
+ headers:
11
+ Accept:
12
+ - application/json; charset=utf-8
13
+ User-Agent:
14
+ - Notion Ruby Client/0.0.4
15
+ Authorization:
16
+ - Bearer <NOTION_API_TOKEN>
17
+ Content-Type:
18
+ - application/json
19
+ Accept-Encoding:
20
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
21
+ response:
22
+ status:
23
+ code: 200
24
+ message: OK
25
+ headers:
26
+ Date:
27
+ - Sun, 25 Apr 2021 21:05:49 GMT
28
+ Content-Type:
29
+ - application/json; charset=utf-8
30
+ Transfer-Encoding:
31
+ - chunked
32
+ Connection:
33
+ - keep-alive
34
+ Set-Cookie:
35
+ - __cfduid=d34e3fa5c4766df6ac5561da522624b761619384749; expires=Tue, 25-May-21
36
+ 21:05:49 GMT; path=/; domain=.notion.com; HttpOnly; SameSite=Lax
37
+ - notion_browser_id=e006545d-4f97-4497-8d9c-491386cb6d2a; Domain=www.notion.so;
38
+ Path=/; Expires=Wed, 01 Jan 2053 22:52:29 GMT; Secure
39
+ X-Dns-Prefetch-Control:
40
+ - 'off'
41
+ X-Frame-Options:
42
+ - SAMEORIGIN
43
+ Strict-Transport-Security:
44
+ - max-age=5184000; includeSubDomains
45
+ X-Download-Options:
46
+ - noopen
47
+ X-Content-Type-Options:
48
+ - nosniff
49
+ X-Xss-Protection:
50
+ - 1; mode=block
51
+ Referrer-Policy:
52
+ - same-origin
53
+ Content-Security-Policy:
54
+ - 'script-src ''self'' ''unsafe-inline'' ''unsafe-eval'' https://gist.github.com
55
+ https://apis.google.com https://api.amplitude.com https://widget.intercom.io
56
+ https://js.intercomcdn.com https://logs-01.loggly.com https://cdn.segment.com
57
+ https://analytics.pgncs.notion.so https://checkout.stripe.com https://js.stripe.com/v3
58
+ https://embed.typeform.com https://admin.typeform.com https://platform.twitter.com
59
+ https://cdn.syndication.twimg.com https://www.googletagmanager.com https://x.clearbitjs.com;
60
+ connect-src ''self'' https://msgstore.www.notion.so wss://msgstore.www.notion.so
61
+ ws://localhost:* https://notion-emojis.s3-us-west-2.amazonaws.com https://s3-us-west-2.amazonaws.com
62
+ https://s3.us-west-2.amazonaws.com https://notion-production-snapshots-2.s3.us-west-2.amazonaws.com
63
+ https: http: https://api.amplitude.com https://api.embed.ly https://js.intercomcdn.com
64
+ https://api-iam.intercom.io wss://nexus-websocket-a.intercom.io https://logs-01.loggly.com
65
+ https://api.segment.io https://api.pgncs.notion.so https://checkout.stripe.com
66
+ https://js.stripe.com/v3 https://cdn.contentful.com https://preview.contentful.com
67
+ https://images.ctfassets.net https://api.unsplash.com https://boards-api.greenhouse.io;
68
+ font-src ''self'' data: https://cdnjs.cloudflare.com https://js.intercomcdn.com;
69
+ img-src ''self'' data: blob: https: https://platform.twitter.com https://syndication.twitter.com
70
+ https://pbs.twimg.com https://ton.twimg.com www.googletagmanager.com; style-src
71
+ ''self'' ''unsafe-inline'' https://cdnjs.cloudflare.com https://github.githubassets.com
72
+ https://platform.twitter.com https://ton.twimg.com; frame-src https: http:;
73
+ media-src https: http:'
74
+ X-Content-Security-Policy:
75
+ - 'script-src ''self'' ''unsafe-inline'' ''unsafe-eval'' https://gist.github.com
76
+ https://apis.google.com https://api.amplitude.com https://widget.intercom.io
77
+ https://js.intercomcdn.com https://logs-01.loggly.com https://cdn.segment.com
78
+ https://analytics.pgncs.notion.so https://checkout.stripe.com https://js.stripe.com/v3
79
+ https://embed.typeform.com https://admin.typeform.com https://platform.twitter.com
80
+ https://cdn.syndication.twimg.com https://www.googletagmanager.com https://x.clearbitjs.com;
81
+ connect-src ''self'' https://msgstore.www.notion.so wss://msgstore.www.notion.so
82
+ ws://localhost:* https://notion-emojis.s3-us-west-2.amazonaws.com https://s3-us-west-2.amazonaws.com
83
+ https://s3.us-west-2.amazonaws.com https://notion-production-snapshots-2.s3.us-west-2.amazonaws.com
84
+ https: http: https://api.amplitude.com https://api.embed.ly https://js.intercomcdn.com
85
+ https://api-iam.intercom.io wss://nexus-websocket-a.intercom.io https://logs-01.loggly.com
86
+ https://api.segment.io https://api.pgncs.notion.so https://checkout.stripe.com
87
+ https://js.stripe.com/v3 https://cdn.contentful.com https://preview.contentful.com
88
+ https://images.ctfassets.net https://api.unsplash.com https://boards-api.greenhouse.io;
89
+ font-src ''self'' data: https://cdnjs.cloudflare.com https://js.intercomcdn.com;
90
+ img-src ''self'' data: blob: https: https://platform.twitter.com https://syndication.twitter.com
91
+ https://pbs.twimg.com https://ton.twimg.com www.googletagmanager.com; style-src
92
+ ''self'' ''unsafe-inline'' https://cdnjs.cloudflare.com https://github.githubassets.com
93
+ https://platform.twitter.com https://ton.twimg.com; frame-src https: http:;
94
+ media-src https: http:'
95
+ X-Webkit-Csp:
96
+ - 'script-src ''self'' ''unsafe-inline'' ''unsafe-eval'' https://gist.github.com
97
+ https://apis.google.com https://api.amplitude.com https://widget.intercom.io
98
+ https://js.intercomcdn.com https://logs-01.loggly.com https://cdn.segment.com
99
+ https://analytics.pgncs.notion.so https://checkout.stripe.com https://js.stripe.com/v3
100
+ https://embed.typeform.com https://admin.typeform.com https://platform.twitter.com
101
+ https://cdn.syndication.twimg.com https://www.googletagmanager.com https://x.clearbitjs.com;
102
+ connect-src ''self'' https://msgstore.www.notion.so wss://msgstore.www.notion.so
103
+ ws://localhost:* https://notion-emojis.s3-us-west-2.amazonaws.com https://s3-us-west-2.amazonaws.com
104
+ https://s3.us-west-2.amazonaws.com https://notion-production-snapshots-2.s3.us-west-2.amazonaws.com
105
+ https: http: https://api.amplitude.com https://api.embed.ly https://js.intercomcdn.com
106
+ https://api-iam.intercom.io wss://nexus-websocket-a.intercom.io https://logs-01.loggly.com
107
+ https://api.segment.io https://api.pgncs.notion.so https://checkout.stripe.com
108
+ https://js.stripe.com/v3 https://cdn.contentful.com https://preview.contentful.com
109
+ https://images.ctfassets.net https://api.unsplash.com https://boards-api.greenhouse.io;
110
+ font-src ''self'' data: https://cdnjs.cloudflare.com https://js.intercomcdn.com;
111
+ img-src ''self'' data: blob: https: https://platform.twitter.com https://syndication.twitter.com
112
+ https://pbs.twimg.com https://ton.twimg.com www.googletagmanager.com; style-src
113
+ ''self'' ''unsafe-inline'' https://cdnjs.cloudflare.com https://github.githubassets.com
114
+ https://platform.twitter.com https://ton.twimg.com; frame-src https: http:;
115
+ media-src https: http:'
116
+ Etag:
117
+ - W/"e5-DFeqv/gYemlg3XfmjAU4nnWmtn8"
118
+ Vary:
119
+ - Accept-Encoding
120
+ Cf-Cache-Status:
121
+ - DYNAMIC
122
+ Cf-Request-Id:
123
+ - '09ac73642e00000818408a7000000001'
124
+ Expect-Ct:
125
+ - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
126
+ Server:
127
+ - cloudflare
128
+ Cf-Ray:
129
+ - 645a8819e9030818-CDG
130
+ body:
131
+ encoding: UTF-8
132
+ string: '{"object":"block","id":"723578f1-6e51-450c-8ee1-09158c19fd4c","created_time":"2021-01-29T20:50:51.917Z","last_edited_time":"2021-04-25T21:05:49.706Z","has_children":true,"type":"child_page","child_page":{"title":"A
133
+ Notion page"}}'
134
+ recorded_at: Sun, 25 Apr 2021 21:05:49 GMT
135
+ recorded_with: VCR 6.0.0