notion_ruby_mapping 4.1.0 → 4.2.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.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/lib/notion_ruby_mapping/blocks/block.rb +51 -48
- data/lib/notion_ruby_mapping/blocks/breadcrumb_block.rb +4 -4
- data/lib/notion_ruby_mapping/blocks/callout_block.rb +6 -1
- data/lib/notion_ruby_mapping/blocks/divider_block.rb +4 -4
- data/lib/notion_ruby_mapping/blocks/equation_block.rb +2 -2
- data/lib/notion_ruby_mapping/blocks/file_base_block.rb +48 -19
- data/lib/notion_ruby_mapping/blocks/file_block.rb +4 -2
- data/lib/notion_ruby_mapping/blocks/heading1_block.rb +1 -1
- data/lib/notion_ruby_mapping/blocks/heading2_block.rb +1 -1
- data/lib/notion_ruby_mapping/blocks/heading3_block.rb +1 -1
- data/lib/notion_ruby_mapping/blocks/heading4_block.rb +1 -1
- data/lib/notion_ruby_mapping/blocks/table_of_contents_block.rb +6 -5
- data/lib/notion_ruby_mapping/controllers/rich_text_array.rb +2 -1
- data/lib/notion_ruby_mapping/objects/equation_object.rb +6 -4
- data/lib/notion_ruby_mapping/objects/file_object.rb +50 -25
- data/lib/notion_ruby_mapping/objects/file_upload_object.rb +114 -68
- data/lib/notion_ruby_mapping/properties/property.rb +1 -1
- data/lib/notion_ruby_mapping/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4d8cbce3013176a87d6e023e5bbbd0b5ae6f5c43b437f50c3c6df468ce302d19
|
|
4
|
+
data.tar.gz: 0ea1f4d67320d4039a42eb156ba03a29595a4362c51a44f7541a874c735c257b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 05a4e8f93a32115e06047aa0324879e63c5ec761d7b98e1cfc7c1df76033389705d1a25ca0b79a1b657dfb4df27b80fa4eeb0793ec21be359d3c2105cbe3f98c
|
|
7
|
+
data.tar.gz: cc86a8c085ed3da0b7905560c4f92ec8c627739480c3ee13a951f9b9c82bf0ec5cf042f0d888af7bc98a9ecaee788282d042b4de7961852c9cd3407ed8389ab6
|
data/README.md
CHANGED
|
@@ -143,6 +143,7 @@ NotionRubyMapping.configuration { |c| c.notion_token = ENV["NOTION_API_TOKEN"] }
|
|
|
143
143
|
|
|
144
144
|
## 3. ChangeLog
|
|
145
145
|
|
|
146
|
+
- 2026/9/7 [v4.2.0] Refactor file handling (move FileUploadObject into NotionRubyMapping, remove FileObject update tracking, and reject missing or empty file sources)
|
|
146
147
|
- 2026/8/23 [v4.1.0] Refactor Property base class (remove def_delegators and contents?, raise for unsupported property type)
|
|
147
148
|
- 2026/7/14 [v4.0.2] Add clear method for DateProperty
|
|
148
149
|
- 2026/5/4 [v4.0.1] Add retrieve / update / delete a comment
|
|
@@ -14,54 +14,10 @@ module NotionRubyMapping
|
|
|
14
14
|
end
|
|
15
15
|
attr_reader :can_have_children, :can_append, :type, :rich_text_array, :url, :caption, :color, :language
|
|
16
16
|
|
|
17
|
-
def self.type2class(type, has_children = false)
|
|
18
|
-
@type2class ||= {
|
|
19
|
-
false => {
|
|
20
|
-
audio: AudioBlock,
|
|
21
|
-
bookmark: BookmarkBlock,
|
|
22
|
-
breadcrumb: BreadcrumbBlock,
|
|
23
|
-
bulleted_list_item: BulletedListItemBlock,
|
|
24
|
-
callout: CalloutBlock,
|
|
25
|
-
child_database: ChildDatabaseBlock,
|
|
26
|
-
child_page: ChildPageBlock,
|
|
27
|
-
code: CodeBlock,
|
|
28
|
-
column: ColumnBlock,
|
|
29
|
-
column_list: ColumnListBlock,
|
|
30
|
-
divider: DividerBlock,
|
|
31
|
-
embed: EmbedBlock,
|
|
32
|
-
equation: EquationBlock,
|
|
33
|
-
file: FileBlock,
|
|
34
|
-
heading_1: Heading1Block,
|
|
35
|
-
heading_2: Heading2Block,
|
|
36
|
-
heading_3: Heading3Block,
|
|
37
|
-
heading_4: Heading4Block,
|
|
38
|
-
image: ImageBlock,
|
|
39
|
-
link_preview: LinkPreviewBlock,
|
|
40
|
-
link_to_page: LinkToPageBlock,
|
|
41
|
-
numbered_list_item: NumberedListItemBlock,
|
|
42
|
-
paragraph: ParagraphBlock,
|
|
43
|
-
pdf: PdfBlock,
|
|
44
|
-
quote: QuoteBlock,
|
|
45
|
-
synced_block: SyncedBlock,
|
|
46
|
-
table: TableBlock,
|
|
47
|
-
table_row: TableRowBlock,
|
|
48
|
-
table_of_contents: TableOfContentsBlock,
|
|
49
|
-
to_do: ToDoBlock,
|
|
50
|
-
toggle: ToggleBlock,
|
|
51
|
-
video: VideoBlock,
|
|
52
|
-
},
|
|
53
|
-
true => {
|
|
54
|
-
heading_1: ToggleHeading1Block,
|
|
55
|
-
heading_2: ToggleHeading2Block,
|
|
56
|
-
heading_3: ToggleHeading3Block,
|
|
57
|
-
heading_4: ToggleHeading4Block,
|
|
58
|
-
},
|
|
59
|
-
}
|
|
60
|
-
@klass = @type2class[has_children][type.to_sym] || @type2class[false][type.to_sym] || Block
|
|
61
|
-
end
|
|
62
|
-
|
|
63
17
|
def self.decode_block(json)
|
|
64
|
-
|
|
18
|
+
type = json["type"]
|
|
19
|
+
is_toggleable = !!(json[type] && json[type]["is_toggleable"])
|
|
20
|
+
type2class(type, is_toggleable).new json: json
|
|
65
21
|
end
|
|
66
22
|
|
|
67
23
|
# @see https://www.notion.so/hkob/Block-689ad4cbff50404d8a1baf67b6d6d78d#298916c7c379424682f39ff09ee38544
|
|
@@ -101,7 +57,7 @@ module NotionRubyMapping
|
|
|
101
57
|
|
|
102
58
|
# @return [NotionRubyMapping::RichTextArray]
|
|
103
59
|
def decode_block_caption
|
|
104
|
-
@caption = RichTextArray.new "caption", json: @json[type]["caption"]
|
|
60
|
+
@caption = RichTextArray.new "caption", json: @json[type]["caption"] || []
|
|
105
61
|
end
|
|
106
62
|
|
|
107
63
|
# @return [String]
|
|
@@ -154,5 +110,52 @@ module NotionRubyMapping
|
|
|
154
110
|
@color = color
|
|
155
111
|
self
|
|
156
112
|
end
|
|
113
|
+
|
|
114
|
+
def self.type2class(type, is_toggleable = false)
|
|
115
|
+
@type2class ||= {
|
|
116
|
+
false => {
|
|
117
|
+
audio: AudioBlock,
|
|
118
|
+
bookmark: BookmarkBlock,
|
|
119
|
+
breadcrumb: BreadcrumbBlock,
|
|
120
|
+
bulleted_list_item: BulletedListItemBlock,
|
|
121
|
+
callout: CalloutBlock,
|
|
122
|
+
child_database: ChildDatabaseBlock,
|
|
123
|
+
child_page: ChildPageBlock,
|
|
124
|
+
code: CodeBlock,
|
|
125
|
+
column: ColumnBlock,
|
|
126
|
+
column_list: ColumnListBlock,
|
|
127
|
+
divider: DividerBlock,
|
|
128
|
+
embed: EmbedBlock,
|
|
129
|
+
equation: EquationBlock,
|
|
130
|
+
file: FileBlock,
|
|
131
|
+
heading_1: Heading1Block,
|
|
132
|
+
heading_2: Heading2Block,
|
|
133
|
+
heading_3: Heading3Block,
|
|
134
|
+
heading_4: Heading4Block,
|
|
135
|
+
image: ImageBlock,
|
|
136
|
+
link_preview: LinkPreviewBlock,
|
|
137
|
+
link_to_page: LinkToPageBlock,
|
|
138
|
+
numbered_list_item: NumberedListItemBlock,
|
|
139
|
+
paragraph: ParagraphBlock,
|
|
140
|
+
pdf: PdfBlock,
|
|
141
|
+
quote: QuoteBlock,
|
|
142
|
+
synced_block: SyncedBlock,
|
|
143
|
+
table: TableBlock,
|
|
144
|
+
table_row: TableRowBlock,
|
|
145
|
+
table_of_contents: TableOfContentsBlock,
|
|
146
|
+
to_do: ToDoBlock,
|
|
147
|
+
toggle: ToggleBlock,
|
|
148
|
+
video: VideoBlock,
|
|
149
|
+
},
|
|
150
|
+
true => {
|
|
151
|
+
heading_1: ToggleHeading1Block,
|
|
152
|
+
heading_2: ToggleHeading2Block,
|
|
153
|
+
heading_3: ToggleHeading3Block,
|
|
154
|
+
heading_4: ToggleHeading4Block,
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
@type2class[is_toggleable][type.to_sym] || @type2class[false][type.to_sym] || Block
|
|
158
|
+
end
|
|
159
|
+
private_class_method :type2class
|
|
157
160
|
end
|
|
158
161
|
end
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module NotionRubyMapping
|
|
4
|
-
# Notion block
|
|
4
|
+
# Notion breadcrumb block
|
|
5
5
|
class BreadcrumbBlock < Block
|
|
6
|
-
# @return [String
|
|
6
|
+
# @return [String] block type
|
|
7
7
|
def type
|
|
8
8
|
"breadcrumb"
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
# @param [Boolean] not_update false
|
|
12
|
-
# @return [Hash{String
|
|
11
|
+
# @param [Boolean] not_update true for a full block payload; false for an update payload
|
|
12
|
+
# @return [Hash{String => Object}] block payload
|
|
13
13
|
def block_json(not_update: true)
|
|
14
14
|
ans = super
|
|
15
15
|
ans[type] = {}
|
|
@@ -14,8 +14,13 @@ module NotionRubyMapping
|
|
|
14
14
|
if @json
|
|
15
15
|
decode_block_rich_text_array
|
|
16
16
|
decode_color
|
|
17
|
+
@emoji = json[type]&.dig("icon", "emoji")&.then { |str| EmojiObject.new emoji: str }
|
|
18
|
+
@file_object = json[type]&.dig("icon")&.then { |file_json| FileObject.new json: file_json }
|
|
17
19
|
else
|
|
18
20
|
rich_text_array_and_color "rich_text", text_info, color
|
|
21
|
+
raise ArgumentError, "Specify either emoji or file_url." if emoji.nil? && file_url.nil?
|
|
22
|
+
raise ArgumentError, "Specify either emoji or file_url, not both." if !emoji.nil? && !file_url.nil?
|
|
23
|
+
|
|
19
24
|
@emoji = EmojiObject.emoji_object emoji if emoji
|
|
20
25
|
@file_object = FileObject.file_object file_url if file_url
|
|
21
26
|
add_sub_blocks sub_blocks
|
|
@@ -34,7 +39,7 @@ module NotionRubyMapping
|
|
|
34
39
|
ans[type] = @rich_text_array.update_property_schema_json not_update
|
|
35
40
|
ans[type]["color"] = @color
|
|
36
41
|
ans[type]["icon"] = @emoji.property_values_json if @emoji
|
|
37
|
-
ans[type]["icon"] = @file_object.property_values_json if @file_object
|
|
42
|
+
ans[type]["icon"] = @file_object.property_values_json if @file_object && @file_object.url
|
|
38
43
|
ans[type]["children"] = @sub_blocks.map(&:block_json) if @sub_blocks
|
|
39
44
|
ans
|
|
40
45
|
end
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module NotionRubyMapping
|
|
4
|
-
# Notion block
|
|
4
|
+
# Notion divider block
|
|
5
5
|
class DividerBlock < Block
|
|
6
|
-
# @param [Boolean] not_update false
|
|
7
|
-
# @return [Hash{String
|
|
6
|
+
# @param [Boolean] not_update true for a full block payload; false for an update payload
|
|
7
|
+
# @return [Hash{String => Object}] block payload
|
|
8
8
|
def block_json(not_update: true)
|
|
9
9
|
ans = super
|
|
10
10
|
ans[type] = {}
|
|
11
11
|
ans
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
# @return [String
|
|
14
|
+
# @return [String] block type
|
|
15
15
|
def type
|
|
16
16
|
"divider"
|
|
17
17
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module NotionRubyMapping
|
|
4
4
|
# Notion block
|
|
5
5
|
class EquationBlock < Block
|
|
6
|
-
# @param [String, NotionRubyMapping::EquationObject] expression
|
|
6
|
+
# @param [String, NotionRubyMapping::EquationObject, nil] expression
|
|
7
7
|
# @see https://www.notion.so/hkob/EquationBlock-df0f823dc5ac41b798052f161dd6540c#cfcd2ceb77194c0e915500b429e8b91b
|
|
8
8
|
def initialize(expression = nil, json: nil, id: nil, parent: nil)
|
|
9
9
|
super(json: json, id: id, parent: parent)
|
|
@@ -28,7 +28,7 @@ module NotionRubyMapping
|
|
|
28
28
|
@equation_object&.expression
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
# @param [String] new_expression
|
|
31
|
+
# @param [String, nil] new_expression
|
|
32
32
|
# @see https://www.notion.so/hkob/EquationBlock-df0f823dc5ac41b798052f161dd6540c#f05e3b2c82914cea9f05e9e6644647e1
|
|
33
33
|
def expression=(new_expression)
|
|
34
34
|
@equation_object = EquationObject.equation_object new_expression
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module NotionRubyMapping
|
|
4
|
-
#
|
|
4
|
+
# Base class for blocks that contain a file and an optional caption.
|
|
5
|
+
#
|
|
6
|
+
# This class manages the file representation, caption, and update payload
|
|
7
|
+
# shared by file-based block types.
|
|
8
|
+
# @abstract Subclass and implement {#type}.
|
|
5
9
|
class FileBaseBlock < Block
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# @
|
|
9
|
-
#
|
|
10
|
+
# Creates a file-based block.
|
|
11
|
+
#
|
|
12
|
+
# @param url_or_fuo [String, FileUploadObject, FileObject, nil]
|
|
13
|
+
# an external URL, a file upload object, or a file object
|
|
14
|
+
# @param caption [RichTextArray, String, Array<String>, RichTextObject,
|
|
15
|
+
# Array<RichTextObject>, nil] caption content
|
|
16
|
+
# @param json [Hash, nil] block JSON returned by the Notion API
|
|
17
|
+
# @param id [String, nil] block ID
|
|
18
|
+
# @param parent [Page, Block, nil] parent object
|
|
10
19
|
def initialize(url_or_fuo = nil, caption: [], json: nil, id: nil, parent: nil)
|
|
11
20
|
super(json: json, id: id, parent: parent)
|
|
12
21
|
if @json
|
|
@@ -19,12 +28,18 @@ module NotionRubyMapping
|
|
|
19
28
|
end
|
|
20
29
|
end
|
|
21
30
|
|
|
22
|
-
# @
|
|
23
|
-
|
|
24
|
-
attr_reader :caption, :file_object
|
|
31
|
+
# @return [RichTextArray] block caption
|
|
32
|
+
attr_reader :caption
|
|
25
33
|
|
|
26
|
-
# @
|
|
27
|
-
|
|
34
|
+
# @return [FileObject] current file representation
|
|
35
|
+
attr_reader :file_object
|
|
36
|
+
|
|
37
|
+
# Builds the block JSON representation.
|
|
38
|
+
#
|
|
39
|
+
# @param not_update [Boolean] true for a complete block payload;
|
|
40
|
+
# false for an update payload
|
|
41
|
+
# @return [Hash{String => Object}] block JSON
|
|
42
|
+
# @api private
|
|
28
43
|
def block_json(not_update: true)
|
|
29
44
|
ans = super
|
|
30
45
|
ans[type] = @file_object.property_values_json
|
|
@@ -32,27 +47,41 @@ module NotionRubyMapping
|
|
|
32
47
|
ans
|
|
33
48
|
end
|
|
34
49
|
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
50
|
+
# Returns the current file URL.
|
|
51
|
+
#
|
|
52
|
+
# A URL returned for a Notion-hosted file may expire.
|
|
53
|
+
#
|
|
54
|
+
# @return [String, nil] current file URL
|
|
38
55
|
def url
|
|
39
56
|
@file_object&.url
|
|
40
57
|
end
|
|
41
58
|
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
59
|
+
# Replaces the current file with an external URL.
|
|
60
|
+
#
|
|
61
|
+
# Call {Block#save} to send the change to the Notion API.
|
|
62
|
+
#
|
|
63
|
+
# @param url [String] new external URL
|
|
45
64
|
def url=(url)
|
|
46
65
|
@file_object.url = url
|
|
47
66
|
@payload.add_update_block_key "external"
|
|
48
67
|
end
|
|
49
68
|
|
|
50
|
-
#
|
|
51
|
-
|
|
52
|
-
|
|
69
|
+
# Replaces the current file with an uploaded file.
|
|
70
|
+
#
|
|
71
|
+
# Call {Block#save} to send the change to the Notion API. After saving,
|
|
72
|
+
# the file is reconstructed as a Notion-hosted file.
|
|
73
|
+
#
|
|
74
|
+
# @param file_upload_object [FileUploadObject] uploaded file
|
|
75
|
+
def file_upload_object=(file_upload_object)
|
|
76
|
+
@file_object.file_upload_object = file_upload_object
|
|
53
77
|
@payload.add_update_block_key "file_upload"
|
|
54
78
|
end
|
|
55
79
|
|
|
80
|
+
# Reconstructs the file and caption from an API response.
|
|
81
|
+
#
|
|
82
|
+
# @param json [Hash] block JSON returned by the Notion API
|
|
83
|
+
# @return [void]
|
|
84
|
+
# @api private
|
|
56
85
|
def update_file_object_from_json(json)
|
|
57
86
|
@file_object = FileObject.new json: json[type]
|
|
58
87
|
decode_block_caption
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module NotionRubyMapping
|
|
4
|
-
# Notion block
|
|
4
|
+
# Represents a Notion file block.
|
|
5
5
|
class FileBlock < FileBaseBlock
|
|
6
|
-
#
|
|
6
|
+
# Returns the Notion block type.
|
|
7
|
+
#
|
|
8
|
+
# @return [String] `"file"`
|
|
7
9
|
def type
|
|
8
10
|
"file"
|
|
9
11
|
end
|
|
@@ -5,7 +5,7 @@ module NotionRubyMapping
|
|
|
5
5
|
class Heading1Block < TextSubBlockColorBaseBlock
|
|
6
6
|
# @param [RichTextArray, String, Array<String>, RichTextObject, Array<RichTextObject>, nil] text_info
|
|
7
7
|
# @param [String] color
|
|
8
|
-
def initialize(text_info = nil, color:
|
|
8
|
+
def initialize(text_info = nil, color: "default", json: nil, id: nil, parent: nil)
|
|
9
9
|
super(text_info, color: color, json: json, id: id, parent: parent)
|
|
10
10
|
@can_have_children = false
|
|
11
11
|
end
|
|
@@ -5,7 +5,7 @@ module NotionRubyMapping
|
|
|
5
5
|
class Heading2Block < TextSubBlockColorBaseBlock
|
|
6
6
|
# @param [RichTextArray, String, Array<String>, RichTextObject, Array<RichTextObject>] text_info
|
|
7
7
|
# @param [String] color
|
|
8
|
-
def initialize(text_info = nil, color:
|
|
8
|
+
def initialize(text_info = nil, color: "default", json: nil, id: nil, parent: nil)
|
|
9
9
|
super(text_info, color: color, json: json, id: id, parent: parent)
|
|
10
10
|
@can_have_children = false
|
|
11
11
|
end
|
|
@@ -5,7 +5,7 @@ module NotionRubyMapping
|
|
|
5
5
|
class Heading3Block < TextSubBlockColorBaseBlock
|
|
6
6
|
# @param [RichTextArray, String, Array<String>, RichTextObject, Array<RichTextObject>] text_info
|
|
7
7
|
# @param [String] color
|
|
8
|
-
def initialize(text_info = nil, color:
|
|
8
|
+
def initialize(text_info = nil, color: "default", json: nil, id: nil, parent: nil)
|
|
9
9
|
super(text_info, color: color, json: json, id: id, parent: parent)
|
|
10
10
|
@can_have_children = false
|
|
11
11
|
end
|
|
@@ -5,7 +5,7 @@ module NotionRubyMapping
|
|
|
5
5
|
class Heading4Block < TextSubBlockColorBaseBlock
|
|
6
6
|
# @param [RichTextArray, String, Array<String>, RichTextObject, Array<RichTextObject>] text_info
|
|
7
7
|
# @param [String] color
|
|
8
|
-
def initialize(text_info = nil, color:
|
|
8
|
+
def initialize(text_info = nil, color: "default", json: nil, id: nil, parent: nil)
|
|
9
9
|
super(text_info, color: color, json: json, id: id, parent: parent)
|
|
10
10
|
@can_have_children = false
|
|
11
11
|
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module NotionRubyMapping
|
|
4
|
-
# Notion block
|
|
5
|
-
# @param [String] color
|
|
4
|
+
# Notion table of contents block
|
|
6
5
|
class TableOfContentsBlock < Block
|
|
6
|
+
# @param [String] color block color
|
|
7
7
|
def initialize(color = "default", json: nil, id: nil, parent: nil)
|
|
8
8
|
super(json: json, id: id, parent: parent)
|
|
9
9
|
if @json
|
|
@@ -13,18 +13,19 @@ module NotionRubyMapping
|
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
# @param [String] new_color new block color
|
|
16
17
|
def color=(new_color)
|
|
17
18
|
@color = new_color
|
|
18
19
|
@payload.add_update_block_key "color"
|
|
19
20
|
end
|
|
20
21
|
|
|
21
|
-
# @return [String
|
|
22
|
+
# @return [String] block type
|
|
22
23
|
def type
|
|
23
24
|
"table_of_contents"
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
# @param [Boolean] not_update false
|
|
27
|
-
# @return [Hash{String
|
|
27
|
+
# @param [Boolean] not_update true for a full block payload; false for an update payload
|
|
28
|
+
# @return [Hash{String => Object}] block payload
|
|
28
29
|
def block_json(not_update: true)
|
|
29
30
|
ans = super
|
|
30
31
|
ans[type] = {"color" => @color}
|
|
@@ -19,13 +19,14 @@ module NotionRubyMapping
|
|
|
19
19
|
end
|
|
20
20
|
@will_update = will_update
|
|
21
21
|
end
|
|
22
|
+
attr_reader :rich_text_objects
|
|
22
23
|
attr_writer :will_update
|
|
23
24
|
|
|
24
25
|
def self.rich_text_array(key, text_objects = nil)
|
|
25
26
|
if text_objects.nil?
|
|
26
27
|
RichTextArray.new key
|
|
27
28
|
elsif text_objects.is_a? RichTextArray
|
|
28
|
-
text_objects
|
|
29
|
+
RichTextArray.new key, text_objects: text_objects.rich_text_objects
|
|
29
30
|
else
|
|
30
31
|
RichTextArray.new key, text_objects: text_objects
|
|
31
32
|
end
|
|
@@ -3,16 +3,17 @@
|
|
|
3
3
|
module NotionRubyMapping
|
|
4
4
|
# EquationObject
|
|
5
5
|
class EquationObject < RichTextObject
|
|
6
|
-
# @param [String] expression
|
|
7
|
-
# @return [
|
|
6
|
+
# @param [String, nil] expression
|
|
7
|
+
# @return [EquationObject]
|
|
8
8
|
def initialize(expression, options = {})
|
|
9
|
+
expression ||= ""
|
|
9
10
|
super "equation", {"plain_text" => expression}.merge(options)
|
|
10
11
|
@expression = expression
|
|
11
12
|
@will_update = false
|
|
12
13
|
end
|
|
13
14
|
attr_reader :will_update, :expression
|
|
14
15
|
|
|
15
|
-
# @param [EquationObject, String]
|
|
16
|
+
# @param [EquationObject, String, nil] expression_or_eo
|
|
16
17
|
# @return [EquationObject] self or created EquationObject
|
|
17
18
|
# @see https://www.notion.so/hkob/EquationObject-cd50126fce544ad5bb76463a4269859b#45b0b0810d8647a1968a5e4293920aeb
|
|
18
19
|
def self.equation_object(expression_or_eo)
|
|
@@ -23,9 +24,10 @@ module NotionRubyMapping
|
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
# @param [String] expression
|
|
27
|
+
# @param [String, nil] expression
|
|
27
28
|
# @see https://www.notion.so/hkob/EquationObject-cd50126fce544ad5bb76463a4269859b#155800e2c69d4676a2d74572ed8f0de8
|
|
28
29
|
def expression=(expression)
|
|
30
|
+
expression ||= ""
|
|
29
31
|
@expression = expression
|
|
30
32
|
@options["plain_text"] = expression
|
|
31
33
|
@will_update = true
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module NotionRubyMapping
|
|
4
|
-
#
|
|
4
|
+
# Represents file information shared by file-based blocks and file properties.
|
|
5
|
+
#
|
|
6
|
+
# Use the owning block or property to update a file. This object is primarily
|
|
7
|
+
# intended for inspecting the current file representation.
|
|
5
8
|
class FileObject
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
+
# Creates a file representation.
|
|
10
|
+
#
|
|
11
|
+
# @param url [String, nil] external file URL
|
|
12
|
+
# @param file_upload_object [FileUploadObject, nil] uploaded file
|
|
13
|
+
# @param json [Hash, nil] file JSON returned by the Notion API
|
|
14
|
+
# @raise [ArgumentError] if no file source is provided
|
|
15
|
+
# @api private
|
|
16
|
+
def initialize(url: nil, file_upload_object: nil, json: nil)
|
|
9
17
|
if url
|
|
10
18
|
@type = "external"
|
|
11
19
|
@url = url
|
|
@@ -17,49 +25,66 @@ module NotionRubyMapping
|
|
|
17
25
|
@url = json[@type]["url"]
|
|
18
26
|
@expiry_time = json[@type]["expiry_time"]
|
|
19
27
|
else
|
|
20
|
-
raise
|
|
28
|
+
raise ArgumentError, "FileObject requires url:, file_upload_object:, or json:"
|
|
21
29
|
end
|
|
22
|
-
@will_update = false
|
|
23
30
|
end
|
|
24
|
-
|
|
31
|
+
# @return [String, nil] current file URL
|
|
32
|
+
attr_reader :url
|
|
25
33
|
|
|
26
|
-
# @
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
# @return [String] file type: `"external"`, `"file"`, or `"file_upload"`
|
|
35
|
+
attr_reader :type
|
|
36
|
+
|
|
37
|
+
# @return [FileUploadObject, nil] current file upload object
|
|
38
|
+
attr_reader :file_upload_object
|
|
39
|
+
|
|
40
|
+
# Converts a supported file value to a file object.
|
|
41
|
+
#
|
|
42
|
+
# @param value [String, FileUploadObject, FileObject]
|
|
43
|
+
# external URL, uploaded file, or existing file object
|
|
44
|
+
# @return [FileObject] converted file object
|
|
45
|
+
# @api private
|
|
46
|
+
def self.file_object(value)
|
|
47
|
+
if value.is_a? FileUploadObject
|
|
48
|
+
FileObject.new file_upload_object: value
|
|
49
|
+
elsif value.is_a? FileObject
|
|
50
|
+
value
|
|
34
51
|
else
|
|
35
|
-
FileObject.new url:
|
|
52
|
+
FileObject.new url: value
|
|
36
53
|
end
|
|
37
54
|
end
|
|
38
55
|
|
|
39
|
-
#
|
|
56
|
+
# Checks whether the file uses an external URL.
|
|
57
|
+
#
|
|
58
|
+
# @return [Boolean] true if the file type is `"external"`
|
|
40
59
|
def external?
|
|
41
60
|
@type == "external"
|
|
42
61
|
end
|
|
43
62
|
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
|
|
63
|
+
# Replaces the current representation with an uploaded file.
|
|
64
|
+
#
|
|
65
|
+
# @param file_upload_object [FileUploadObject] uploaded file
|
|
66
|
+
# @api private
|
|
67
|
+
def file_upload_object=(file_upload_object)
|
|
68
|
+
@file_upload_object = file_upload_object
|
|
47
69
|
@type = "file_upload"
|
|
48
70
|
@url = nil
|
|
49
71
|
@expiry_time = nil
|
|
50
|
-
@will_update = true
|
|
51
72
|
end
|
|
52
73
|
|
|
53
|
-
#
|
|
54
|
-
#
|
|
74
|
+
# Replaces the current representation with an external URL.
|
|
75
|
+
#
|
|
76
|
+
# @param url [String] external file URL
|
|
77
|
+
# @api private
|
|
55
78
|
def url=(url)
|
|
56
79
|
@url = url
|
|
57
80
|
@type = "external"
|
|
58
81
|
@expiry_time = nil
|
|
59
|
-
@will_update = true
|
|
60
82
|
end
|
|
61
83
|
|
|
62
|
-
#
|
|
84
|
+
# Builds the Notion API file JSON.
|
|
85
|
+
#
|
|
86
|
+
# @return [Hash{String => Object}] file JSON
|
|
87
|
+
# @api private
|
|
63
88
|
def property_values_json
|
|
64
89
|
if @type == "file_upload"
|
|
65
90
|
{
|
|
@@ -1,86 +1,132 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
else
|
|
12
|
-
raise StandardError, "FileUploadObject requires a valid file name: #{fname}" unless File.exist?(fname)
|
|
3
|
+
module NotionRubyMapping
|
|
4
|
+
# Uploads a local or external file to Notion.
|
|
5
|
+
#
|
|
6
|
+
# Creating an instance starts the upload immediately. Large local files are
|
|
7
|
+
# split into multiple parts before being uploaded.
|
|
8
|
+
class FileUploadObject
|
|
9
|
+
# Maximum size of each upload part.
|
|
10
|
+
MAX_SIZE = 10 * 1024 * 1024
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
# Creates and uploads a file.
|
|
13
|
+
#
|
|
14
|
+
# When +external_url+ is provided, +fname+ is used as the filename sent to
|
|
15
|
+
# Notion. Otherwise, +fname+ must point to an existing local file.
|
|
16
|
+
#
|
|
17
|
+
# @param fname [String] local file path or filename
|
|
18
|
+
# @param external_url [String, nil] URL of a file for Notion to import
|
|
19
|
+
# @raise [StandardError] if the local file does not exist
|
|
20
|
+
# @raise [ArgumentError] if the local file is empty
|
|
21
|
+
def initialize(fname:, external_url: nil)
|
|
22
|
+
@fname = fname
|
|
23
|
+
if external_url
|
|
24
|
+
payload = {mode: "external_url", external_url: external_url, filename: fname}
|
|
25
|
+
create payload
|
|
25
26
|
else
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
raise StandardError, "FileUploadObject requires a valid file name: #{fname}" unless File.exist?(fname)
|
|
28
|
+
|
|
29
|
+
@file_size = File.size fname
|
|
30
|
+
raise ArgumentError, "FileUploadObject requires a non-empty file: #{fname}" if @file_size.zero?
|
|
31
|
+
|
|
32
|
+
@number_of_parts = (@file_size - 1) / MAX_SIZE + 1
|
|
33
|
+
payload = if @number_of_parts == 1
|
|
34
|
+
{}
|
|
35
|
+
else
|
|
36
|
+
{number_of_parts: @number_of_parts, mode: "multi_part",
|
|
37
|
+
filename: File.basename(@fname)}
|
|
38
|
+
end
|
|
39
|
+
create payload
|
|
40
|
+
if @number_of_parts == 1
|
|
41
|
+
single_file_upload
|
|
42
|
+
else
|
|
43
|
+
@temp_files = FileUploadObject.split_to_small_files(@fname, MAX_SIZE)
|
|
44
|
+
@temp_files.each_with_index do |temp_file, i|
|
|
45
|
+
single_file_upload temp_file.path, i + 1
|
|
46
|
+
temp_file.close
|
|
47
|
+
temp_file.unlink
|
|
48
|
+
end
|
|
49
|
+
NotionRubyMapping::NotionCache.instance.complete_a_file_upload_request @id
|
|
31
50
|
end
|
|
32
|
-
NotionRubyMapping::NotionCache.instance.complete_a_file_upload_request @id
|
|
33
51
|
end
|
|
34
52
|
end
|
|
35
|
-
|
|
36
|
-
|
|
53
|
+
# @return [String] file upload ID
|
|
54
|
+
attr_reader :id
|
|
37
55
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
nc = NotionRubyMapping::NotionCache.instance
|
|
41
|
-
response = nc.create_file_upload_request(payload)
|
|
42
|
-
@id = nc.hex_id response["id"]
|
|
43
|
-
@status = response["status"]
|
|
44
|
-
end
|
|
56
|
+
# @return [String] local file path or filename
|
|
57
|
+
attr_reader :fname
|
|
45
58
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
response = nc.file_upload_request @id
|
|
49
|
-
@status = response["status"]
|
|
50
|
-
self
|
|
51
|
-
end
|
|
59
|
+
# @return [String] last retrieved upload status
|
|
60
|
+
attr_reader :status
|
|
52
61
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
# Creates a file upload through the Notion API.
|
|
63
|
+
#
|
|
64
|
+
# @param payload [Hash] file upload creation payload
|
|
65
|
+
# @return [String] initial upload status
|
|
66
|
+
# @api private
|
|
67
|
+
def create(payload)
|
|
68
|
+
nc = NotionRubyMapping::NotionCache.instance
|
|
69
|
+
response = nc.create_file_upload_request(payload)
|
|
70
|
+
@id = nc.hex_id response["id"]
|
|
71
|
+
@status = response["status"]
|
|
62
72
|
end
|
|
63
|
-
nc = NotionRubyMapping::NotionCache.instance
|
|
64
|
-
response = nc.send_file_upload_request fname, @id, options
|
|
65
|
-
return if nc.hex_id(response["id"]) == @id && response["status"] == status
|
|
66
73
|
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
# Reloads the current upload status from Notion.
|
|
75
|
+
#
|
|
76
|
+
# @return [FileUploadObject] self
|
|
77
|
+
def reload
|
|
78
|
+
nc = NotionRubyMapping::NotionCache.instance
|
|
79
|
+
response = nc.file_upload_request @id
|
|
80
|
+
@status = response["status"]
|
|
81
|
+
self
|
|
82
|
+
end
|
|
69
83
|
|
|
70
|
-
|
|
71
|
-
|
|
84
|
+
# Uploads a local file or one part of a multipart upload.
|
|
85
|
+
#
|
|
86
|
+
# @param fname [String] path of the file to upload
|
|
87
|
+
# @param part_number [Integer] multipart part number
|
|
88
|
+
# @return [void]
|
|
89
|
+
# @raise [StandardError] if the upload response is invalid
|
|
90
|
+
# @api private
|
|
91
|
+
def single_file_upload(fname = @fname, part_number = 0)
|
|
92
|
+
if @number_of_parts > 1
|
|
93
|
+
options = {"part_number" => part_number}
|
|
94
|
+
status = "pending"
|
|
95
|
+
else
|
|
96
|
+
options = {}
|
|
97
|
+
status = "uploaded"
|
|
98
|
+
end
|
|
99
|
+
nc = NotionRubyMapping::NotionCache.instance
|
|
100
|
+
response = nc.send_file_upload_request fname, @id, options
|
|
101
|
+
return if nc.hex_id(response["id"]) == @id && response["status"] == status
|
|
102
|
+
|
|
103
|
+
raise StandardError, "File upload failed: #{response}"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Splits a file into temporary files.
|
|
107
|
+
#
|
|
108
|
+
# The caller is responsible for closing and deleting the returned files.
|
|
109
|
+
#
|
|
110
|
+
# @param org_file [String] path of the original file
|
|
111
|
+
# @param max_size [Integer] maximum size of each part in bytes
|
|
112
|
+
# @return [Array<Tempfile>] temporary file parts
|
|
113
|
+
# @raise [StandardError] if the original file does not exist
|
|
114
|
+
# @api private
|
|
115
|
+
def self.split_to_small_files(org_file, max_size = MAX_SIZE)
|
|
116
|
+
raise StandardError, "File does not exist: #{org_file}" unless File.exist?(org_file)
|
|
72
117
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
118
|
+
temp_files = []
|
|
119
|
+
File.open(org_file, "rb") do |file|
|
|
120
|
+
until file.eof?
|
|
121
|
+
chunk = file.read(max_size)
|
|
122
|
+
temp_file = Tempfile.new("part_")
|
|
123
|
+
temp_file.binmode
|
|
124
|
+
temp_file.write(chunk)
|
|
125
|
+
temp_file.rewind
|
|
126
|
+
temp_files << temp_file
|
|
127
|
+
end
|
|
82
128
|
end
|
|
129
|
+
temp_files
|
|
83
130
|
end
|
|
84
|
-
temp_files
|
|
85
131
|
end
|
|
86
132
|
end
|