feat: derive model/test owners from dbt group owner when no direct owner is set#1034
Conversation
…ner is set Co-Authored-By: Yosef Arbiv <yosef.arbiv@gmail.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
👋 @arbiv |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 42 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds a ChangesGroup owner inheritance
Estimated code review effort: 3 (Moderate) | ~25 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
| {% set group_name = config_dict.get("group") or node_dict.get("group") %} | ||
| {% if not formatted_owner and group_name %} | ||
| {% set group_owner = elementary.get_group_owner(group_name) %} | ||
| {% if group_owner %} {% do formatted_owner.append(group_owner) %} {% endif %} | ||
| {% endif %} |
There was a problem hiding this comment.
🚩 Behavioral change to owner enforcement: grouped models now pass enforce_owners checks
The enforce_project_configurations macro at macros/commands/enforce_project_configurations.sql:85 checks flattened_node.owner | length == 0 to enforce that models have owners. Since flatten_model now populates the owner field from the group owner when no direct owner is set, models that previously failed this check (no direct owner) will now silently pass if they belong to a group with an owner. This may be the desired behavior (groups as an ownership mechanism), but it changes the semantics of enforce_owners from 'model must have a directly-assigned owner' to 'model must have an owner from any source'. Users relying on enforce_owners to ensure explicit per-model ownership will no longer get warnings for grouped models.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Good catch — this is a real, intended consequence. enforce_project_configurations (line 85) checks flattened_node.owner | length == 0, so grouped models with a group owner will now pass enforce_owners even without a direct +owner.
This is consistent with the goal of the change: a dbt +group owner is treated as a legitimate ownership source, so enforce_owners now means "model has an owner from any source (direct or group)" rather than "model has a directly-assigned owner". For projects that consolidated ownership into groups (the motivating use case here), that's the desired behavior.
Flagging to the repo owners: if you'd prefer enforce_owners to keep requiring a direct per-model owner, we can gate the group fallback behind a config var (e.g. derive_owner_from_group, default true) and have the enforcement check the direct owner only. Happy to add that if preferred.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
integration_tests/tests/test_dbt_artifacts/test_groups.py (1)
565-729: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider extracting shared test setup.
The three new tests duplicate the same group-config/schema-yaml/write/run/cleanup boilerplate. Extracting a small helper (e.g., parameterized by model config, direct owner, and expected owner list/table) would reduce duplication and make future group-owner test cases cheaper to add.
🤖 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 `@integration_tests/tests/test_dbt_artifacts/test_groups.py` around lines 565 - 729, The three new tests in test_model_owner_derived_from_group, test_test_owner_derived_from_group, and test_direct_owner_takes_precedence_over_group repeat the same group config, schema YAML, model file write, dbt run, and cleanup steps. Extract the shared setup into a small helper in the same test module, parameterized by model name, group name, model SQL, schema config, expected table name, and expected owner assertion, and have the three tests call that helper with their specific owner expectations.
🤖 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 `@integration_tests/tests/test_dbt_artifacts/test_groups.py`:
- Around line 565-729: The three new tests in
test_model_owner_derived_from_group, test_test_owner_derived_from_group, and
test_direct_owner_takes_precedence_over_group repeat the same group config,
schema YAML, model file write, dbt run, and cleanup steps. Extract the shared
setup into a small helper in the same test module, parameterized by model name,
group name, model SQL, schema config, expected table name, and expected owner
assertion, and have the three tests call that helper with their specific owner
expectations.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3dd97b5a-ff92-473b-930c-ffc514a5ec4f
📒 Files selected for processing (3)
integration_tests/tests/test_dbt_artifacts/test_groups.pymacros/edr/dbt_artifacts/upload_dbt_models.sqlmacros/utils/graph/get_group_owner.sql
Co-Authored-By: Yosef Arbiv <yosef.arbiv@gmail.com>
elazarlachkar
left a comment
There was a problem hiding this comment.
Wondering about the approach - as we sync both groups and models, perhaps we should do the fallback when using the data (in Elementary CLI)?
It will allow us to keep the "real" data as is, and we could change the fallbacks easily in the future.
|
@elazarlachkar re: doing the fallback at consumption time (Elementary CLI/Cloud) instead of in the package — Good question, worth deciding explicitly. The reason I put it in the package: the original request is that ownership shows up in the Elementary schema in the customer's warehouse ( Your point does hold for the Cloud path (we already sync both |
Co-Authored-By: Yosef Arbiv <yosef.arbiv@gmail.com>
Summary
Projects that moved ownership from model-level
+ownerto dbt+groupconfig lost ownership visibility in the Elementary schema:dbt_models.ownerand, downstream,dbt_tests.model_owners→elementary_test_results.ownersbecame[]. This breaks alert routing / node ownership, which read theownersfield.Root cause: owner resolution only ever read the node's direct owner:
Groups were captured (in
dbt_groups+group_namecolumns) but their owner was never folded intoowner.This adds a group-owner fallback, following the email-first precedence already used by
dbt_groupsand keeping direct owner authoritative:Because test
model_ownersderive from the tested model's flattenedowner, this automatically flows intoelementary_test_results.owners— no change needed inflatten_test.Changes
elementary.get_group_owner(group_name)— resolves a group's owner fromgraph.groups, preferringowner.email, falling back toowner.name.flatten_model: fall back to the group owner only when no direct model/config owner exists (direct owner still wins, mirroring dbt's config precedence).test_groups.py:test_model_owner_derived_from_group— grouped model, no direct owner →owner = [group email].test_test_owner_derived_from_group— test on that model →model_owners = [group email].test_direct_owner_takes_precedence_over_group— direct owner wins, group ignored.Ran locally against Postgres: the 3 new tests plus existing
test_groups.py/test_test_owners.py(13 total) pass.Link to Devin session: https://app.devin.ai/sessions/7cc0b65582e44ceb9b95e5ea9559f08d
Requested by: @arbiv
Summary by CodeRabbit