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

Commit 9c3fb29

Browse files
committed
v2 consolidates validation (#98)
* Validators made for all but 7 keywords * Adds validate_required * Fixes validator values and comparisons * Adds validate_items * Adds validate_properties * Adds validate_additional_properties * Updates schemas template file * Adds JsonSchemaValidator as a base class to all MetaOapg * More typo and bugfixes * Embeds oneOf anyOf and allOf classes in subclesses like properties does * Sample regenerated with composition class wrapper fix * Fixes some validate tests * Adds validate_one_of * Adds anyOf validation * Adds validate_all_of * Fixes validate_additional_properties * Speeds up Configuration property access * Removes from_server info from ValidationMetadata * Ensures that configuration is present in ValidationMetadata * Fixes disabled_json_schema_keywords, updates configuration * Fixes configuration test * Fixes tests in test_any_type_schema * Adds validate_not * Fixes BinarySchema one_of definition * Fixes int checking with no format * Adds path_to_type dict * Mfgs classes for unvalidated paths * Fixes generation of bool and none classes on unvalidated paths * Fixes more tests * Fixes test_dict_validate * Removes unest type checking * Adds validate_discriminator * Sample regen * Fixes 3 tests * Adds self handling for oneOf/anyOf/allOf validation, fixes last test * Samples regenerated * Fixes integer validation * Removes Discriminable * Removes ComposedBase + BinaryBase * Removes Int32Base/Int64Base/Float32Base/Float64Base * Adds int format for integers * Removes EnumBase * Replaces ComposedSchema with AnyTypeSchema, fixes two tests * Fixes two tests, handles AnyType + InheritorOfAnyType + XSchema use case * Adds template changes that allow nonCompliantUseDiscriminatorIfCompositionFails to work * Sample regen * Fixes test for nonCompliantUseDiscriminatorIfCompositionFails feature sample * Samples regenerated * Samples regenerate with UnsetAnyTypeSchema exclusion in cast_to_allowed..
1 parent a131283 commit 9c3fb29

File tree

320 files changed

+9224
-10426
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

320 files changed

+9224
-10426
lines changed

modules/openapi-json-schema-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -840,7 +840,12 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo
840840
// fix needed for values with /n /t etc in them
841841
String fixedName = handleSpecialCharacters(name);
842842
CodegenProperty cp = super.fromProperty(fixedName, p, required, schemaIsFromAdditionalProperties, sourceJsonPath);
843-
843+
if (cp.isInteger && cp.getFormat() == null) {
844+
// this generator treats integers as type number
845+
// this is done so type int + float has the same base class (decimal.Decimal)
846+
// so integer validation info must be set using formatting
847+
cp.setFormat("int");
848+
}
844849
if (cp.isAnyType && cp.isNullable) {
845850
cp.isNullable = false;
846851
}
@@ -1275,6 +1280,12 @@ public CodegenModel fromModel(String name, Schema sc) {
12751280
String pattern = (String) cm.vendorExtensions.get("x-regex");
12761281
cm.setPattern(pattern);
12771282
}
1283+
if (cm.isInteger && cm.getFormat() == null) {
1284+
// this generator treats integers as type number
1285+
// this is done so type int + float has the same base class (decimal.Decimal)
1286+
// so integer validation info must be set using formatting
1287+
cm.setFormat("int");
1288+
}
12781289
if (cm.isNullable) {
12791290
cm.setIsNull(true);
12801291
cm.isNullable = false;

modules/openapi-json-schema-generator/src/main/resources/python/configuration.handlebars

+63-22
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,46 @@
33
{{>partial_header}}
44

55
import copy
6+
from http import client as http_client
67
import logging
78
{{#unless asyncio}}
89
import multiprocessing
910
{{/unless}}
1011
import sys
12+
import typing
13+
1114
import urllib3
1215

13-
from http import client as http_client
1416
from {{packageName}}.exceptions import ApiValueError
1517

1618

17-
JSON_SCHEMA_VALIDATION_KEYWORDS = {
18-
'multipleOf', 'maximum', 'exclusiveMaximum',
19-
'minimum', 'exclusiveMinimum', 'maxLength',
20-
'minLength', 'pattern', 'maxItems', 'minItems',
21-
'uniqueItems', 'maxProperties', 'minProperties',
19+
JSON_SCHEMA_KEYWORD_TO_PYTHON_KEYWORD = {
20+
'types': 'types',
21+
'type': 'types',
22+
'enum': 'enum_value_to_name',
23+
'uniqueItems': 'unique_items',
24+
'minItems': 'min_items',
25+
'maxItems': 'max_items',
26+
'minProperties': 'min_properties',
27+
'maxProperties': 'max_properties',
28+
'minLength': 'min_length',
29+
'maxLength': 'max_length',
30+
'minimum': 'inclusive_minimum',
31+
'exclusiveMinimum': 'exclusive_minimum',
32+
'maximum': 'inclusive_maximum',
33+
'exclusiveMaximum': 'exclusive_maximum',
34+
'multipleOf': 'multiple_of',
35+
'pattern': 'regex',
36+
'format': 'format',
37+
'required': 'required',
38+
'items': 'items',
39+
'properties': 'properties',
40+
'additionalProperties': 'additional_properties',
41+
'oneOf': 'one_of',
42+
'anyOf': 'any_of',
43+
'allOf': 'all_of',
44+
'not': 'not_schema',
45+
'discriminator': 'discriminator'
2246
}
2347

2448
class Configuration(object):
@@ -37,7 +61,7 @@ class Configuration(object):
3761
The dict value is an API key prefix when generating the auth data.
3862
:param username: Username for HTTP basic authentication
3963
:param password: Password for HTTP basic authentication
40-
:param disabled_client_side_validations (string): Comma-separated list of
64+
:param disabled_json_schema_keywords (set): Set of
4165
JSON schema validation keywords to disable JSON schema structural validation
4266
rules. The following keywords may be specified: multipleOf, maximum,
4367
exclusiveMaximum, minimum, exclusiveMinimum, maxLength, minLength, pattern,
@@ -46,7 +70,7 @@ class Configuration(object):
4670
and data received from the server, independent of any validation performed by
4771
the server side. If the input data does not satisfy the JSON schema validation
4872
rules specified in the OpenAPI document, an exception is raised.
49-
If disabled_client_side_validations is set, structural validation is
73+
If disabled_json_schema_keywords is set, structural validation is
5074
disabled. This can be useful to troubleshoot data validation problem, such as
5175
when the OpenAPI document validation rules do not match the actual API data
5276
received by the server.
@@ -162,7 +186,7 @@ conf = {{{packageName}}}.Configuration(
162186
username=None,
163187
password=None,
164188
{{/if}}
165-
disabled_client_side_validations="",
189+
disabled_json_schema_keywords=frozenset(),
166190
{{#if hasHttpSignatureMethods}}
167191
signing_info=None,
168192
{{/if}}
@@ -214,10 +238,8 @@ conf = {{{packageName}}}.Configuration(
214238
"""Password for HTTP basic authentication
215239
"""
216240
{{/if}}
217-
self.disabled_client_side_validations = disabled_client_side_validations
241+
self.disabled_json_schema_keywords = disabled_json_schema_keywords
218242
{{#if hasHttpSignatureMethods}}
219-
if signing_info is not None:
220-
signing_info.host = host
221243
self.signing_info = signing_info
222244
"""The HTTP signing configuration
223245
"""
@@ -314,20 +336,39 @@ conf = {{{packageName}}}.Configuration(
314336
result.debug = self.debug
315337
return result
316338

317-
def __setattr__(self, name, value):
318-
object.__setattr__(self, name, value)
319-
if name == 'disabled_client_side_validations':
320-
s = set(filter(None, value.split(',')))
321-
for v in s:
322-
if v not in JSON_SCHEMA_VALIDATION_KEYWORDS:
323-
raise ApiValueError(
324-
"Invalid keyword: '{0}''".format(v))
325-
self._disabled_client_side_validations = s
339+
@property
340+
def disabled_json_schema_keywords(self) -> typing.Set[str]:
341+
return self.__disabled_json_schema_keywords
342+
343+
@property
344+
def disabled_json_schema_python_keywords(self) -> typing.Set[str]:
345+
return self.__disabled_json_schema_python_keywords
346+
347+
@disabled_json_schema_keywords.setter
348+
def disabled_json_schema_keywords(self, json_keywords: typing.Set[str]):
349+
disabled_json_schema_keywords = set()
350+
disabled_json_schema_python_keywords = set()
351+
for k in json_keywords:
352+
if k not in JSON_SCHEMA_KEYWORD_TO_PYTHON_KEYWORD:
353+
raise ApiValueError(
354+
"Invalid keyword: '{0}''".format(k))
355+
disabled_json_schema_keywords.add(k)
356+
disabled_json_schema_python_keywords.add(JSON_SCHEMA_KEYWORD_TO_PYTHON_KEYWORD[k])
357+
self.__disabled_json_schema_keywords = disabled_json_schema_keywords
358+
self.__disabled_json_schema_python_keywords = disabled_json_schema_python_keywords
326359
{{#if hasHttpSignatureMethods}}
327-
if name == "signing_info" and value is not None:
360+
361+
@property
362+
def signing_info(self) -> typing.Optional['HttpSigningConfiguration']:
363+
return self.__signing_info
364+
365+
@signing_info.setter
366+
def signing_info(self, value: typing.Optional['HttpSigningConfiguration']):
367+
if value is not None:
328368
# Ensure the host paramater from signing info is the same as
329369
# Configuration.host.
330370
value.host = self.host
371+
self.__signing_info = value
331372
{{/if}}
332373

333374
@classmethod

modules/openapi-json-schema-generator/src/main/resources/python/model_templates/composed_schemas.handlebars

+38-64
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,70 @@
11
{{#with composedSchemas}}
2+
{{#if allOf}}
3+
4+
class all_of:
25
{{#each allOf}}
36
{{#unless refClass}}
4-
{{> model_templates/schema }}
5-
{{/unless}}
6-
{{/each}}
7-
{{#each oneOf}}
8-
{{#unless refClass}}
9-
{{> model_templates/schema }}
10-
{{/unless}}
11-
{{/each}}
12-
{{#each anyOf}}
13-
{{#unless refClass}}
14-
{{> model_templates/schema }}
7+
{{> model_templates/schema }}
8+
{{else}}
9+
10+
@staticmethod
11+
def {{#if nameInSnakeCase}}{{name}}{{else}}{{baseName}}{{/if}}() -> typing.Type['{{refClass}}']:
12+
return {{refClass}}
1513
{{/unless}}
1614
{{/each}}
17-
{{/with}}
18-
{{#with composedSchemas}}
19-
{{#if allOf}}
20-
21-
@classmethod
22-
@functools.lru_cache()
23-
def all_of(cls):
24-
# we need this here to make our import statements work
25-
# we must store _composed_schemas in here so the code is only run
26-
# when we invoke this method. If we kept this at the class
27-
# level we would get an error because the class level
28-
# code would be run when this module is imported, and these composed
29-
# classes don't exist yet because their module has not finished
30-
# loading
31-
return [
15+
classes = [
3216
{{#each allOf}}
33-
{{#if refClass}}
34-
{{refClass}},
35-
{{else}}
3617
{{#if nameInSnakeCase}}
37-
cls.{{name}},
18+
{{name}},
3819
{{else}}
39-
cls.{{baseName}},
20+
{{baseName}},
4021
{{/if}}
41-
{{/if}}
4222
{{/each}}
4323
]
4424
{{/if}}
4525
{{#if oneOf}}
4626

47-
@classmethod
48-
@functools.lru_cache()
49-
def one_of(cls):
50-
# we need this here to make our import statements work
51-
# we must store _composed_schemas in here so the code is only run
52-
# when we invoke this method. If we kept this at the class
53-
# level we would get an error because the class level
54-
# code would be run when this module is imported, and these composed
55-
# classes don't exist yet because their module has not finished
56-
# loading
57-
return [
27+
class one_of:
5828
{{#each oneOf}}
59-
{{#if refClass}}
60-
{{refClass}},
29+
{{#unless refClass}}
30+
{{> model_templates/schema }}
6131
{{else}}
32+
33+
@staticmethod
34+
def {{#if nameInSnakeCase}}{{name}}{{else}}{{baseName}}{{/if}}() -> typing.Type['{{refClass}}']:
35+
return {{refClass}}
36+
{{/unless}}
37+
{{/each}}
38+
classes = [
39+
{{#each oneOf}}
6240
{{#if nameInSnakeCase}}
63-
cls.{{name}},
41+
{{name}},
6442
{{else}}
65-
cls.{{baseName}},
43+
{{baseName}},
6644
{{/if}}
67-
{{/if}}
6845
{{/each}}
6946
]
7047
{{/if}}
7148
{{#if anyOf}}
7249

73-
@classmethod
74-
@functools.lru_cache()
75-
def any_of(cls):
76-
# we need this here to make our import statements work
77-
# we must store _composed_schemas in here so the code is only run
78-
# when we invoke this method. If we kept this at the class
79-
# level we would get an error because the class level
80-
# code would be run when this module is imported, and these composed
81-
# classes don't exist yet because their module has not finished
82-
# loading
83-
return [
50+
class any_of:
8451
{{#each anyOf}}
85-
{{#if refClass}}
86-
{{refClass}},
52+
{{#unless refClass}}
53+
{{> model_templates/schema }}
8754
{{else}}
55+
56+
@staticmethod
57+
def {{#if nameInSnakeCase}}{{name}}{{else}}{{baseName}}{{/if}}() -> typing.Type['{{refClass}}']:
58+
return {{refClass}}
59+
{{/unless}}
60+
{{/each}}
61+
classes = [
62+
{{#each anyOf}}
8863
{{#if nameInSnakeCase}}
89-
cls.{{name}},
64+
{{name}},
9065
{{else}}
91-
cls.{{baseName}},
66+
{{baseName}},
9267
{{/if}}
93-
{{/if}}
9468
{{/each}}
9569
]
9670
{{/if}}

modules/openapi-json-schema-generator/src/main/resources/python/model_templates/format_base.handlebars

-15
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,4 @@ schemas.DateTimeBase,
99
{{/eq}}
1010
{{#eq getFormat "number"}}
1111
schemas.DecimalBase,
12-
{{/eq}}
13-
{{#eq getFormat "binary"}}
14-
schemas.BinaryBase,
15-
{{/eq}}
16-
{{#eq getFormat "int32"}}
17-
schemas.Int32Base,
18-
{{/eq}}
19-
{{#eq getFormat "int64"}}
20-
schemas.Int64Base,
21-
{{/eq}}
22-
{{#eq getFormat "float"}}
23-
schemas.Float32Base,
24-
{{/eq}}
25-
{{#eq getFormat "double"}}
26-
schemas.Float64Base,
2712
{{/eq}}

modules/openapi-json-schema-generator/src/main/resources/python/model_templates/schema_composed_or_anytype.handlebars

+3-10
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,8 @@ class {{#if this.classname}}{{classname}}{{else}}{{#if nameInSnakeCase}}{{name}}
55
{{#if getFormat}}
66
{{> model_templates/format_base }}
77
{{/if}}
8-
{{#if composedSchemas}}
9-
schemas.ComposedSchema,
10-
{{else}}
118
schemas.AnyTypeSchema,
12-
{{/if}}
139
{{else}}
14-
{{#if composedSchemas}}
15-
schemas.ComposedBase,
16-
{{/if}}
17-
{{#if isEnum}}
18-
schemas.EnumBase,
19-
{{/if}}
2010
{{> model_templates/xbase_schema }}
2111
{{/if}}
2212
):
@@ -34,6 +24,9 @@ class {{#if this.classname}}{{classname}}{{else}}{{#if nameInSnakeCase}}{{name}}
3424

3525

3626
class MetaOapg:
27+
{{#if isAnyType}}
28+
# any type
29+
{{/if}}
3730
{{> model_templates/types }}
3831
{{#if getFormat}}
3932
format = '{{getFormat}}'

modules/openapi-json-schema-generator/src/main/resources/python/model_templates/schema_simple.handlebars

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11

22

33
class {{#if this.classname}}{{classname}}{{else}}{{#if nameInSnakeCase}}{{name}}{{else}}{{baseName}}{{/if}}{{/if}}(
4-
{{#if isEnum}}
5-
schemas.EnumBase,
6-
{{/if}}
74
{{> model_templates/xbase_schema }}
85
):
96
{{#if this.classname}}
@@ -22,6 +19,9 @@ class {{#if this.classname}}{{classname}}{{else}}{{#if nameInSnakeCase}}{{name}}
2219

2320

2421
class MetaOapg:
22+
{{#if isAnyType}}
23+
# any type
24+
{{/if}}
2525
{{> model_templates/types }}
2626
{{#if getFormat}}
2727
format = '{{getFormat}}'

modules/openapi-json-schema-generator/src/main/resources/python/model_templates/types.handlebars

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
{{#if isAnyType}}
2-
types = None
3-
{{else}}
1+
{{#unless isAnyType}}
42
types = {
5-
{{/if}}
63
{{#if isNull}}
74
schemas.NoneClass,
85
{{/if}}
@@ -26,6 +23,5 @@ types = {
2623
{{#if isBoolean}}
2724
schemas.BoolClass,
2825
{{/if}}
29-
{{#unless isAnyType}}
3026
}
3127
{{/unless}}

0 commit comments

Comments
 (0)