Skip to content
This repository was archived by the owner on Dec 25, 2024. It is now read-only.

Commit bd48337

Browse files
authored
Adds patternProperties (#220)
* Adds patternProperties to codegenSchema * Adds sample spec schemas and stores data in java codegenSchema class * Writes pattern_properties info in python schema class * Writes pattern property schemas in python * Adds validate_pattern_properties * Adds python tests of patternProperties * Removes unneeded line from test file * Samples and docs regen * Fixes bug
1 parent 834b21b commit bd48337

File tree

31 files changed

+493
-36
lines changed

31 files changed

+493
-36
lines changed

docs/generators/java.md

+1
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
342342
|Nullable|✗|OAS3
343343
|OneOf|✗|OAS3
344344
|Pattern|✓|OAS2,OAS3
345+
|PatternProperties|✗|OAS3
345346
|Properties|✓|OAS2,OAS3
346347
|PropertyNames|✗|OAS3
347348
|Required|✓|OAS2,OAS3

docs/generators/jaxrs-jersey.md

+1
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
325325
|Nullable|✗|OAS3
326326
|OneOf|✗|OAS3
327327
|Pattern|✓|OAS2,OAS3
328+
|PatternProperties|✗|OAS3
328329
|Properties|✓|OAS2,OAS3
329330
|PropertyNames|✗|OAS3
330331
|Required|✓|OAS2,OAS3

docs/generators/jmeter.md

+1
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
184184
|Nullable|✗|OAS3
185185
|OneOf|✗|OAS3
186186
|Pattern|✓|OAS2,OAS3
187+
|PatternProperties|✗|OAS3
187188
|Properties|✓|OAS2,OAS3
188189
|PropertyNames|✗|OAS3
189190
|Required|✓|OAS2,OAS3

docs/generators/kotlin.md

+1
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
294294
|Nullable|✗|OAS3
295295
|OneOf|✗|OAS3
296296
|Pattern|✓|OAS2,OAS3
297+
|PatternProperties|✗|OAS3
297298
|Properties|✓|OAS2,OAS3
298299
|PropertyNames|✗|OAS3
299300
|Required|✓|OAS2,OAS3

docs/generators/python.md

+1
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
253253
|Nullable|✓|OAS3
254254
|OneOf|✓|OAS3
255255
|Pattern|✓|OAS2,OAS3
256+
|PatternProperties|✓|OAS3
256257
|Properties|✓|OAS2,OAS3
257258
|PropertyNames|✓|OAS3
258259
|Required|✓|OAS2,OAS3

samples/client/3_0_3_unit_test/python/src/unit_test_api/schemas/validation.py

+32-1
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,36 @@ def validate_property_names(
11241124
return None
11251125

11261126

1127+
def validate_pattern_properties(
1128+
arg: typing.Any,
1129+
pattern_properties: typing.Mapping[PatternInfo, typing.Type[SchemaValidator]],
1130+
cls: typing.Type,
1131+
validation_metadata: ValidationMetadata,
1132+
) -> typing.Optional[PathToSchemasType]:
1133+
if not isinstance(arg, immutabledict):
1134+
return None
1135+
path_to_schemas: PathToSchemasType = {}
1136+
module_namespace = vars(sys.modules[cls.__module__])
1137+
for property_name, property_value in arg.items():
1138+
path_to_item = validation_metadata.path_to_item + (property_name,)
1139+
property_validation_metadata = ValidationMetadata(
1140+
path_to_item=path_to_item,
1141+
configuration=validation_metadata.configuration,
1142+
validated_path_to_schemas=validation_metadata.validated_path_to_schemas
1143+
)
1144+
for pattern_info, schema in pattern_properties.items():
1145+
flags = pattern_info.flags if pattern_info.flags is not None else 0
1146+
if not re.search(pattern_info.pattern, property_name, flags=flags):
1147+
continue
1148+
schema = _get_class(schema, module_namespace)
1149+
if validation_metadata.validation_ran_earlier(schema):
1150+
add_deeper_validated_schemas(validation_metadata, path_to_schemas)
1151+
continue
1152+
other_path_to_schemas = schema._validate(property_value, validation_metadata=property_validation_metadata)
1153+
update(path_to_schemas, other_path_to_schemas)
1154+
return path_to_schemas
1155+
1156+
11271157
validator_type = typing.Callable[[typing.Any, typing.Any, type, ValidationMetadata], typing.Optional[PathToSchemasType]]
11281158
json_schema_keyword_to_validator: typing.Mapping[str, validator_type] = {
11291159
'types': validate_types,
@@ -1157,5 +1187,6 @@ def validate_property_names(
11571187
'const_value_to_name': validate_const,
11581188
'dependent_required': validate_dependent_required,
11591189
'dependent_schemas': validate_dependent_schemas,
1160-
'property_names': validate_property_names
1190+
'property_names': validate_property_names,
1191+
'pattern_properties': validate_pattern_properties
11611192
}

samples/client/3_1_0_json_schema/python/.openapi-generator/FILES

+4
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ docs/components/schema/any_type_dependent_required.md
99
docs/components/schema/any_type_dependent_schemas.md
1010
docs/components/schema/any_type_max_contains_value.md
1111
docs/components/schema/any_type_min_contains_value.md
12+
docs/components/schema/any_type_pattern_properties.md
1213
docs/components/schema/any_type_property_names.md
1314
docs/components/schema/array_contains_value.md
1415
docs/components/schema/array_max_contains_value.md
1516
docs/components/schema/array_min_contains_value.md
1617
docs/components/schema/object_dependent_required.md
1718
docs/components/schema/object_dependent_schemas.md
19+
docs/components/schema/object_pattern_properties.md
1820
docs/components/schema/object_property_names.md
1921
docs/components/schema/string_const_string.md
2022
docs/paths/some_path/get.md
@@ -43,12 +45,14 @@ src/json_schema_api/components/schema/any_type_dependent_required.py
4345
src/json_schema_api/components/schema/any_type_dependent_schemas.py
4446
src/json_schema_api/components/schema/any_type_max_contains_value.py
4547
src/json_schema_api/components/schema/any_type_min_contains_value.py
48+
src/json_schema_api/components/schema/any_type_pattern_properties.py
4649
src/json_schema_api/components/schema/any_type_property_names.py
4750
src/json_schema_api/components/schema/array_contains_value.py
4851
src/json_schema_api/components/schema/array_max_contains_value.py
4952
src/json_schema_api/components/schema/array_min_contains_value.py
5053
src/json_schema_api/components/schema/object_dependent_required.py
5154
src/json_schema_api/components/schema/object_dependent_schemas.py
55+
src/json_schema_api/components/schema/object_pattern_properties.py
5256
src/json_schema_api/components/schema/object_property_names.py
5357
src/json_schema_api/components/schema/string_const_string.py
5458
src/json_schema_api/components/schemas/__init__.py

samples/client/3_1_0_json_schema/python/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,14 @@ Class | Description
179179
[AnyTypeDependentSchemas](docs/components/schema/any_type_dependent_schemas.md) |
180180
[AnyTypeMaxContainsValue](docs/components/schema/any_type_max_contains_value.md) |
181181
[AnyTypeMinContainsValue](docs/components/schema/any_type_min_contains_value.md) |
182+
[AnyTypePatternProperties](docs/components/schema/any_type_pattern_properties.md) |
182183
[AnyTypePropertyNames](docs/components/schema/any_type_property_names.md) |
183184
[ArrayContainsValue](docs/components/schema/array_contains_value.md) |
184185
[ArrayMaxContainsValue](docs/components/schema/array_max_contains_value.md) |
185186
[ArrayMinContainsValue](docs/components/schema/array_min_contains_value.md) |
186187
[ObjectDependentRequired](docs/components/schema/object_dependent_required.md) |
187188
[ObjectDependentSchemas](docs/components/schema/object_dependent_schemas.md) |
189+
[ObjectPatternProperties](docs/components/schema/object_pattern_properties.md) |
188190
[ObjectPropertyNames](docs/components/schema/object_property_names.md) |
189191
[StringConstString](docs/components/schema/string_const_string.md) |
190192

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# AnyTypePatternProperties
2+
json_schema_api.components.schema.any_type_pattern_properties
3+
```
4+
type: schemas.Schema
5+
```
6+
7+
## validate method
8+
Input Type | Return Type | Notes
9+
------------ | ------------- | -------------
10+
dict, schemas.immutabledict, str, datetime.date, datetime.datetime, uuid.UUID, int, float, bool, None, list, tuple, bytes, io.FileIO, io.BufferedReader | schemas.immutabledict, str, float, int, bool, None, tuple, bytes, io.FileIO |
11+
12+
[[Back to top]](#top) [[Back to Component Schemas]](../../../README.md#Component-Schemas) [[Back to README]](../../../README.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# ObjectPatternProperties
2+
json_schema_api.components.schema.object_pattern_properties
3+
```
4+
type: schemas.Schema
5+
```
6+
7+
## validate method
8+
Input Type | Return Type | Notes
9+
------------ | ------------- | -------------
10+
dict, schemas.immutabledict | schemas.immutabledict |
11+
12+
[[Back to top]](#top) [[Back to Component Schemas]](../../../README.md#Component-Schemas) [[Back to README]](../../../README.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# coding: utf-8
2+
3+
"""
4+
Example
5+
No description provided (generated by Openapi JSON Schema Generator https://github.com/openapi-json-schema-tools/openapi-json-schema-generator) # noqa: E501
6+
The version of the OpenAPI document: 1.0.0
7+
Generated by: https://github.com/openapi-json-schema-tools/openapi-json-schema-generator
8+
"""
9+
10+
from __future__ import annotations
11+
from json_schema_api.shared_imports.schema_imports import * # pyright: ignore [reportWildcardImportFromLibrary]
12+
13+
S: typing_extensions.TypeAlias = schemas.StrSchema
14+
I: typing_extensions.TypeAlias = schemas.IntSchema
15+
16+
17+
@dataclasses.dataclass(frozen=True)
18+
class AnyTypePatternProperties(
19+
schemas.AnyTypeSchema[schemas.immutabledict[str, schemas.OUTPUT_BASE_TYPES], typing.Tuple[schemas.OUTPUT_BASE_TYPES, ...]],
20+
):
21+
"""NOTE: This class is auto generated by OpenAPI JSON Schema Generator.
22+
Ref: https://github.com/openapi-json-schema-tools/openapi-json-schema-generator
23+
24+
Do not edit the class manually.
25+
"""
26+
# any type
27+
pattern_properties: typing.Mapping[
28+
schemas.PatternInfo,
29+
typing.Type[schemas.Schema]
30+
] = dataclasses.field(
31+
default_factory=lambda: {
32+
schemas.PatternInfo(
33+
pattern=r'^S_' # noqa: E501
34+
): S,
35+
schemas.PatternInfo(
36+
pattern=r'^I_' # noqa: E501
37+
): I,
38+
}
39+
)
40+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# coding: utf-8
2+
3+
"""
4+
Example
5+
No description provided (generated by Openapi JSON Schema Generator https://github.com/openapi-json-schema-tools/openapi-json-schema-generator) # noqa: E501
6+
The version of the OpenAPI document: 1.0.0
7+
Generated by: https://github.com/openapi-json-schema-tools/openapi-json-schema-generator
8+
"""
9+
10+
from __future__ import annotations
11+
from json_schema_api.shared_imports.schema_imports import * # pyright: ignore [reportWildcardImportFromLibrary]
12+
13+
S: typing_extensions.TypeAlias = schemas.StrSchema
14+
I: typing_extensions.TypeAlias = schemas.IntSchema
15+
16+
17+
@dataclasses.dataclass(frozen=True)
18+
class ObjectPatternProperties(
19+
schemas.Schema[schemas.immutabledict[str, schemas.OUTPUT_BASE_TYPES], tuple]
20+
):
21+
"""NOTE: This class is auto generated by OpenAPI JSON Schema Generator.
22+
Ref: https://github.com/openapi-json-schema-tools/openapi-json-schema-generator
23+
24+
Do not edit the class manually.
25+
"""
26+
types: typing.FrozenSet[typing.Type] = frozenset({schemas.immutabledict})
27+
pattern_properties: typing.Mapping[
28+
schemas.PatternInfo,
29+
typing.Type[schemas.Schema]
30+
] = dataclasses.field(
31+
default_factory=lambda: {
32+
schemas.PatternInfo(
33+
pattern=r'^S_' # noqa: E501
34+
): S,
35+
schemas.PatternInfo(
36+
pattern=r'^I_' # noqa: E501
37+
): I,
38+
}
39+
)
40+
41+
@classmethod
42+
def validate(
43+
cls,
44+
arg: typing.Mapping[str, schemas.INPUT_TYPES_ALL],
45+
configuration: typing.Optional[schema_configuration.SchemaConfiguration] = None
46+
) -> schemas.immutabledict[str, schemas.OUTPUT_BASE_TYPES]:
47+
return super().validate_base(
48+
arg,
49+
configuration=configuration,
50+
)
51+

samples/client/3_1_0_json_schema/python/src/json_schema_api/components/schemas/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
from json_schema_api.components.schema.any_type_dependent_schemas import AnyTypeDependentSchemas
1818
from json_schema_api.components.schema.any_type_max_contains_value import AnyTypeMaxContainsValue
1919
from json_schema_api.components.schema.any_type_min_contains_value import AnyTypeMinContainsValue
20+
from json_schema_api.components.schema.any_type_pattern_properties import AnyTypePatternProperties
2021
from json_schema_api.components.schema.any_type_property_names import AnyTypePropertyNames
2122
from json_schema_api.components.schema.array_contains_value import ArrayContainsValue
2223
from json_schema_api.components.schema.array_max_contains_value import ArrayMaxContainsValue
2324
from json_schema_api.components.schema.array_min_contains_value import ArrayMinContainsValue
2425
from json_schema_api.components.schema.object_dependent_required import ObjectDependentRequired
2526
from json_schema_api.components.schema.object_dependent_schemas import ObjectDependentSchemas
27+
from json_schema_api.components.schema.object_pattern_properties import ObjectPatternProperties
2628
from json_schema_api.components.schema.object_property_names import ObjectPropertyNames
2729
from json_schema_api.components.schema.string_const_string import StringConstString

samples/client/3_1_0_json_schema/python/src/json_schema_api/schemas/validation.py

+32-1
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,36 @@ def validate_property_names(
11241124
return None
11251125

11261126

1127+
def validate_pattern_properties(
1128+
arg: typing.Any,
1129+
pattern_properties: typing.Mapping[PatternInfo, typing.Type[SchemaValidator]],
1130+
cls: typing.Type,
1131+
validation_metadata: ValidationMetadata,
1132+
) -> typing.Optional[PathToSchemasType]:
1133+
if not isinstance(arg, immutabledict):
1134+
return None
1135+
path_to_schemas: PathToSchemasType = {}
1136+
module_namespace = vars(sys.modules[cls.__module__])
1137+
for property_name, property_value in arg.items():
1138+
path_to_item = validation_metadata.path_to_item + (property_name,)
1139+
property_validation_metadata = ValidationMetadata(
1140+
path_to_item=path_to_item,
1141+
configuration=validation_metadata.configuration,
1142+
validated_path_to_schemas=validation_metadata.validated_path_to_schemas
1143+
)
1144+
for pattern_info, schema in pattern_properties.items():
1145+
flags = pattern_info.flags if pattern_info.flags is not None else 0
1146+
if not re.search(pattern_info.pattern, property_name, flags=flags):
1147+
continue
1148+
schema = _get_class(schema, module_namespace)
1149+
if validation_metadata.validation_ran_earlier(schema):
1150+
add_deeper_validated_schemas(validation_metadata, path_to_schemas)
1151+
continue
1152+
other_path_to_schemas = schema._validate(property_value, validation_metadata=property_validation_metadata)
1153+
update(path_to_schemas, other_path_to_schemas)
1154+
return path_to_schemas
1155+
1156+
11271157
validator_type = typing.Callable[[typing.Any, typing.Any, type, ValidationMetadata], typing.Optional[PathToSchemasType]]
11281158
json_schema_keyword_to_validator: typing.Mapping[str, validator_type] = {
11291159
'types': validate_types,
@@ -1157,5 +1187,6 @@ def validate_property_names(
11571187
'const_value_to_name': validate_const,
11581188
'dependent_required': validate_dependent_required,
11591189
'dependent_schemas': validate_dependent_schemas,
1160-
'property_names': validate_property_names
1190+
'property_names': validate_property_names,
1191+
'pattern_properties': validate_pattern_properties
11611192
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# coding: utf-8
2+
3+
"""
4+
Example
5+
No description provided (generated by Openapi JSON Schema Generator https://github.com/openapi-json-schema-tools/openapi-json-schema-generator) # noqa: E501
6+
The version of the OpenAPI document: 1.0.0
7+
Generated by: https://github.com/openapi-json-schema-tools/openapi-json-schema-generator
8+
"""
9+
10+
import unittest
11+
12+
import json_schema_api
13+
from json_schema_api.components.schema.any_type_pattern_properties import AnyTypePatternProperties
14+
15+
16+
class TestAnyTypePatternProperties(unittest.TestCase):
17+
"""AnyTypePatternProperties unit test stubs"""
18+
19+
def test_succceds_with_other_type(self):
20+
inst = AnyTypePatternProperties.validate(0)
21+
assert inst == 0
22+
23+
def test_succceds_with_non_matching_key(self):
24+
inst = AnyTypePatternProperties.validate({"keyword": "value"})
25+
assert inst == {"keyword": "value"}
26+
27+
def test_succceds_with_matching_keys(self):
28+
inst = AnyTypePatternProperties.validate({"S_25": "This is a string", "I_0": 42})
29+
assert inst == {"S_25": "This is a string", "I_0": 42}
30+
31+
def test_fails_with_incorrect_value_type_for_s_key(self):
32+
with self.assertRaises(json_schema_api.ApiTypeError):
33+
AnyTypePatternProperties.validate({"S_0": 42})
34+
35+
def test_fails_with_incorrect_value_type_for_i_key(self):
36+
with self.assertRaises(json_schema_api.ApiTypeError):
37+
AnyTypePatternProperties.validate({"I_0": "This is a string"})
38+
39+
40+
if __name__ == '__main__':
41+
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# coding: utf-8
2+
3+
"""
4+
Example
5+
No description provided (generated by Openapi JSON Schema Generator https://github.com/openapi-json-schema-tools/openapi-json-schema-generator) # noqa: E501
6+
The version of the OpenAPI document: 1.0.0
7+
Generated by: https://github.com/openapi-json-schema-tools/openapi-json-schema-generator
8+
"""
9+
10+
import unittest
11+
12+
import json_schema_api
13+
from json_schema_api.components.schema.object_pattern_properties import ObjectPatternProperties
14+
15+
16+
class TestObjectPatternProperties(unittest.TestCase):
17+
"""ObjectPatternProperties unit test stubs"""
18+
19+
def test_succceds_with_non_matching_key(self):
20+
inst = ObjectPatternProperties.validate({"keyword": "value"})
21+
assert inst == {"keyword": "value"}
22+
23+
def test_succceds_with_matching_keys(self):
24+
inst = ObjectPatternProperties.validate({"S_25": "This is a string", "I_0": 42})
25+
assert inst == {"S_25": "This is a string", "I_0": 42}
26+
27+
def test_fails_with_incorrect_value_type_for_s_key(self):
28+
with self.assertRaises(json_schema_api.ApiTypeError):
29+
ObjectPatternProperties.validate({"S_0": 42})
30+
31+
def test_fails_with_incorrect_value_type_for_i_key(self):
32+
with self.assertRaises(json_schema_api.ApiTypeError):
33+
ObjectPatternProperties.validate({"I_0": "This is a string"})
34+
35+
36+
if __name__ == '__main__':
37+
unittest.main()

0 commit comments

Comments
 (0)