Skip to content

Conversation

vicheey
Copy link
Contributor

@vicheey vicheey commented Aug 22, 2025

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.

  • Added property rule validation method in samtranslator/model/init.py
  • Enhanced add propagate tags logic in samtranslator/model/init.py
  • Add enhance property rule validation logic samtranslator/validator/property_rule.py
  • Updated SAM resources in samtranslator/model/sam_resources.py to integrate
    validation
  • Comprehensive test coverage in tests/test_model.py
  • Comprehensive test coverage in tests/validator/test_property_rule.py

Description of how you validated changes

  • Added extensive test suite covering validation scenarios
  • All existing tests continue to pass

Checklist

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@vicheey vicheey requested a review from a team as a code owner August 22, 2025 21:16
@vicheey
Copy link
Contributor Author

vicheey commented Aug 22, 2025

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:
• samtranslator/validator/validator.py - 27% coverage
• samtranslator/model/init.py - 69% coverage
• samtranslator/model/naming.py - 71% coverage
• samtranslator/region_configuration.py - 79% coverage

Removing deprecated and unused samtranslator/validator/validator.py

@vicheey
Copy link
Contributor Author

vicheey commented Aug 22, 2025

Current usage analysis:
• No active imports of SamTemplateValidator in any source
files
• No test files directly testing SamTemplateValidator

What IS being used:
samtranslator.validator.value_validator.sam_expect - Used extensively across 12+ files
• Direct jsonschema.validate() calls in tests (instead of the deprecated SAM wrapper)

However, we cannot delete this file yet because this public facing interface might be used by package consuming this package.
I added the samtranslator.validator.validator.py to test coverage ignore list.

--------------------------------
TOTAL   9640    314   3402    220    95%

Required test coverage of 95% reached. Total coverage: 95.28%

=============== 4118 passed in 86.15s (0:01:26) ================

@vicheey vicheey force-pushed the validation-framework-extraction branch from 188db2a to b52c322 Compare August 22, 2025 23:50

error_messages = []

for rule_type, properties in rules:
Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Outdated.

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]
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll update.

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:
Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated commit allows mutually inclusive, mutually exclusive, conditionally inclusive and conditionally exclusive.

@@ -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:
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adding a new variable requires adding the class to _keyword. I prefer function parameter for that matter.

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 "")
Copy link
Member

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)

Copy link
Contributor Author

@vicheey vicheey Aug 27, 2025

Choose a reason for hiding this comment

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

This is to allow: A, B, and C, A and B, and A

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:
Copy link
Member

@roger-zhangg roger-zhangg Aug 26, 2025

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)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The validate_properties_and_return_model is used by other classes. It is risky to modify the method.

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.

2 participants