Skip to content

Commit 8df8b25

Browse files
committed
Merge remote-tracking branch 'lundalogik/handle-one-of' into 15-fix-oneof-validation-handling
# Conflicts: # tests/test_dump.py # marshmallow_jsonschema/validation.py # ruff caught / fix
2 parents c11d84d + 0b4d250 commit 8df8b25

File tree

3 files changed

+54
-20
lines changed

3 files changed

+54
-20
lines changed

marshmallow_jsonschema/validation.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,14 @@ def handle_length(schema, field, validator, _parent_schema):
5151
return schema
5252

5353

54-
def handle_one_of(schema, _field, validator, _parent_schema):
54+
def handle_one_of(schema, field, validator, _parent_schema):
5555
"""Adds the validation logic for ``marshmallow.validate.OneOf`` by setting
56-
the JSONSchema `enum` property to the allowed choices in the validator.
56+
the JSONSchema `oneOf` property to the allowed choices in the validator.
5757
5858
Args:
5959
schema (dict): The original JSON schema we generated. This is what we
6060
want to post-process.
61-
_field (fields.Field): The field that generated the original schema and
61+
field (fields.Field): The field that generated the original schema and
6262
who this post-processor belongs to.
6363
validator (marshmallow.validate.OneOf): The validator attached to the
6464
passed in field.
@@ -69,8 +69,21 @@ def handle_one_of(schema, _field, validator, _parent_schema):
6969
dict: New JSON Schema that has been post processed and
7070
altered.
7171
"""
72-
schema["enum"] = list(validator.choices)
73-
schema["enumNames"] = list(validator.labels)
72+
if schema["type"] not in ["string", "number"]:
73+
return schema
74+
75+
if "oneOf" in schema:
76+
return schema
77+
78+
choices = [field._serialize(choice, field.name, None) for choice in validator.choices]
79+
if not choices:
80+
return schema
81+
82+
labels = validator.labels if validator.labels else choices
83+
schema["oneOf"] = [
84+
{"type": schema["type"], "title": label, "const": choice}
85+
for choice, label in zip(choices, labels, strict=False)
86+
]
7487

7588
return schema
7689

tests/test_dump.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,8 +549,8 @@ class TestSchema(Schema):
549549
dumped = validate_and_dump(schema)
550550

551551
assert dumped["definitions"]["TestSchema"]["properties"]["foo"] == {
552-
"enum": list(mapping.values()),
553-
"enumNames": list(mapping.keys()),
552+
"oneOf": [{"type": "number", "title": k, "const": v} for k, v in mapping.items()],
553+
"format": "integer",
554554
"title": "foo",
555555
"type": "integer",
556556
}

tests/test_validation.py

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,11 @@ def test_one_of_validator():
5252

5353
dumped = validate_and_dump(schema)
5454

55-
assert dumped["definitions"]["UserSchema"]["properties"]["sex"]["enum"] == [
56-
"male",
57-
"female",
58-
"non_binary",
59-
"other",
60-
]
61-
assert dumped["definitions"]["UserSchema"]["properties"]["sex"]["enumNames"] == [
62-
"Male",
63-
"Female",
64-
"Non-binary/fluid",
65-
"Other",
55+
assert dumped["definitions"]["UserSchema"]["properties"]["sex"]["oneOf"] == [
56+
{"type": "string", "title": "Male", "const": "male"},
57+
{"type": "string", "title": "Female", "const": "female"},
58+
{"type": "string", "title": "Non-binary/fluid", "const": "non_binary"},
59+
{"type": "string", "title": "Other", "const": "other"},
6660
]
6761

6862

@@ -75,8 +69,35 @@ class TestSchema(Schema):
7569
dumped = validate_and_dump(schema)
7670

7771
foo_property = dumped["definitions"]["TestSchema"]["properties"]["foo"]
78-
assert foo_property["enum"] == []
79-
assert foo_property["enumNames"] == []
72+
assert "oneOf" not in foo_property
73+
74+
75+
def test_one_of_object():
76+
class TestSchema(Schema):
77+
foo = fields.Dict(validate=OneOf([{"a": 1}]))
78+
79+
schema = TestSchema()
80+
81+
dumped = validate_and_dump(schema)
82+
83+
foo_property = dumped["definitions"]["TestSchema"]["properties"]["foo"]
84+
assert "oneOf" not in foo_property
85+
86+
87+
def test_one_of_custom_field():
88+
class CustomField(fields.String):
89+
def _jsonschema_type_mapping(self):
90+
return {"type": "string", "oneOf": [{"const": "one"}, {"const": "two"}]}
91+
92+
class TestSchema(Schema):
93+
foo = CustomField(validate=OneOf(["one", "two"]))
94+
95+
schema = TestSchema()
96+
97+
dumped = validate_and_dump(schema)
98+
99+
foo_property = dumped["definitions"]["TestSchema"]["properties"]["foo"]
100+
assert foo_property["oneOf"] == [{"const": "one"}, {"const": "two"}]
80101

81102

82103
def test_range():

0 commit comments

Comments
 (0)