Skip to content

Commit 49690c7

Browse files
authored
Merge branch 'main' into maxi297/asyncretriever-with-concurrent-cursor
2 parents 03d1958 + 5da0a7f commit 49690c7

File tree

3 files changed

+168
-4
lines changed

3 files changed

+168
-4
lines changed

.github/workflows/connector-tests.yml

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ concurrency:
2525
jobs:
2626
cdk_changes:
2727
name: Get Changes
28-
runs-on: ubuntu-24.04
28+
runs-on: ubuntu-22.04
2929
permissions:
3030
statuses: write
3131
pull-requests: read
@@ -62,7 +62,7 @@ jobs:
6262
# Forked PRs are handled by the community_ci.yml workflow
6363
# If the condition is not met the job will be skipped (it will not fail)
6464
# runs-on: connector-test-large
65-
runs-on: ubuntu-24.04
65+
runs-on: ubuntu-22.04
6666
timeout-minutes: 360 # 6 hours
6767
strategy:
6868
fail-fast: false
@@ -96,6 +96,8 @@ jobs:
9696
name: "Check: '${{matrix.connector}}' (skip=${{needs.cdk_changes.outputs['src'] == 'false' || needs.cdk_changes.outputs[matrix.cdk_extra] == 'false'}})"
9797
permissions:
9898
checks: write
99+
contents: write # Required for creating commit statuses
100+
pull-requests: read
99101
steps:
100102
- name: Abort if extra not changed (${{matrix.cdk_extra}})
101103
id: no_changes
@@ -127,6 +129,22 @@ jobs:
127129
uses: actions/setup-python@v5
128130
with:
129131
python-version: "3.10"
132+
# Create initial pending status for test report
133+
- name: Create Pending Test Report Status
134+
if: steps.no_changes.outputs.status != 'cancelled'
135+
env:
136+
GH_TOKEN: ${{ secrets.GH_PAT_MAINTENANCE_OCTAVIA }}
137+
run: |
138+
HEAD_SHA="${{ github.event.pull_request.head.sha || github.sha }}"
139+
gh api \
140+
--method POST \
141+
-H "Accept: application/vnd.github+json" \
142+
-H "X-GitHub-Api-Version: 2022-11-28" \
143+
repos/${{ github.repository }}/statuses/$HEAD_SHA \
144+
-f state="pending" \
145+
-f description="Running connector tests..." \
146+
-f context="${{ matrix.connector }} Test Report"
147+
130148
- name: Test Connector
131149
if: steps.no_changes.outputs.status != 'cancelled'
132150
timeout-minutes: 90
@@ -173,6 +191,39 @@ jobs:
173191
echo "success=${success}" >> $GITHUB_OUTPUT
174192
echo "html_report_url=${html_report_url}" >> $GITHUB_OUTPUT
175193
194+
# Update the test report status with results
195+
- name: Update Test Report Status
196+
if: always() && steps.no_changes.outputs.status != 'cancelled' && steps.evaluate_output.outcome == 'success'
197+
env:
198+
GH_TOKEN: ${{ secrets.GH_PAT_MAINTENANCE_OCTAVIA }}
199+
run: |
200+
HEAD_SHA="${{ github.event.pull_request.head.sha || github.sha }}"
201+
gh api \
202+
--method POST \
203+
-H "Accept: application/vnd.github+json" \
204+
-H "X-GitHub-Api-Version: 2022-11-28" \
205+
repos/${{ github.repository }}/statuses/$HEAD_SHA \
206+
-f state="${{ steps.evaluate_output.outputs.success == 'true' && 'success' || 'failure' }}" \
207+
-f target_url="${{ steps.evaluate_output.outputs.html_report_url }}" \
208+
-f description="Click Details to view the test report" \
209+
-f context="${{ matrix.connector }} Test Report"
210+
211+
# Create failure status if report generation failed
212+
- name: Create Report Generation Failed Status
213+
if: always() && steps.no_changes.outputs.status != 'cancelled' && steps.evaluate_output.outcome != 'success'
214+
env:
215+
GH_TOKEN: ${{ secrets.GH_PAT_MAINTENANCE_OCTAVIA }}
216+
run: |
217+
HEAD_SHA="${{ github.event.pull_request.head.sha || github.sha }}"
218+
gh api \
219+
--method POST \
220+
-H "Accept: application/vnd.github+json" \
221+
-H "X-GitHub-Api-Version: 2022-11-28" \
222+
repos/${{ github.repository }}/statuses/$HEAD_SHA \
223+
-f state="failure" \
224+
-f description="Failed to run connector tests." \
225+
-f context="${{ matrix.connector }} Test Report"
226+
176227
# Upload the job output to the artifacts
177228
- name: Upload Job Output
178229
id: upload_job_output

airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,12 @@ def set_initial_state(self, stream_state: StreamState) -> None:
296296

297297
if not parent_state and incremental_dependency:
298298
# Attempt to retrieve child state
299-
substream_state = list(stream_state.values())
300-
substream_state = substream_state[0] if substream_state else {} # type: ignore [assignment] # Incorrect type for assignment
299+
substream_state_values = list(stream_state.values())
300+
substream_state = substream_state_values[0] if substream_state_values else {}
301+
# Filter out per partition state. Because we pass the state to the parent stream in the format {cursor_field: substream_state}
302+
if isinstance(substream_state, (list, dict)):
303+
substream_state = {}
304+
301305
parent_state = {}
302306

303307
# Copy child state to parent streams with incremental dependencies

unit_tests/sources/declarative/partition_routers/test_substream_partition_router.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,115 @@ def test_substream_partition_router_invalid_parent_record_type():
402402
_ = [s for s in partition_router.stream_slices()]
403403

404404

405+
@pytest.mark.parametrize(
406+
"initial_state, expected_parent_state",
407+
[
408+
# Case 1: Empty initial state, no parent state expected
409+
({}, {}),
410+
# Case 2: Initial state with no `parent_state`, migrate `updated_at` to `parent_stream_cursor`
411+
(
412+
{"updated_at": "2023-05-27T00:00:00Z"},
413+
{"parent_stream_cursor": "2023-05-27T00:00:00Z"},
414+
),
415+
# Case 3: Initial state with global `state`, no migration expected
416+
(
417+
{"state": {"updated": "2023-05-27T00:00:00Z"}},
418+
{},
419+
),
420+
# Case 4: Initial state with per-partition `states`, no migration expected
421+
(
422+
{
423+
"states": [
424+
{
425+
"partition": {
426+
"issue_id": "10012",
427+
"parent_slice": {
428+
"parent_slice": {},
429+
"project_id": "10000",
430+
},
431+
},
432+
"cursor": {"updated": "2021-01-01T00:00:00+0000"},
433+
},
434+
{
435+
"partition": {
436+
"issue_id": "10019",
437+
"parent_slice": {
438+
"parent_slice": {},
439+
"project_id": "10000",
440+
},
441+
},
442+
"cursor": {"updated": "2021-01-01T00:00:00+0000"},
443+
},
444+
{
445+
"partition": {
446+
"issue_id": "10000",
447+
"parent_slice": {
448+
"parent_slice": {},
449+
"project_id": "10000",
450+
},
451+
},
452+
"cursor": {"updated": "2021-01-01T00:00:00+0000"},
453+
},
454+
]
455+
},
456+
{},
457+
),
458+
# Case 5: Initial state with `parent_state`, existing parent state persists
459+
(
460+
{
461+
"parent_state": {
462+
"parent_stream_name1": {"parent_stream_cursor": "2023-05-27T00:00:00Z"},
463+
},
464+
},
465+
{"parent_stream_cursor": "2023-05-27T00:00:00Z"},
466+
),
467+
],
468+
ids=[
469+
"empty_initial_state",
470+
"initial_state_no_parent_legacy_state",
471+
"initial_state_no_parent_global_state",
472+
"initial_state_no_parent_per_partition_state",
473+
"initial_state_with_parent_state",
474+
],
475+
)
476+
def test_set_initial_state(initial_state, expected_parent_state):
477+
"""
478+
Test the `set_initial_state` method of SubstreamPartitionRouter.
479+
480+
This test verifies that the method correctly handles different initial state formats
481+
and sets the appropriate parent stream state.
482+
"""
483+
parent_stream = MockStream(
484+
slices=[{}],
485+
records=[],
486+
name="parent_stream_name1",
487+
cursor_field="parent_stream_cursor",
488+
)
489+
parent_stream.state = {}
490+
parent_stream_config = ParentStreamConfig(
491+
stream=parent_stream,
492+
parent_key="id",
493+
partition_field="parent_stream_id",
494+
parameters={},
495+
config={},
496+
incremental_dependency=True,
497+
)
498+
499+
partition_router = SubstreamPartitionRouter(
500+
parent_stream_configs=[parent_stream_config],
501+
parameters={},
502+
config={},
503+
)
504+
505+
partition_router.set_initial_state(initial_state)
506+
507+
# Assert the state of the parent stream
508+
assert parent_stream.state == expected_parent_state, (
509+
f"Unexpected parent state. Initial state: {initial_state}, "
510+
f"Expected: {expected_parent_state}, Got: {parent_stream.state}"
511+
)
512+
513+
405514
@pytest.mark.parametrize(
406515
"parent_stream_request_parameters, expected_req_params, expected_headers, expected_body_json, expected_body_data",
407516
[

0 commit comments

Comments
 (0)