Skip to content

fix(zoom): derive start date from sync time; make manifest strictly valid - #1746

Merged
mitasovr merged 10 commits into
constructorfabric:mainfrom
mitasovr:claude/airbyte-job-529-logs-26ea7d
Jul 13, 2026
Merged

fix(zoom): derive start date from sync time; make manifest strictly valid#1746
mitasovr merged 10 commits into
constructorfabric:mainfrom
mitasovr:claude/airbyte-job-529-logs-26ea7d

Conversation

@mitasovr

@mitasovr mitasovr commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Problem

Zoom syncs on dev-vhc fail at the pre-sync connection check (e.g. Airbyte job 529):

'GET' https://api.zoom.us/v2/metrics/meetings?type=past&from=2026-01-01&to=2026-01-30 → 400
{'code': 300, 'message': 'The request can only be queried for a month that falls within the last six months.'}

The Zoom Dashboard API (/v2/metrics/*) only serves the last six months. The meetings stream built its first slice from the static zoom_start_date config (2026-01-01), and the Airbyte check command receives no state — so once the configured date fell out of the window, every sync died at check, even though the connection's saved cursor (end_time: 2026-06-29) was perfectly valid.

Fix

  • meetings.start_datetime is now computed: now_utc() - P150D (~5 months — a safety margin inside Zoom's window, whose month-granularity semantics are ambiguous at the boundary). Same pattern as m365.
  • zoom_start_date removed from the connector spec, descriptor required_fields, secret example, and README. Existing K8s secrets that still carry the field stay valid (additionalProperties: true), so no secret rotation is needed.
  • Descriptor version bumped to 1.1.0 so reconcile republishes the manifest (equal versions → noop).

Also: Builder-UI strict validation

validate-strict failed on main with 3 errors (and more masked behind them — the validator surfaces one leaf error per stream). The strict validator performs no $ref resolution, so any typed-object slot holding a $ref fails. Fixed by inlining:

  • request_body in both login_requesters,
  • error_handler / paginator in users,
  • authenticator in participants,
  • the whole-object parent $ref: "#/streams/1" on participants replaced with an inline _meetings parent stream (youtrack pattern).

Note: collaboration/m365 currently fails validate-strict with the same login_requester.request_body $ref pattern (4 streams) — left out of scope here.

Verification

  • source.sh validate-strict collaboration/zoom — strictly valid
  • source.sh validate collaboration/zoom — manifest valid
  • source.sh check with real dev-vhc credentials — Check succeeded (first slice now inside the window)
  • Full source.sh read with real credentials — meetings 24 525, users 375, participants 77 085 records; the inline _meetings parent routes partitions correctly. (One transient 400 mid-pagination of a 30-day slice — Zoom's next_page_token 15-min expiry during the 150-day cold read; pagination behavior unchanged from main.)

Existing connection state is untouched; syncs continue from their saved cursor.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Updates

    • Simplified Zoom connector setup by removing the zoom_start_date configuration requirement, and updated the connector version, documentation, and credential secret example accordingly.
    • Zoom meetings now automatically backfill a recent window before switching to incremental sync from saved progress.
  • Improvements

    • Enhanced Zoom API sync reliability with improved retry/backoff and explicit pagination handling.
    • Refined Zoom bronze placeholder behavior to better preserve row uniqueness during merges.
  • Tests

    • Added/expanded end-to-end metric tests for Zoom and Teams+Zoom cross-source behavior, plus updated related fixture schemas/templates.
  • Chores

    • Updated e2e session startup truncation and metric coverage skip logic.

…alid

The Zoom Dashboard API (/v2/metrics/*) only serves the last six months.
With a fixed zoom_start_date the pre-sync connection check always probes
the first slice from that date, and started failing with HTTP 400 (code
300) once the configured date fell out of the window — every sync job
died at check (dev-vhc job 529) even though the connection state was
well inside the window. Replace the config knob with a computed start
(now - 150 days, a safety margin inside the window) and drop
zoom_start_date from the spec, descriptor required_fields, secret
example and README. Extra zoom_start_date fields in existing K8s
secrets remain harmless (additionalProperties: true).

Also make the manifest Builder-UI strictly valid (validate-strict):
inline the typed-object $refs (login_requester request_body, error
handlers, paginators, authenticators) and replace the whole-object
parent $ref "#/streams/1" on participants with an inline _meetings
parent stream, following the youtrack pattern.

Bump descriptor version to 1.1.0 so reconcile republishes the manifest.

Verified: validate-strict and validate green; live check green; full
read against dev-vhc credentials — meetings 24525, users 375,
participants 77085 records.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Roman Mitasov <Roman.Mitasov@constructor.tech>
@mitasovr
mitasovr requested a review from a team as a code owner July 13, 2026 08:31
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@mitasovr, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 23 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 584c7147-20dc-479e-a647-b1e1384692cd

📥 Commits

Reviewing files that changed from the base of the PR and between dc7af2d and a96de1f.

📒 Files selected for processing (4)
  • src/ingestion/connectors/collaboration/zoom/tests/config.py
  • src/ingestion/connectors/collaboration/zoom/tests/test_meetings.py
  • src/ingestion/connectors/collaboration/zoom/tests/test_participants.py
  • src/ingestion/connectors/collaboration/zoom/tests/test_users.py
📝 Walkthrough

Walkthrough

The Zoom connector removes zoom_start_date, computes 150-day initial backfills, inlines requester definitions, aligns Bronze Zoom keys with promoted tables, and adds connector and end-to-end coverage for Zoom and cross-source collaboration metrics.

Changes

Zoom connector and metric validation

Layer / File(s) Summary
Connector sync contract and requesters
src/ingestion/connectors/collaboration/zoom/..., src/ingestion/secrets/connectors/zoom.yaml.example
Removes zoom_start_date, inlines OAuth and error-handling configuration, and uses computed 150-day synchronization boundaries for meetings and participants.
Bronze Zoom keys and reusable fixtures
src/ingestion/scripts/create-bronze-placeholders.sh, src/ingestion/tests/e2e/metrics/schemas/..., src/ingestion/tests/e2e/metrics/templates/zoom.yaml, src/ingestion/connectors/collaboration/zoom/tests/fixtures/*
Bronze Zoom tables, schemas, templates, and fixtures use nullable unique_key values and include meeting-level records.
Connector test coverage
src/ingestion/connectors/collaboration/zoom/tests/...
Adds mocked tests for authentication, pagination, transformations, retries, incremental state, request windows, schemas, and participant partitioning.
E2E isolation and collaboration metrics
src/ingestion/tests/e2e/...
Truncates Zoom Bronze tables between sessions, enables Zoom metric coverage, and adds Teams-plus-Zoom and Zoom-only scenarios covering aggregation, deduplication, stitching, distributions, and empty windows.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.62% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: deriving the Zoom start date from sync time and making the manifest strictly valid.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/ingestion/connectors/collaboration/zoom/connector.yaml`:
- Around line 117-163: Restore the users stream’s request_parameters in the
inlined requester, including the configured page_size parameter, while
preserving the existing GET URL, error handling, and pagination configuration.
- Around line 572-683: Update the ParentStreamConfig for the incremental
_meetings stream to set incremental_dependency: true. Ensure this configuration
is applied to the participants fan-out relationship so parent incremental state
advances with child processing while preserving the existing 150-day window and
pagination settings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e59977e7-af1e-4255-9b91-79bca44a3386

📥 Commits

Reviewing files that changed from the base of the PR and between b3ecca9 and dc6d8bf.

📒 Files selected for processing (4)
  • src/ingestion/connectors/collaboration/zoom/README.md
  • src/ingestion/connectors/collaboration/zoom/connector.yaml
  • src/ingestion/connectors/collaboration/zoom/descriptor.yaml
  • src/ingestion/secrets/connectors/zoom.yaml.example
💤 Files with no reviewable changes (1)
  • src/ingestion/secrets/connectors/zoom.yaml.example

Comment thread src/ingestion/connectors/collaboration/zoom/connector.yaml
Comment thread src/ingestion/connectors/collaboration/zoom/connector.yaml

@ktursunov ktursunov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zoom metrics are not covered with tests

Roman Mitasov and others added 4 commits July 13, 2026 16:22
… zoom fixes

Review follow-ups (CodeRabbit on constructorfabric#1746):
- users stream now sends page_size (was never applied to this stream, also
  not on main — inherited from before the inlining)
- participants ParentStreamConfig sets incremental_dependency: true so the
  substream persists/reuses the _meetings parent cursor (verified the
  SubstreamPartitionRouter parent-state mechanics in CDK 6.60.9 sources)
- both changes verified with a live full read: meetings 24780 / users 374 /
  participants 77093

e2e coverage for every FE metric fed by Zoom data (metric catalog keys with
source_tag zoom):
- collab_zoom_meeting_hours: per-day multi-meeting sum, re-emit dedup,
  camera/share modality gating
- collab_zoom_meetings: distinct-meeting count incl. session stitching (a
  2-min host-drop rejoin counts as ONE meeting)
- collab_meeting_hours_zoom_cross / collab_meetings_count_zoom_cross:
  Teams+Zoom additivity (distribution shifted so a dropped Zoom leg fails
  every asserted field)
- collab_meeting_free_zoom: Zoom activity cancels a meeting-free day
- zoom_meeting_hours / zoom_meetings removed from the coverage-gate
  SKIP_LIST — coverage is now enforced

Two e2e-rig bugs found and fixed on the way:
- bronze_zoom placeholders diverged from the promoted prod shape: RMT
  ORDER BY email (participants) / uuid (meetings) with no unique_key column,
  silently collapsing all of a person's rows on merge — deployed tables are
  unaffected (promote_bronze_to_rmt produces ORDER BY unique_key; verified
  on dev-vhc), but the rig runs on the placeholder and could not seed more
  than one meeting per person. Placeholders now mirror the promoted shape;
  the stale workaround note in collab_meetings_count.test.yaml is rewritten.
- warm-rerun contamination: bronze_zoom.meetings is read (via the
  zoom__meeting_sessions upstream) by tests that seed only participants, so
  it was neither seed-truncated nor ledger-truncated; a prior session's
  leftover rows were re-appended into zoom__meeting_sessions on every such
  build, tripping its unique dbt test on the second one. bronze_zoom.* added
  to the conftest session-start truncate list.

Verified: cold full e2e suite 155 passed (CI scenario); the previously
failing warm sequence (zoom subset, then full suite, no down) now 5 + 155
passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Roman Mitasov <Roman.Mitasov@constructor.tech>
Mock-server tests for collaboration/zoom modeled on the task-tracking/jira
reference suite (15 tests: users 6, meetings 5, participants 4), including
the job-529 regression pin: with the clock frozen, every meetings slice is
matched by an exact from/to matcher and the first slice starts at now-150d
— a connector regressing to a static / out-of-window start date (the
original zoom_start_date=2026-01-01 failure) issues a request no matcher
accepts and the test fails. Also covered: S2S OAuth token exchange
(byte-exact form body), per-stream stamping/schema/pagination/empty-page,
429 retry, incremental state + P7D-lookback resume filtering, participants
partition-per-meeting with uuid URL-escaping.

The suite disproved one review follow-up from this PR:
incremental_dependency on the participants ParentStreamConfig is a silent
no-op — parent_state only piggybacks on an incremental child's state, and
participants is full-refresh (emits only __ab_no_cursor_state_message; the
earlier live read agrees). The flag is removed again with an explanatory
manifest comment, and test_full_refresh_substream_emits_no_cursor_state
pins the actual contract.

CDK facts the suite encodes: the concurrent cursor absorbs the window tail
into the last slice while the synchronous parent-read path emits a 1-day
tail slice (both layouts in tests/config.py); POST matchers compare the
body byte-exact.

Verified: harness run 35 passed / 1 skipped (pre-existing jira schema-drift
skip); validate-strict + validate green after the manifest change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Roman Mitasov <Roman.Mitasov@constructor.tech>
@mitasovr
mitasovr requested a review from ktursunov July 13, 2026 14:00
Comment thread src/ingestion/connectors/collaboration/zoom/tests/test_meetings.py
from pathlib import Path

# Local builder modules (config.py) are importable under --import-mode=importlib.
sys.path.insert(0, str(Path(__file__).parent))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why claude likes to modify syspath? I think there is easier import fix like from . import...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like copypaste
will fix it later

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now it is later :) Still not fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/ingestion/connectors/collaboration/zoom/connector.yaml (1)

580-691: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Significant duplication of requester/auth/error/paginator config across 4 stream definitions.

The _meetings nested stream (lines 588–658) is a near-exact copy of the meetings requester (lines 303–373), and the participants requester (lines 707–768) duplicates the same authenticator/error_handler pattern a third time. While Builder-UI strict validation requires inlining typed-object references, this means any change to auth, error handling, or pagination must be mirrored in 4 places — creating real drift risk.

Consider whether the _meetings nested stream could reuse the main meetings stream definition via a different reference mechanism, or at minimum add a comment cross-referencing the duplicated blocks so future changes are applied consistently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ingestion/connectors/collaboration/zoom/connector.yaml` around lines 580
- 691, The requester, authenticator, error-handler, and paginator configuration
in the `_meetings` DeclarativeStream duplicates the corresponding `meetings` and
`participants` definitions. Reuse the existing `meetings` configuration through
a supported reference mechanism if strict validation permits; otherwise retain
the inline typed objects and add a concise cross-reference comment to each
duplicated block instructing future changes to stay synchronized.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/ingestion/connectors/collaboration/zoom/connector.yaml`:
- Around line 580-691: The requester, authenticator, error-handler, and
paginator configuration in the `_meetings` DeclarativeStream duplicates the
corresponding `meetings` and `participants` definitions. Reuse the existing
`meetings` configuration through a supported reference mechanism if strict
validation permits; otherwise retain the inline typed objects and add a concise
cross-reference comment to each duplicated block instructing future changes to
stay synchronized.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 108465a3-513a-408f-bfd9-069098b6e3ef

📥 Commits

Reviewing files that changed from the base of the PR and between d0ad879 and dc7af2d.

📒 Files selected for processing (10)
  • src/ingestion/connectors/collaboration/zoom/connector.yaml
  • src/ingestion/connectors/collaboration/zoom/tests/config.py
  • src/ingestion/connectors/collaboration/zoom/tests/conftest.py
  • src/ingestion/connectors/collaboration/zoom/tests/fixtures/meeting.json
  • src/ingestion/connectors/collaboration/zoom/tests/fixtures/participant.json
  • src/ingestion/connectors/collaboration/zoom/tests/fixtures/user.json
  • src/ingestion/connectors/collaboration/zoom/tests/test_meetings.py
  • src/ingestion/connectors/collaboration/zoom/tests/test_participants.py
  • src/ingestion/connectors/collaboration/zoom/tests/test_users.py
  • src/ingestion/scripts/create-bronze-placeholders.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/ingestion/scripts/create-bronze-placeholders.sh

Roman Mitasov and others added 3 commits July 13, 2026 18:26
…f config

Applied ruff 0.15.21 (the .pre-commit-config.yaml pin) check --fix +
format with the root ruff.toml (isort I rules, line-length 120,
skip-magic-trailing-comma). Suite unchanged behaviorally: 15/15 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Roman Mitasov <Roman.Mitasov@constructor.tech>
@mitasovr
mitasovr merged commit e9cff87 into constructorfabric:main Jul 13, 2026
33 checks passed
@ktursunov ktursunov mentioned this pull request Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

data: Zoom meeting metrics silently flatline — connector's daily sync has failed 10 days straight

3 participants