notion_to_md 2.5.1 → 3.0.0.beta2
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.
- checksums.yaml +4 -4
- data/README.md +94 -83
- data/lib/notion_to_md/blocks/block.rb +45 -16
- data/lib/notion_to_md/blocks/factory.rb +49 -17
- data/lib/notion_to_md/blocks/normalizer.rb +82 -14
- data/lib/notion_to_md/blocks/numbered_list_item_block.rb +1 -3
- data/lib/notion_to_md/blocks/{types.rb → renderer.rb} +17 -1
- data/lib/notion_to_md/blocks/to_do_list_item_block.rb +0 -2
- data/lib/notion_to_md/database/builder.rb +107 -0
- data/lib/notion_to_md/database.rb +114 -0
- data/lib/notion_to_md/logger.rb +1 -0
- data/lib/notion_to_md/metadata_type.rb +170 -0
- data/lib/notion_to_md/page/builder.rb +112 -0
- data/lib/notion_to_md/page.rb +80 -106
- data/lib/notion_to_md/support/frontmatter.rb +119 -0
- data/lib/notion_to_md/support/metadata_properties.rb +102 -0
- data/lib/notion_to_md/support/pagination.rb +55 -0
- data/lib/notion_to_md/support/yaml_sanitizer.rb +42 -0
- data/lib/notion_to_md/text.rb +27 -0
- data/lib/notion_to_md/text_annotation.rb +41 -9
- data/lib/notion_to_md/version.rb +1 -1
- data/lib/notion_to_md.rb +65 -38
- metadata +24 -103
- data/lib/notion_to_md/blocks/builder.rb +0 -67
- data/lib/notion_to_md/blocks.rb +0 -35
- data/lib/notion_to_md/converter.rb +0 -72
- data/lib/notion_to_md/helpers/yaml_sanitizer.rb +0 -19
- data/lib/notion_to_md/helpers.rb +0 -3
- data/lib/notion_to_md/page_property.rb +0 -106
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class NotionToMd
|
4
|
+
class Database
|
5
|
+
# === NotionToMd::Database::Builder
|
6
|
+
#
|
7
|
+
# Responsible for fetching and building all pages of a Notion database
|
8
|
+
# into {NotionToMd::Page} instances. Handles pagination via `has_more`
|
9
|
+
# and `next_cursor` automatically.
|
10
|
+
#
|
11
|
+
# @example Build pages for a database
|
12
|
+
# notion_client = Notion::Client.new(token: ENV["NOTION_TOKEN"])
|
13
|
+
# pages = NotionToMd::Database::Builder.call(
|
14
|
+
# database_id: "xxxx-xxxx",
|
15
|
+
# notion_client: notion_client,
|
16
|
+
# filter: { property: "Year", number: { equals: 2023 } },
|
17
|
+
# sorts: [{ property: "Title", direction: "ascending" }],
|
18
|
+
# frontmatter: true
|
19
|
+
# )
|
20
|
+
#
|
21
|
+
# pages.each { |page| puts page.to_s }
|
22
|
+
#
|
23
|
+
# @see NotionToMd::Database
|
24
|
+
# @see NotionToMd::Page
|
25
|
+
class Builder
|
26
|
+
include Support::Pagination
|
27
|
+
|
28
|
+
class << self
|
29
|
+
# Build pages from a database.
|
30
|
+
#
|
31
|
+
# @param database_id [String] The Notion database ID.
|
32
|
+
# @param notion_client [Notion::Client] The Notion API client.
|
33
|
+
# @param filter [Hash, nil] Optional filter to pass to the Notion API.
|
34
|
+
# @param sorts [Array<Hash>, nil] Optional sort criteria.
|
35
|
+
# @param frontmatter [Boolean] Whether to include frontmatter in page Markdown output.
|
36
|
+
#
|
37
|
+
# @return [Array<NotionToMd::Page>] An array of page objects.
|
38
|
+
def call(database_id:, notion_client:, filter: nil, sorts: nil, frontmatter: false)
|
39
|
+
new(
|
40
|
+
database_id: database_id,
|
41
|
+
notion_client: notion_client,
|
42
|
+
filter: filter,
|
43
|
+
sorts: sorts,
|
44
|
+
frontmatter: frontmatter
|
45
|
+
).call
|
46
|
+
end
|
47
|
+
|
48
|
+
# @!method build(...)
|
49
|
+
# Alias of {.call}.
|
50
|
+
alias build call
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [String] The database ID.
|
54
|
+
attr_reader :database_id
|
55
|
+
|
56
|
+
# @return [Notion::Client] The Notion API client.
|
57
|
+
attr_reader :notion_client
|
58
|
+
|
59
|
+
# @return [Hash, nil] Filter criteria passed to Notion API.
|
60
|
+
attr_reader :filter
|
61
|
+
|
62
|
+
# @return [Array<Hash>, nil] Sort criteria passed to Notion API.
|
63
|
+
attr_reader :sorts
|
64
|
+
|
65
|
+
# @return [Hash] Options for building pages (e.g. `{ frontmatter: true }`).
|
66
|
+
attr_reader :page_options
|
67
|
+
|
68
|
+
# Initialize a new builder.
|
69
|
+
#
|
70
|
+
# @param database_id [String] The Notion database ID.
|
71
|
+
# @param notion_client [Notion::Client] The Notion API client.
|
72
|
+
# @param filter [Hash, nil] Optional filter to pass to the Notion API.
|
73
|
+
# @param sorts [Array<Hash>, nil] Optional sort criteria.
|
74
|
+
# @param frontmatter [Boolean] Whether to include frontmatter in page Markdown output.
|
75
|
+
def initialize(database_id:, notion_client:, filter: nil, sorts: nil, frontmatter: false)
|
76
|
+
@database_id = database_id
|
77
|
+
@notion_client = notion_client
|
78
|
+
@filter = filter
|
79
|
+
@sorts = sorts
|
80
|
+
@page_options = { frontmatter: frontmatter }
|
81
|
+
end
|
82
|
+
|
83
|
+
# Fetch and build all pages of the database.
|
84
|
+
#
|
85
|
+
# @return [Array<NotionToMd::Page>]
|
86
|
+
def call
|
87
|
+
fetch_pages.map do |page|
|
88
|
+
NotionToMd::Page.call(id: page.id, notion_client: notion_client, **page_options)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Fetch all raw page records from the Notion database.
|
93
|
+
#
|
94
|
+
# @return [Array<Notion::Messages::Message>] Raw page messages from the Notion API.
|
95
|
+
def fetch_pages
|
96
|
+
params = {}
|
97
|
+
|
98
|
+
paginate do |cursor|
|
99
|
+
params = { database_id: database_id, filter: filter, sorts: sorts }.compact
|
100
|
+
params[:start_cursor] = cursor if cursor
|
101
|
+
|
102
|
+
notion_client.database_query(params)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class NotionToMd
|
4
|
+
# === NotionToMd::Database
|
5
|
+
#
|
6
|
+
# Represents a Notion database and allows converting its pages
|
7
|
+
# into Markdown documents.
|
8
|
+
#
|
9
|
+
# A `Database` object encapsulates the database metadata and
|
10
|
+
# the collection of {NotionToMd::Page} children it contains.
|
11
|
+
#
|
12
|
+
# @example Convert a Notion database to Markdown
|
13
|
+
# notion_client = Notion::Client.new(token: ENV["NOTION_TOKEN"])
|
14
|
+
# db = NotionToMd::Database.call(id: "xxxx-xxxx", notion_client: notion_client, frontmatter: true)
|
15
|
+
#
|
16
|
+
# db.to_s.each_with_index do |page_md, idx|
|
17
|
+
# File.write("page_#{idx}.md", page_md)
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# @see NotionToMd::Page
|
21
|
+
# @see NotionToMd::Database::Builder
|
22
|
+
class Database
|
23
|
+
include Support::MetadataProperties
|
24
|
+
|
25
|
+
class << self
|
26
|
+
# Build a new {Database} from the Notion API.
|
27
|
+
#
|
28
|
+
# @param id [String] The Notion database ID.
|
29
|
+
# @param notion_client [Notion::Client] The Notion API client.
|
30
|
+
# @param filter [Hash, nil] Optional filter criteria to pass to the Notion API.
|
31
|
+
# @param sorts [Array<Hash>, nil] Optional sort criteria.
|
32
|
+
# @param frontmatter [Boolean] Whether to include frontmatter in each page’s Markdown output.
|
33
|
+
#
|
34
|
+
# @return [NotionToMd::Database] a new database instance.
|
35
|
+
#
|
36
|
+
# @see .build
|
37
|
+
def call(id:, notion_client:, filter: nil, sorts: nil, frontmatter: false)
|
38
|
+
new(id: id, notion_client: notion_client, filter: filter, sorts: sorts, frontmatter: frontmatter).call
|
39
|
+
end
|
40
|
+
|
41
|
+
# @!method build(...)
|
42
|
+
# Alias of {.call}.
|
43
|
+
alias build call
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [String] The Notion id database.
|
47
|
+
attr_reader :id
|
48
|
+
|
49
|
+
# @param jNotion::Client] The Notion API client.
|
50
|
+
attr_reader :notion_client
|
51
|
+
|
52
|
+
# @return [Hash] The database configuration options.
|
53
|
+
attr_reader :config
|
54
|
+
|
55
|
+
# @return [Object] The metadata associated with the database.
|
56
|
+
attr_reader :metadata
|
57
|
+
|
58
|
+
# @return [Array<NotionToMd::Page>] The pages contained in the database.
|
59
|
+
attr_reader :children
|
60
|
+
|
61
|
+
# @!method pages
|
62
|
+
# Alias for {#children}.
|
63
|
+
alias pages children
|
64
|
+
|
65
|
+
# Initialize a new database representation.
|
66
|
+
#
|
67
|
+
# @param id [String] The Notion database ID.
|
68
|
+
# @param notion_client [Notion::Client] The Notion API client.
|
69
|
+
# @param filter [Hash, nil] Optional filter criteria to pass to the Notion API.
|
70
|
+
# @param sorts [Array<Hash>, nil] Optional sort criteria.
|
71
|
+
# @param frontmatter [Boolean] Whether to include frontmatter in each page’s Markdown output.
|
72
|
+
def initialize(id:, notion_client:, filter:, sorts:, frontmatter: false)
|
73
|
+
@id = id
|
74
|
+
@notion_client = notion_client
|
75
|
+
@config = {
|
76
|
+
frontmatter: frontmatter,
|
77
|
+
filter: filter,
|
78
|
+
sorts: sorts
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
# Fetch database metadata and contained pages from the Notion API.
|
83
|
+
#
|
84
|
+
# This method populates the database's metadata and children by making API calls
|
85
|
+
# to retrieve the database structure and all pages contained within it, applying
|
86
|
+
# any configured filters and sorting criteria.
|
87
|
+
#
|
88
|
+
# @return [NotionToMd::Database] returns self to allow method chaining.
|
89
|
+
def call
|
90
|
+
@metadata = notion_client.database(database_id: id)
|
91
|
+
@children = Builder.call(
|
92
|
+
database_id: id,
|
93
|
+
notion_client: notion_client,
|
94
|
+
filter: config[:filter],
|
95
|
+
sorts: config[:sorts],
|
96
|
+
frontmatter: config[:frontmatter]
|
97
|
+
)
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
alias build call
|
102
|
+
|
103
|
+
# Convert all database pages into Markdown.
|
104
|
+
#
|
105
|
+
# @return [Array<String>] Markdown documents for each page in the database.
|
106
|
+
def to_s
|
107
|
+
pages.map(&:to_s)
|
108
|
+
end
|
109
|
+
|
110
|
+
# @!method to_md
|
111
|
+
# Alias for {#to_s}.
|
112
|
+
alias to_md to_s
|
113
|
+
end
|
114
|
+
end
|
data/lib/notion_to_md/logger.rb
CHANGED
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class NotionToMd
|
4
|
+
# === NotionToMd::MetadataType
|
5
|
+
#
|
6
|
+
# Utility class responsible for extracting and sanitizing values
|
7
|
+
# from Notion database/page property objects. Each class method
|
8
|
+
# corresponds to a Notion property type and returns a normalized
|
9
|
+
# Ruby value suitable for use in YAML frontmatter or Markdown.
|
10
|
+
#
|
11
|
+
# This class is typically used indirectly via
|
12
|
+
# {NotionToMd::Support::MetadataProperties}.
|
13
|
+
#
|
14
|
+
# @example Extract a multi-select property
|
15
|
+
# prop = { multi_select: [{ name: "Action" }, { name: "Drama" }] }
|
16
|
+
# NotionToMd::MetadataType.multi_select(prop)
|
17
|
+
# # => ["Action", "Drama"]
|
18
|
+
#
|
19
|
+
# @example Extract a date property
|
20
|
+
# prop = { date: { start: "2024-01-01T00:00:00Z" } }
|
21
|
+
# NotionToMd::MetadataType.date(prop)
|
22
|
+
# # => 2024-01-01 00:00:00 +0000
|
23
|
+
#
|
24
|
+
# @see NotionToMd::Support::YamlSanitizer
|
25
|
+
class MetadataType
|
26
|
+
class << self
|
27
|
+
include Support::YamlSanitizer
|
28
|
+
|
29
|
+
# Extract file URL.
|
30
|
+
# @param prop [Hash]
|
31
|
+
# @return [String, nil]
|
32
|
+
def file(prop)
|
33
|
+
prop.dig(:file, :url)
|
34
|
+
rescue NoMethodError
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# Extract external file URL.
|
39
|
+
# @param prop [Hash]
|
40
|
+
# @return [String, nil]
|
41
|
+
def external(prop)
|
42
|
+
prop.dig(:external, :url)
|
43
|
+
rescue NoMethodError
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
# Extract emoji character.
|
48
|
+
# @param prop [Hash]
|
49
|
+
# @return [String, nil]
|
50
|
+
def emoji(prop)
|
51
|
+
prop[:emoji]
|
52
|
+
rescue NoMethodError
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
|
56
|
+
# Extract multi-select values as names.
|
57
|
+
# @param prop [Hash]
|
58
|
+
# @return [Array<String>, nil]
|
59
|
+
def multi_select(prop)
|
60
|
+
prop[:multi_select].map { |sel| sel[:name] }
|
61
|
+
rescue NoMethodError
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
# Extract selected option name.
|
66
|
+
# Escapes YAML-sensitive characters.
|
67
|
+
# @param prop [Hash]
|
68
|
+
# @return [String, nil]
|
69
|
+
def select(prop)
|
70
|
+
escape_frontmatter_value(prop.dig(:select, :name))
|
71
|
+
rescue NoMethodError
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
|
75
|
+
# Extract people names.
|
76
|
+
# @param prop [Hash]
|
77
|
+
# @return [Array<String>, nil]
|
78
|
+
def people(prop)
|
79
|
+
prop[:people].map { |sel| sel[:name] }
|
80
|
+
rescue NoMethodError
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
84
|
+
# Extract file or external URLs from files list.
|
85
|
+
# @param prop [Hash]
|
86
|
+
# @return [Array<String>, nil]
|
87
|
+
def files(prop)
|
88
|
+
prop[:files].map { |f| file(f) || external(f) }
|
89
|
+
rescue NoMethodError
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
|
93
|
+
# Extract phone number.
|
94
|
+
# @param prop [Hash]
|
95
|
+
# @return [String, nil]
|
96
|
+
def phone_number(prop)
|
97
|
+
prop[:phone_number]
|
98
|
+
rescue NoMethodError
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
|
102
|
+
# Extract number.
|
103
|
+
# @param prop [Hash]
|
104
|
+
# @return [Numeric, nil]
|
105
|
+
def number(prop)
|
106
|
+
prop[:number]
|
107
|
+
rescue NoMethodError
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
111
|
+
# Extract email.
|
112
|
+
# @param prop [Hash]
|
113
|
+
# @return [String, nil]
|
114
|
+
def email(prop)
|
115
|
+
prop[:email]
|
116
|
+
rescue NoMethodError
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
|
120
|
+
# Extract checkbox (as string "true"/"false").
|
121
|
+
# @param prop [Hash]
|
122
|
+
# @return [String, nil]
|
123
|
+
def checkbox(prop)
|
124
|
+
prop[:checkbox]&.to_s
|
125
|
+
rescue NoMethodError
|
126
|
+
nil
|
127
|
+
end
|
128
|
+
|
129
|
+
# Extract date value.
|
130
|
+
# Converts strings to {Time}, {Date} to {Time}, leaves Time unchanged.
|
131
|
+
# End date and time zone are not supported.
|
132
|
+
#
|
133
|
+
# @param prop [Hash]
|
134
|
+
# @return [Time, String, nil] Parsed start date, raw value, or nil.
|
135
|
+
def date(prop)
|
136
|
+
date = prop.dig(:date, :start)
|
137
|
+
|
138
|
+
case date
|
139
|
+
when Date
|
140
|
+
date.to_time
|
141
|
+
when String
|
142
|
+
Time.parse(date)
|
143
|
+
else
|
144
|
+
date # Time or nil
|
145
|
+
end
|
146
|
+
rescue NoMethodError
|
147
|
+
nil
|
148
|
+
end
|
149
|
+
|
150
|
+
# Extract URL.
|
151
|
+
# @param prop [Hash]
|
152
|
+
# @return [String, nil]
|
153
|
+
def url(prop)
|
154
|
+
prop[:url]
|
155
|
+
rescue NoMethodError
|
156
|
+
nil
|
157
|
+
end
|
158
|
+
|
159
|
+
# Extract rich text as plain string, escaped for YAML if necessary.
|
160
|
+
# @param prop [Hash]
|
161
|
+
# @return [String, nil]
|
162
|
+
def rich_text(prop)
|
163
|
+
text = prop[:rich_text].map { |text| text[:plain_text] }.join
|
164
|
+
text.blank? ? nil : escape_frontmatter_value(text)
|
165
|
+
rescue NoMethodError
|
166
|
+
nil
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class NotionToMd
|
4
|
+
class Page
|
5
|
+
# === NotionToMd::Page::Builder
|
6
|
+
#
|
7
|
+
# Builds a tree of Notion blocks for a given block (or page) ID by
|
8
|
+
# fetching children from the Notion API and recursively traversing
|
9
|
+
# blocks that are allowed to contain nested children.
|
10
|
+
#
|
11
|
+
# @example Build all blocks for a page
|
12
|
+
# notion = Notion::Client.new(token: ENV["NOTION_TOKEN"])
|
13
|
+
# blocks = NotionToMd::Page::Builder.call(block_id: "xxxx-xxxx", notion_client: notion)
|
14
|
+
# blocks # => [NotionToMd::Blocks::Block, ...]
|
15
|
+
#
|
16
|
+
# @see NotionToMd::Blocks::Factory
|
17
|
+
# @see NotionToMd::Blocks::Normalizer
|
18
|
+
class Builder
|
19
|
+
include Support::Pagination
|
20
|
+
|
21
|
+
# Block types allowed to have nested blocks (children).
|
22
|
+
#
|
23
|
+
# @return [Array<Symbol>]
|
24
|
+
BLOCKS_WITH_PERMITTED_CHILDREN = %i[
|
25
|
+
bulleted_list_item
|
26
|
+
numbered_list_item
|
27
|
+
paragraph
|
28
|
+
to_do
|
29
|
+
table
|
30
|
+
].freeze
|
31
|
+
|
32
|
+
class << self
|
33
|
+
# Build the block tree from a starting block ID.
|
34
|
+
#
|
35
|
+
# @param block_id [String] The Notion block (or page) ID to expand.
|
36
|
+
# @param notion_client [Notion::Client] Initialized Notion client.
|
37
|
+
#
|
38
|
+
# @return [Array<NotionToMd::Blocks::Block>] Normalized, possibly nested blocks.
|
39
|
+
#
|
40
|
+
# @see .build
|
41
|
+
def call(block_id:, notion_client:)
|
42
|
+
new(block_id: block_id, notion_client: notion_client).call
|
43
|
+
end
|
44
|
+
|
45
|
+
# @!method build(...)
|
46
|
+
# Alias of {.call}.
|
47
|
+
alias build call
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String] The root block (or page) ID to expand.
|
51
|
+
attr_reader :block_id
|
52
|
+
|
53
|
+
# @return [Notion::Client] The Notion API client.
|
54
|
+
attr_reader :notion_client
|
55
|
+
|
56
|
+
# Create a new builder.
|
57
|
+
#
|
58
|
+
# @param block_id [String] The Notion block (or page) ID to expand.
|
59
|
+
# @param notion_client [Notion::Client] Initialized Notion client.
|
60
|
+
def initialize(block_id:, notion_client:)
|
61
|
+
@block_id = block_id
|
62
|
+
@notion_client = notion_client
|
63
|
+
end
|
64
|
+
|
65
|
+
# Fetch, build, and normalize the full block tree.
|
66
|
+
#
|
67
|
+
# Recursively expands children for block types listed in
|
68
|
+
# {BLOCKS_WITH_PERMITTED_CHILDREN}. Uses {#fetch_blocks} to page through
|
69
|
+
# results via `has_more` / `next_cursor`.
|
70
|
+
#
|
71
|
+
# @return [Array<NotionToMd::Blocks::Block>] Normalized blocks ready for rendering.
|
72
|
+
def call
|
73
|
+
notion_blocks = fetch_blocks
|
74
|
+
blocks = notion_blocks.map do |block|
|
75
|
+
children = if permitted_children_for?(block: block)
|
76
|
+
self.class.call(block_id: block.id, notion_client: notion_client)
|
77
|
+
else
|
78
|
+
[]
|
79
|
+
end
|
80
|
+
Blocks::Factory.build(block: block, children: children)
|
81
|
+
end
|
82
|
+
|
83
|
+
Blocks::Normalizer.call(blocks: blocks)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Whether a block can have (and actually has) children to be traversed.
|
87
|
+
#
|
88
|
+
# @param block [Notion::Messages::Message] A Notion block message.
|
89
|
+
#
|
90
|
+
# @return [Boolean] True if the block type allows children and `has_children` is truthy.
|
91
|
+
def permitted_children_for?(block:)
|
92
|
+
BLOCKS_WITH_PERMITTED_CHILDREN.include?(block.type.to_sym) && block.has_children
|
93
|
+
end
|
94
|
+
|
95
|
+
# Fetch all direct children for {#block_id}, handling Notion pagination.
|
96
|
+
#
|
97
|
+
# @note This method loops until `has_more` is false, following `next_cursor`.
|
98
|
+
#
|
99
|
+
# @return [Array<Notion::Messages::Message>] Raw Notion block messages.
|
100
|
+
def fetch_blocks
|
101
|
+
params = {}
|
102
|
+
|
103
|
+
paginate do |cursor|
|
104
|
+
params[:block_id] = block_id
|
105
|
+
params[:start_cursor] = cursor if cursor
|
106
|
+
|
107
|
+
notion_client.block_children(params)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|