Skip to content

Commit e7ce8a0

Browse files
Fix: source-notion depth leak (#69780)
Co-authored-by: Alfredo Garcia <[email protected]>
1 parent 9129cbe commit e7ce8a0

File tree

4 files changed

+65
-2
lines changed

4 files changed

+65
-2
lines changed

airbyte-integrations/connectors/source-notion/components.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def read_records(
8585
cursor_slice=stream_slice.cursor_slice,
8686
)
8787
yield from self.read_records(records_schema, child_stream_slice)
88+
self.current_block_depth -= 1
8889

8990
if "parent" in stream_data:
9091
stream_data["parent"]["sequence_number"] = sequence_number

airbyte-integrations/connectors/source-notion/metadata.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ data:
1010
connectorSubtype: api
1111
connectorType: source
1212
definitionId: 6e00b415-b02e-4160-bf02-58176a0ae687
13-
dockerImageTag: 3.3.6
13+
dockerImageTag: 3.3.7
1414
dockerRepository: airbyte/source-notion
1515
documentationUrl: https://docs.airbyte.com/integrations/sources/notion
1616
externalDocumentationUrls:

airbyte-integrations/connectors/source-notion/unit_tests/test_components.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#
22
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
33
#
4-
from unittest.mock import MagicMock
4+
from unittest.mock import MagicMock, patch
55

66
import pytest
77

88
from airbyte_cdk.models import ConfiguredAirbyteCatalogSerializer
9+
from airbyte_cdk.sources.declarative.retrievers.simple_retriever import SimpleRetriever
10+
from airbyte_cdk.sources.declarative.types import Record, StreamSlice
911
from airbyte_cdk.test.entrypoint_wrapper import read
1012
from airbyte_cdk.test.state_builder import StateBuilder
1113
from unit_tests.conftest import get_source
@@ -97,6 +99,65 @@ def test_notion_properties_transformation(components_module):
9799
]
98100

99101

102+
def test_blocks_retriever_depth_restored_after_children(components_module):
103+
"""
104+
Test to verify that current_block_depth is correctly incremented and decremented during recursive calls.
105+
106+
This test creates a scenario with two sibling blocks at the same level, both having children:
107+
- Block A (has_children=True)
108+
- Block A1 (has_children=False)
109+
- Block B (has_children=True)
110+
- Block B1 (has_children=False)
111+
112+
The depth counter should properly increment when entering a child level and decrement when
113+
returning to the parent level. This ensures sibling blocks are processed at the correct depth.
114+
"""
115+
116+
retriever = components_module.BlocksRetriever(
117+
name="test",
118+
primary_key=["id"],
119+
requester=MagicMock(),
120+
record_selector=MagicMock(),
121+
paginator=MagicMock(),
122+
config={},
123+
parameters={},
124+
)
125+
retriever._paginator = MagicMock()
126+
depth_tracker = []
127+
128+
block_a = Record(
129+
data={"id": "block-a", "has_children": True}, stream_name="blocks", associated_slice=StreamSlice(partition={}, cursor_slice={})
130+
)
131+
block_a1 = Record(
132+
data={"id": "block-a1", "has_children": False}, stream_name="blocks", associated_slice=StreamSlice(partition={}, cursor_slice={})
133+
)
134+
block_b = Record(
135+
data={"id": "block-b", "has_children": True}, stream_name="blocks", associated_slice=StreamSlice(partition={}, cursor_slice={})
136+
)
137+
block_b1 = Record(
138+
data={"id": "block-b1", "has_children": False}, stream_name="blocks", associated_slice=StreamSlice(partition={}, cursor_slice={})
139+
)
140+
141+
def mock_super_read_records(self, records_schema, stream_slice=None):
142+
depth_tracker.append(retriever.current_block_depth)
143+
144+
if stream_slice is None or not stream_slice.partition:
145+
yield block_a
146+
yield block_b
147+
elif stream_slice.partition.get("block_id") == "block-a":
148+
yield block_a1
149+
elif stream_slice.partition.get("block_id") == "block-b":
150+
yield block_b1
151+
152+
with patch.object(SimpleRetriever, "read_records", mock_super_read_records):
153+
stream_slice = StreamSlice(partition={}, cursor_slice={})
154+
results = list(retriever.read_records({}, stream_slice))
155+
156+
assert len(depth_tracker) == 3, f"Expected 3 depth measurements, got {len(depth_tracker)}: {depth_tracker}"
157+
assert depth_tracker == [0, 1, 1], f"Depth should be properly managed. Depth tracker: {depth_tracker}"
158+
assert retriever.current_block_depth == 0, f"Final depth should be 0, got {retriever.current_block_depth}"
159+
160+
100161
def test_blocks_retriever(requests_mock):
101162
page_response_data = {
102163
"object": "list",

docs/integrations/sources/notion.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ The connector is restricted by Notion [request limits](https://developers.notion
119119

120120
| Version | Date | Pull Request | Subject |
121121
|:------------|:-----------|:---------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
122+
| 3.3.7 | 2025-11-24 | [69375](https://github.com/airbytehq/airbyte/pull/69780) | Fix bug in current_block_depth |
122123
| 3.3.6 | 2025-11-18 | [69375](https://github.com/airbytehq/airbyte/pull/69375) | Update dependencies |
123124
| 3.3.5 | 2025-10-29 | [68750](https://github.com/airbytehq/airbyte/pull/68750) | Update dependencies |
124125
| 3.3.4 | 2025-10-21 | [68399](https://github.com/airbytehq/airbyte/pull/68399) | Update dependencies |

0 commit comments

Comments
 (0)