-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Add resource property validation framework to SAM translator. #3805
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
base: develop
Are you sure you want to change the base?
Conversation
The Tests failed because of lower coverage percentage. (94.82% instead of 95%). Added validation logic push the overall total line increase and lower the coverage percentage. Low coverage files: Removing deprecated and unused |
Current usage analysis: What IS being used: However, we cannot delete this file yet because this public facing interface might be used by package consuming this package.
|
188db2a
to
b52c322
Compare
elif ( | ||
rule_type == ValidationRule.CONDITIONAL_REQUIREMENT | ||
and self._get_property_value(properties[0], validated_model) is not None | ||
and self._get_property_value(properties[1], validated_model) is None |
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.
you can only set 2 prop in one rule?
|
||
error_messages = [] | ||
|
||
for rule_type, properties in rules: |
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.
Can we do something like [ rule.run() for rules in self.__validation_rules__]
so we can save all the if /elif below?
error_messages = [] | ||
|
||
for rule_type, properties in rules: | ||
present = [prop for prop in properties if self._get_property_value(prop, validated_model) is not None] |
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.
is there a better name for present
? It's hard to understnad
prop_names = [f"'{p}'" for p in present] | ||
error_messages.append(f"Cannot specify {self._combine_string(prop_names)} together.") | ||
|
||
elif rule_type == ValidationRule.MUTUALLY_INCLUSIVE: |
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.
Is this like an antipattern? User need to constantly set 2 or more params to achieve a single action. Is there an example for this MUTUALLY_INCLUSIVE
? Can we optimize it to be CONDITIONAL_REQUIREMENT
?
@@ -582,6 +687,71 @@ def _check_tag(self, reserved_tag_name, tags): # type: ignore[no-untyped-def] | |||
"input.", | |||
) | |||
|
|||
def validate_before_transform(self, schema_class: Optional[Type[RT]], collect_all_errors: bool = False) -> None: |
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.
should collect_all_errors and schema_class be a class attr? So it's like this.collect_all_errors
and this. schema_class
raise InvalidResourceException(self.logical_id, "\n".join(error_messages)) | ||
|
||
def _combine_string(self, words: List[str]) -> str: | ||
return ", ".join(words[:-1]) + (" and " + words[-1] if len(words) > 1 else words[0] if words else "") |
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.
why? why not ", ".join(words)
@@ -553,6 +624,40 @@ def _construct_tag_list( | |||
# customer's knowledge. | |||
return get_tag_list(sam_tag) + get_tag_list(additional_tags) + get_tag_list(tags) | |||
|
|||
@staticmethod | |||
def propagate_tags_combine( |
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.
This is not mentioned in PR title or description
def _combine_string(self, words: List[str]) -> str: | ||
return ", ".join(words[:-1]) + (" and " + words[-1] if len(words) > 1 else words[0] if words else "") | ||
|
||
def _get_property_value(self, prop: str, validated_model: Any = None) -> Any: |
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.
I would suggest to update validate_properties_and_return_model
return a custom dict that allow you to directly use validated_model["a.b.c"]. Like
class DotDict(dict):
def __getitem__(self, path):
value = self
for key in path.split('.'):
value = dict.__getitem__(value, key)
return value
validated_model["a.b.c"]
would have a much better readability than _get_property_value("a.b.c", validated_model)
class Resource(self.TestResource): | ||
__validation_rules__ = [ | ||
(self.ValidationRule.CONDITIONAL_REQUIREMENT, ["ConditionalVar1", "ConditionalVar2"]), | ||
(self.ValidationRule.CONDITIONAL_REQUIREMENT, ["ConditionalVar2", "ConditionalVar3"]), |
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.
from actually seeing this, does Rule('ConditionalVar2').require('ConditionalVar1')
sounds better?
This can even extend to like Rule('ConditionalVar2').require('ConditionalVar1==123')
self.ValidationRule.CONDITIONAL_REQUIREMENT, | ||
["NestedSetting1.NestedVar1", "NestedSetting2.NestedVar2"], | ||
), | ||
(self.ValidationRule.MUTUALLY_EXCLUSIVE, ["ExclusiveVar1", "ExclusiveVar2"]), |
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.
Rule('ConditionalVar2').reject('ConditionalVar1')
["NestedSetting1.NestedVar1", "NestedSetting2.NestedVar1"], | ||
), | ||
(self.ValidationRule.MUTUALLY_EXCLUSIVE, ["NestedSetting1.NestedVar2", "ExclusiveVar3"]), | ||
(self.ValidationRule.MUTUALLY_INCLUSIVE, ["InclusiveVar1", "ExclusiveVar1"]), |
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.
Rule('ConditionalVar1', 'ConditionalVar2')
Just some thoughts
if not hasattr(self, "__validation_rules__"): | ||
return | ||
|
||
rules = self.__validation_rules__ |
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.
One concern, currently there's no type hint on self. __validation_rules__
, so we need to look at document/ previous implementation everytime when we need to use this function
Issue #, if available
None
Description of changes
This PR introduces a comprehensive validation framework for SAM resource properties, and enhancing the translator's ability to catch configuration errors early in the deployment process.
validation
Description of how you validated changes
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.