-
Notifications
You must be signed in to change notification settings - Fork 24
feat: add conditions to validators #589
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Adds an optional condition
parameter to both PredicateValidator
and DpathValidator
, allowing validation strategies to be skipped based on runtime config. Core changes include:
- Extending validator constructors and
validate
methods with config-driven conditional logic. - Updating the component factory and schemas to support the new
condition
field. - Revising unit tests to pass
config
andcondition
, and adding negative-case tests for skipped validation.
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
File | Description |
---|---|
unit_tests/.../test_predicate_validator.py | Updated instantiation to include config /condition ; added skip test |
unit_tests/.../test_dpath_validator.py | Same updates as above for DpathValidator |
airbyte_cdk/.../predicate_validator.py | Added config , condition fields and conditional skip in validate |
airbyte_cdk/.../dpath_validator.py | Added config , condition fields and conditional skip in validate |
airbyte_cdk/.../model_to_component_factory.py | Passes config and condition into newly extended validators |
airbyte_cdk/.../declarative_component_schema.py | Added condition property to Pydantic models |
airbyte_cdk/.../declarative_component_schema.yaml | Added condition definitions in YAML schema |
Comments suppressed due to low confidence (4)
airbyte_cdk/sources/declarative/validators/predicate_validator.py:27
- Update the
validate
docstring to mention the newconfig
andcondition
parameters and clarify that validation is skipped when the condition evaluates to false.
def validate(self) -> None:
airbyte_cdk/sources/declarative/validators/dpath_validator.py:39
- Enhance the docstring to explain that if
condition
evaluates to false based onconfig
, the validation strategy is skipped.
def validate(self, input_data: dict[str, Any]) -> None:
unit_tests/sources/declarative/validators/test_predicate_validator.py:59
- [nitpick] Add a complementary test where the condition evaluates to true (e.g.,
"{{ config.get('test') }}"
) to verify that the strategy is invoked when the condition passes.
def test_given_condition_is_false_when_validate_then_validate_is_not_called(self):
unit_tests/sources/declarative/validators/test_dpath_validator.py:126
- [nitpick] Similarly, include a test where the condition evaluates to true to ensure the DpathValidator applies the strategy when expected.
def test_given_condition_is_false_when_validate_then_validate_is_not_called(self):
📝 WalkthroughWalkthroughThis change introduces a new optional Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ValidatorFactory
participant Validator
participant Config
User->>ValidatorFactory: Create Validator (with model, config)
ValidatorFactory->>Validator: Instantiate (pass config, condition)
User->>Validator: validate(input_data)
Validator->>Validator: Evaluate condition against config
alt Condition is true
Validator->>Validator: Perform validation strategy
else Condition is false
Validator-->>User: Skip validation
end
Suggested reviewers
Would you like to consider adding more examples of complex conditions in the schema documentation to help users understand possible use cases, wdyt? ✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (9)
unit_tests/sources/declarative/validators/test_predicate_validator.py (1)
59-70
: Excellent test coverage for the conditional validation feature!This test properly verifies that validation is skipped when the condition evaluates to False. The condition logic is correct:
{{ not config.get('test') }}
withconfig={"test": "test"}
evaluates tonot "test"
which isFalse
, sostrategy.validate_called
should indeed beFalse
.One small suggestion - would it be worth adding a comment explaining the condition evaluation for clarity, wdyt? Something like:
# Condition evaluates to False since config.get('test') returns "test" (truthy) condition="{{ not config.get('test') }}",airbyte_cdk/sources/declarative/validators/predicate_validator.py (3)
21-22
: Consider making the condition field optional, wdyt?Since conditional validation is a new optional feature, should the
condition
field be typed asOptional[str] = None
to better reflect that it's not always required? This would make the API more explicit about when conditions are used.- condition: str + condition: Optional[str] = NoneDon't forget to import
Optional
fromtyping
if you go with this approach.
24-25
: Add null safety to the post_init method, wdyt?The current implementation always creates an
InterpolatedBoolean
even whencondition
might be None or empty. Consider adding a guard to handle this more gracefully?def __post_init__(self) -> None: - self._interpolated_condition = InterpolatedBoolean(condition=self.condition, parameters={}) + if self.condition: + self._interpolated_condition = InterpolatedBoolean(condition=self.condition, parameters={}) + else: + self._interpolated_condition = NoneYou'd also need to update the condition check in the validate method accordingly.
33-35
: Consider adding error handling and updating documentation, wdyt?The condition evaluation logic looks correct, but a couple of suggestions:
Should we handle potential exceptions from
_interpolated_condition.eval()
? Condition evaluation errors could be confusing to debug.The class docstring doesn't mention the new conditional behavior - might be worth updating it to reflect this enhancement?
For error handling:
- if self.condition and not self._interpolated_condition.eval(self.config): - return + if self.condition: + try: + if not self._interpolated_condition.eval(self.config): + return + except Exception as e: + raise ValueError(f"Failed to evaluate validator condition '{self.condition}': {e}")For documentation:
class PredicateValidator: """ - Validator that applies a validation strategy to a value. + Validator that applies a validation strategy to a value. + + If a condition is specified, the validation is only performed when the condition + evaluates to true against the provided configuration. """airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py (2)
870-879
: create_dpath_validator: forwardparameters
from the model?I see you’ve added
config
andcondition
support—which is great—but unlike most factory methods, you’re not forwardingmodel.parameters
into theDpathValidator
. Do we need to passparameters=model.parameters or {}
here for interpolation inside the validator? wdyt?
881-892
: create_predicate_validator: consider includingparameters
as wellSimilar to
DpathValidator
, you’ve wired inconfig
andcondition
onPredicateValidator
but didn’t forwardmodel.parameters
. Would it make sense to addparameters=model.parameters or {}
to the constructor to keep it consistent with other components? wdyt?airbyte_cdk/sources/declarative/validators/dpath_validator.py (3)
26-27
: Consider making the new fields more explicit about their optionality?The new
config
andcondition
fields look good for supporting conditional validation. However, I'm wondering if these should be typed as optional or have default values, since conditions might not always be needed? What do you think about making this more explicit - wdyt?- config: Config - condition: str + config: Config + condition: str = ""Or alternatively with Optional typing if conditions are truly optional?
48-50
: Consider adding error handling for condition evaluation?The conditional logic looks good and implements the feature as intended! However, I'm wondering if we should add some safety around the condition evaluation? What happens if
self._interpolated_condition.eval(self.config)
throws an exception or ifself.config
is None?Maybe something like this to be more defensive - wdyt?
- if self.condition and not self._interpolated_condition.eval(self.config): - return + if self.condition: + try: + if not self._interpolated_condition.eval(self.config): + return + except Exception as e: + raise ValueError(f"Error evaluating validator condition '{self.condition}': {e}")
41-47
: Update docstring to mention conditional behavior?The docstring for the
validate
method doesn't mention the new conditional validation behavior. Would it be helpful to update it to reflect that validation can be skipped based on conditions - wdyt?def validate(self, input_data: dict[str, Any]) -> None: """ - Extracts the value at the specified path and applies the validation strategy. + Extracts the value at the specified path and applies the validation strategy. + Validation is skipped if the condition evaluates to false. :param input_data: Dictionary containing the data to validate :raises ValueError: If the path doesn't exist or validation fails """
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
airbyte_cdk/sources/declarative/declarative_component_schema.yaml
(2 hunks)airbyte_cdk/sources/declarative/models/declarative_component_schema.py
(3 hunks)airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py
(1 hunks)airbyte_cdk/sources/declarative/validators/dpath_validator.py
(3 hunks)airbyte_cdk/sources/declarative/validators/predicate_validator.py
(2 hunks)unit_tests/sources/declarative/validators/test_dpath_validator.py
(7 hunks)unit_tests/sources/declarative/validators/test_predicate_validator.py
(4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
unit_tests/sources/declarative/validators/test_predicate_validator.py (2)
airbyte_cdk/sources/declarative/models/declarative_component_schema.py (1)
PredicateValidator
(2024-2050)unit_tests/sources/declarative/validators/test_dpath_validator.py (3)
validate
(18-22)test_given_condition_is_false_when_validate_then_validate_is_not_called
(126-137)MockValidationStrategy
(11-22)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: Check: 'source-amplitude' (skip=false)
- GitHub Check: Check: 'source-pokeapi' (skip=false)
- GitHub Check: Check: 'source-hardcoded-records' (skip=false)
- GitHub Check: Check: 'source-shopify' (skip=false)
- GitHub Check: SDM Docker Image Build
- GitHub Check: Pytest (All, Python 3.10, Ubuntu)
- GitHub Check: Pytest (All, Python 3.11, Ubuntu)
- GitHub Check: Pytest (Fast)
🔇 Additional comments (15)
unit_tests/sources/declarative/validators/test_predicate_validator.py (2)
1-2
: Copyright header update looks good!Nice to see the copyright year updated to 2025.
29-29
: Good approach to maintain backward compatibility.Adding the new
config={}
andcondition=""
parameters with sensible defaults ensures existing behavior is preserved. This is a clean way to introduce the conditional validation feature, wdyt?unit_tests/sources/declarative/validators/test_dpath_validator.py (3)
1-2
: Copyright header update is consistent.Good to see the year updated consistently across test files.
28-33
: Consistent parameter addition maintains backward compatibility.The approach of adding
config={}
andcondition=""
with empty defaults is applied consistently across all existing tests. This ensures that the new conditional validation feature doesn't break existing functionality.
126-137
: Well-implemented conditional validation test!This test effectively verifies the conditional validation behavior and mirrors the approach used in the PredicateValidator tests, which shows good consistency across the codebase. The test logic is sound - the condition
{{ not config.get('test') }}
correctly evaluates toFalse
with the provided config, ensuring validation is skipped.The test data structure with nested objects (
{"user": {"profile": {"email": "test@example.com"}}}
) also properly exercises the DpathValidator's path traversal functionality, wdyt?airbyte_cdk/sources/declarative/declarative_component_schema.yaml (2)
4279-4288
: Looks great!
The newcondition
field forDpathValidator
is correctly declared with a default empty string, proper interpolation context, and clear examples. wdyt?
4323-4332
: LGTM!
Thecondition
property forPredicateValidator
mirrors theDpathValidator
addition and maintains consistency in defaults, context, and examples. wdyt?airbyte_cdk/sources/declarative/validators/predicate_validator.py (1)
8-10
: LGTM on the import additions!The new imports for
InterpolatedBoolean
andConfig
are necessary for the conditional validation functionality and are appropriately placed.airbyte_cdk/sources/declarative/models/declarative_component_schema.py (2)
2013-2021
: Add optionalcondition
to DpathValidator looks good
The newcondition
field aligns perfectly with the schema changes and enables skipping validation based on a template expression. wdyt?
2042-2050
: Add optionalcondition
to PredicateValidator looks good
This mirrors theDpathValidator
update and provides the same conditional flexibility for predicate-based validation. wdyt?airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py (3)
2489-2493
: create_types_map: newcondition
default looks goodYou’ve introduced
condition
with a fallback to"True"
so that mappings are applied by default. This aligns withTypesMap
semantics—looks solid.
2997-2999
: create_record_filter: conditional filtering enabledThe addition of
condition=model.condition or ""
along with passingconfig
andparameters
correctly enables skipping filters when not needed. Implementation matches the pattern used elsewhere.
3096-3098
: create_remove_fields: conditional removal looks correct
condition=model.condition or ""
has been added toRemoveFields
, enabling optional field removal. This change is consistent withConfigRemoveFields
and other transformations.airbyte_cdk/sources/declarative/validators/dpath_validator.py (2)
10-10
: LGTM on the new imports!The imports for
InterpolatedBoolean
andConfig
are correctly added and align with the new functionality.Also applies to: 14-14
30-30
: Question about the interpolation pattern consistency?I notice that the
InterpolatedBoolean
is created with empty parameters{}
, but later in thevalidate
method it's evaluated withself.config
. This seems different from the field path pattern where both creation and evaluation use empty contexts.Is this intentional because conditions need access to config during evaluation? Just want to make sure this follows the expected pattern for this codebase - wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (1)
unit_tests/sources/declarative/spec/test_spec.py (1)
212-276
: 🛠️ Refactor suggestionConsider adding tests for the new conditional validation feature
While these changes correctly adapt the existing tests to the new API, it looks like we're missing test coverage for the actual conditional validation functionality. Would you like me to help generate some test cases that verify validators are skipped/executed based on different condition values? This would ensure the new feature works as expected, wdyt?
🧹 Nitpick comments (2)
unit_tests/sources/declarative/spec/test_spec.py (2)
237-239
: Quick question about the empty condition behaviorThe addition of
config=input_config
andcondition=""
parameters aligns with the new conditional validation feature. However, I'm curious about the empty condition string - does this mean the validation will always run, or does it get skipped? Would it be clearer to use a more explicit value likecondition="true"
or document the expected behavior, wdyt?
269-271
: Same question about condition behaviorSame observation as the previous test - the empty
condition=""
works, but would benefit from clarification on whether this means "always validate" or "never validate", wdyt?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
unit_tests/sources/declarative/spec/test_spec.py
(3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (9)
- GitHub Check: Check: 'source-pokeapi' (skip=false)
- GitHub Check: Check: 'source-shopify' (skip=false)
- GitHub Check: Check: 'source-amplitude' (skip=false)
- GitHub Check: Check: 'source-hardcoded-records' (skip=false)
- GitHub Check: SDM Docker Image Build
- GitHub Check: Pytest (All, Python 3.10, Ubuntu)
- GitHub Check: Pytest (All, Python 3.11, Ubuntu)
- GitHub Check: Pytest (Fast)
- GitHub Check: Analyze (python)
🔇 Additional comments (2)
unit_tests/sources/declarative/spec/test_spec.py (2)
213-213
: Good initialization timing!Moving the
input_config
initialization before thecomponent_spec
creation makes sense since it's now needed for theDpathValidator
constructor.
246-246
: Consistent initialization patternGood consistency with the first test function - initializing
input_config
early for use in the validator constructor.
Closing in favor of simply skipping validation if returned values are falsy: #590 |
What
Summary by CodeRabbit