Skip to content

Commit f12b797

Browse files
authored
fix(contract): added regex for JSON string formats date-time, date, time, and email (#930)
* Added regex for JSON string formats date-time, date, email, and time * When generating an example resource, return ASCII for strings * Added start string replacement for terminate_regex and tests
1 parent 47bd8e3 commit f12b797

File tree

2 files changed

+49
-4
lines changed

2 files changed

+49
-4
lines changed

src/rpdk/core/contract/resource_generator.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,28 @@
2828
# https://github.com/aws-cloudformation/aws-cloudformation-rpdk/issues/118
2929

3030
# Arn is just a placeholder for testing
31+
# format list taken from https://python-jsonschema.readthedocs.io/en/stable/validate/#jsonschema.FormatChecker.checkers
32+
# date-time regex from https://github.com/naimetti/rfc3339-validator
33+
# date is extraction from date-time
34+
# time is extraction from date-time
3135
STRING_FORMATS = {
3236
"arn": "^arn:aws(-(cn|gov))?:[a-z-]+:(([a-z]+-)+[0-9])?:([0-9]{12})?:[^.]+$",
33-
"uri": "^(https?|ftp|file)://[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])(:[0-9]*)*([?/#].*)?$",
37+
"uri": r"^(https?|ftp|file)://[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])(:[0-9]*)*([?/#].*)?$",
38+
"date-time": r"^(\d{4})-(0[1-9]|1[0-2])-(\d{2})T(?:[01]\d|2[0123]):(?:[0-5]\d):(?:[0-5]\d)(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0123]):[0-5]\d)$",
39+
"date": r"^(\d{4})-(0[1-9]|1[0-2])-(\d{2})$",
40+
"time": r"^(?:[01]\d|2[0123]):(?:[0-5]\d):(?:[0-5]\d)(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0123]):[0-5]\d)$",
41+
"email": r"^.+@[^\.].*\.[a-z]{2,}$",
3442
}
3543

3644
NEG_INF = float("-inf")
3745
POS_INF = float("inf")
3846

3947

4048
def terminate_regex(regex):
49+
if regex.startswith("^"):
50+
regex = r"\A" + regex[1:]
4151
if regex.endswith("$"):
42-
return regex[:-1] + r"\Z"
52+
regex = regex[:-1] + r"\Z"
4353
return regex
4454

4555

tests/contract/test_resource_generator.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,28 @@ def test_terminate_regex_end_of_line_like_a_normal_person():
2222
assert re.match(modified_regex, "dfqh3eqefhq")
2323

2424

25-
def test_terminate_regex_no_termination_needed():
25+
def test_terminate_regex_line_start_change():
2626
original_regex = r"^[a-zA-Z0-9]{1,219}\Z"
27-
assert terminate_regex(original_regex) == original_regex
27+
terminated_regex = r"\A[a-zA-Z0-9]{1,219}\Z"
28+
assert terminate_regex(original_regex) == terminated_regex
29+
30+
31+
def test_terminate_regex_line_end_change():
32+
original_regex = r"\A[a-zA-Z0-9]{1,219}$"
33+
terminated_regex = r"\A[a-zA-Z0-9]{1,219}\Z"
34+
assert terminate_regex(original_regex) == terminated_regex
35+
36+
37+
def test_terminate_regex_line_start_and_end_change():
38+
original_regex = r"^[a-zA-Z0-9]{1,219}$"
39+
terminated_regex = r"\A[a-zA-Z0-9]{1,219}\Z"
40+
assert terminate_regex(original_regex) == terminated_regex
41+
42+
43+
def test_terminate_regex_no_termination_needed():
44+
original_regex = r"\A[a-zA-Z0-9]{1,219}\Z"
45+
terminated_regex = r"\A[a-zA-Z0-9]{1,219}\Z"
46+
assert terminate_regex(original_regex) == terminated_regex
2847

2948

3049
@pytest.mark.parametrize("schema_type", ["integer", "number"])
@@ -80,6 +99,22 @@ def test_generate_string_strategy_format():
8099
strategy = ResourceGenerator(schema).generate_schema_strategy(schema)
81100
assert re.fullmatch(STRING_FORMATS["arn"], strategy.example())
82101

102+
schema = {"type": "string", "format": "date-time"}
103+
strategy = ResourceGenerator(schema).generate_schema_strategy(schema)
104+
assert re.match(STRING_FORMATS["date-time"], strategy.example())
105+
106+
schema = {"type": "string", "format": "time"}
107+
strategy = ResourceGenerator(schema).generate_schema_strategy(schema)
108+
assert re.match(STRING_FORMATS["time"], strategy.example())
109+
110+
schema = {"type": "string", "format": "date"}
111+
strategy = ResourceGenerator(schema).generate_schema_strategy(schema)
112+
assert re.match(STRING_FORMATS["date"], strategy.example())
113+
114+
schema = {"type": "string", "format": "email"}
115+
strategy = ResourceGenerator(schema).generate_schema_strategy(schema)
116+
assert re.match(STRING_FORMATS["email"], strategy.example())
117+
83118

84119
def test_generate_string_strategy_length():
85120
schema = {"type": "string", "minLength": 5, "maxLength": 10}

0 commit comments

Comments
 (0)