|
36 | 36 | get_paths, |
37 | 37 | get_responses, |
38 | 38 | get_schemas, |
| 39 | + validate_spec, |
39 | 40 | ) |
40 | 41 |
|
41 | 42 | MA_VERSION = Version(importlib.metadata.version("marshmallow")) |
@@ -1369,3 +1370,146 @@ class SchemaWithTimeDelta(Schema): |
1369 | 1370 | get_schemas(spec)["SchemaWithTimeDelta"]["properties"]["day"]["x-unit"] |
1370 | 1371 | == "days" |
1371 | 1372 | ) |
| 1373 | + |
| 1374 | + |
| 1375 | +try: |
| 1376 | + from marshmallow_oneofschema import OneOfSchema |
| 1377 | + |
| 1378 | + ONEOFSCHEMA_AVAILABLE = True |
| 1379 | +except ImportError: |
| 1380 | + ONEOFSCHEMA_AVAILABLE = False |
| 1381 | + |
| 1382 | + |
| 1383 | +if ONEOFSCHEMA_AVAILABLE: |
| 1384 | + |
| 1385 | + class OneOfFooSchema(Schema): |
| 1386 | + foo = String() |
| 1387 | + |
| 1388 | + class OneOfBarSchema(Schema): |
| 1389 | + bar = Int() |
| 1390 | + |
| 1391 | + class ExampleOneOf(OneOfSchema): |
| 1392 | + type_field = "type" |
| 1393 | + type_schemas = { |
| 1394 | + "foo": OneOfFooSchema, |
| 1395 | + "bar": OneOfBarSchema, |
| 1396 | + } |
| 1397 | + |
| 1398 | + class ExampleParentSchema(Schema): |
| 1399 | + item = Nested(ExampleOneOf) |
| 1400 | + |
| 1401 | + |
| 1402 | +@pytest.mark.skipif( |
| 1403 | + not ONEOFSCHEMA_AVAILABLE, reason="marshmallow-oneofschema not installed" |
| 1404 | +) |
| 1405 | +class TestOneOfSchemaIntegration: |
| 1406 | + @pytest.mark.parametrize("openapi_version", ("3.0.2", "3.1.0")) |
| 1407 | + def test_spec_components_schema_with_oneof(self, openapi_version): |
| 1408 | + spec = APISpec( |
| 1409 | + title="OneOf Test", |
| 1410 | + version="1.0.0", |
| 1411 | + openapi_version=openapi_version, |
| 1412 | + plugins=[MarshmallowPlugin()], |
| 1413 | + ) |
| 1414 | + spec.components.schema("ExampleOneOf", schema=ExampleOneOf) |
| 1415 | + |
| 1416 | + schemas = get_schemas(spec) |
| 1417 | + assert "ExampleOneOf" in schemas |
| 1418 | + assert "OneOfFoo" in schemas |
| 1419 | + assert "OneOfBar" in schemas |
| 1420 | + assert schemas["ExampleOneOf"] == { |
| 1421 | + "oneOf": [ |
| 1422 | + build_ref(spec, "schema", "OneOfFoo"), |
| 1423 | + build_ref(spec, "schema", "OneOfBar"), |
| 1424 | + ], |
| 1425 | + "discriminator": { |
| 1426 | + "propertyName": "type", |
| 1427 | + "mapping": { |
| 1428 | + "foo": build_ref(spec, "schema", "OneOfFoo")["$ref"], |
| 1429 | + "bar": build_ref(spec, "schema", "OneOfBar")["$ref"], |
| 1430 | + }, |
| 1431 | + }, |
| 1432 | + } |
| 1433 | + validate_spec(spec) |
| 1434 | + |
| 1435 | + @pytest.mark.parametrize("openapi_version", ("3.0.2", "3.1.0")) |
| 1436 | + def test_spec_nested_oneof_schema(self, openapi_version): |
| 1437 | + spec = APISpec( |
| 1438 | + title="OneOf Nested Test", |
| 1439 | + version="1.0.0", |
| 1440 | + openapi_version=openapi_version, |
| 1441 | + plugins=[MarshmallowPlugin()], |
| 1442 | + ) |
| 1443 | + spec.components.schema("Parent", schema=ExampleParentSchema) |
| 1444 | + |
| 1445 | + schemas = get_schemas(spec) |
| 1446 | + assert "Parent" in schemas |
| 1447 | + assert "ExampleOneOf" in schemas |
| 1448 | + assert "OneOfFoo" in schemas |
| 1449 | + assert "OneOfBar" in schemas |
| 1450 | + assert schemas["Parent"]["properties"]["item"] == build_ref( |
| 1451 | + spec, "schema", "ExampleOneOf" |
| 1452 | + ) |
| 1453 | + validate_spec(spec) |
| 1454 | + |
| 1455 | + @pytest.mark.parametrize("openapi_version", ("3.0.2", "3.1.0")) |
| 1456 | + def test_spec_operations_with_oneof(self, openapi_version): |
| 1457 | + spec = APISpec( |
| 1458 | + title="OneOf Operations Test", |
| 1459 | + version="1.0.0", |
| 1460 | + openapi_version=openapi_version, |
| 1461 | + plugins=[MarshmallowPlugin()], |
| 1462 | + ) |
| 1463 | + spec.path( |
| 1464 | + "/items", |
| 1465 | + operations={ |
| 1466 | + "post": { |
| 1467 | + "requestBody": { |
| 1468 | + "content": {"application/json": {"schema": ExampleOneOf}} |
| 1469 | + }, |
| 1470 | + "responses": { |
| 1471 | + "200": { |
| 1472 | + "description": "success", |
| 1473 | + "content": {"application/json": {"schema": ExampleOneOf}}, |
| 1474 | + } |
| 1475 | + }, |
| 1476 | + } |
| 1477 | + }, |
| 1478 | + ) |
| 1479 | + schemas = get_schemas(spec) |
| 1480 | + assert "ExampleOneOf" in schemas |
| 1481 | + assert "OneOfFoo" in schemas |
| 1482 | + assert "OneOfBar" in schemas |
| 1483 | + paths = get_paths(spec) |
| 1484 | + assert paths["/items"]["post"]["requestBody"]["content"]["application/json"][ |
| 1485 | + "schema" |
| 1486 | + ] == build_ref(spec, "schema", "ExampleOneOf") |
| 1487 | + validate_spec(spec) |
| 1488 | + |
| 1489 | + @pytest.mark.parametrize("openapi_version", ("3.0.2", "3.1.0")) |
| 1490 | + def test_spec_anonymous_oneof_inline(self, openapi_version): |
| 1491 | + spec = APISpec( |
| 1492 | + title="Anonymous OneOf", |
| 1493 | + version="1.0.0", |
| 1494 | + openapi_version=openapi_version, |
| 1495 | + plugins=[MarshmallowPlugin(schema_name_resolver=lambda s: None)], |
| 1496 | + ) |
| 1497 | + spec.path( |
| 1498 | + "/items", |
| 1499 | + operations={ |
| 1500 | + "post": { |
| 1501 | + "requestBody": { |
| 1502 | + "content": {"application/json": {"schema": ExampleOneOf}} |
| 1503 | + }, |
| 1504 | + "responses": {"200": {"description": "success"}}, |
| 1505 | + } |
| 1506 | + }, |
| 1507 | + ) |
| 1508 | + paths = get_paths(spec) |
| 1509 | + schema = paths["/items"]["post"]["requestBody"]["content"]["application/json"][ |
| 1510 | + "schema" |
| 1511 | + ] |
| 1512 | + assert "oneOf" in schema |
| 1513 | + assert len(schema["oneOf"]) == 2 |
| 1514 | + assert schema["discriminator"] == {"propertyName": "type"} |
| 1515 | + validate_spec(spec) |
0 commit comments