[Bug] Renaming or removing a contracted model should raise a BreakingChange warning/error
jtcohen6 opened this issue · comments
Is this a new bug in dbt-core?
- I believe this is a new bug in dbt-core
- I have searched the existing issues, and I could not find an existing issue for this bug
Current Behavior
When a contracted model changes or disables its contract, dbt raises a warning (UnversionedBreakingChange
) or error (ContractBreakingChangeError
) within the same_contract
check triggered by state:modified
selection/comparison.
However, the same warning/error does not occur if the user simply deletes or renames a contracted model. I believe it should — until/unless unless that contracted model has passed a defined deprecation_date
, at which point deletion is allowed.
Expected Behavior
If a contracted model is present in the comparison manifest, and missing in the current manifest, raise a warning (if unversioned) or error (if versioned) accordingly. Add a new type of "breaking change" for "renamed or removed."
Naïve implementation for illustrative purposes only:
class StateSelectorMethod(SelectorMethod):
...
def check_for_deleted_contracted_models(self) -> None:
old_contracted_nodes = set(
k for k, v in self.previous_state.manifest.nodes.items() if v.config.contract.enforced
)
new_contracted_nodes = set(
k for k, v in self.manifest.nodes.items() if v.config.contract.enforced
)
for unique_id in old_contracted_nodes - new_contracted_nodes:
node = self.previous_state.manifest.nodes[unique_id]
if (
node.deprecation_date
and node.deprecation_date < datetime.datetime.now().astimezone()
):
# Passed its deprecation_date, so deletion is allowed
continue
if node.version is None:
print("WARNING! Unversioned contracted model renamed or removed")
# This should be warn_or_error(UnversionedBreakingChange), and we probably want a new "breaking_change" reason
else:
print("ERROR! Versioned contracted model renamed or removed")
# This should raise ContractBreakingChangeError
...
def search(self, included_nodes: Set[UniqueId], selector: str) -> Iterator[UniqueId]:
if self.previous_state is None or self.previous_state.manifest is None:
raise DbtRuntimeError("Got a state selector method, but no comparison manifest")
self.check_for_deleted_contracted_models()
...
Steps To Reproduce
- Create a model configured with
contract: {enforced: true}
- Parse the project / run the model
- Move the manifest to a folder named
state/
dbt list -s state:modified --state
Relevant log output
No response
Environment
- OS: macOS
- Python: 3.10.11
- dbt: v1.8 / main
Which database adapter are you using with dbt?
No response
Additional Context
No response
@MichelleArk link an example of the current implementation for similar errors in the contracted model.