Skip to content

Commit 7eada08

Browse files
committed
move OAS schema initialization from AutoSchema to SchemaGenerator
1 parent 3d41acd commit 7eada08

File tree

3 files changed

+51
-60
lines changed

3 files changed

+51
-60
lines changed

docs/usage.md

+35-38
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
`
21
# Usage
32

43
The DJA package implements a custom renderer, parser, exception handler, query filter backends, and
@@ -978,41 +977,43 @@ REST_FRAMEWORK = {
978977
}
979978
```
980979

981-
### View-based
980+
### Add some static OAS schema content
982981

983-
You can explicitly use DJA's AutoSchema in your view definition, optionally including an OAS schema document
984-
initializer:
982+
You can optionally include an OAS schema document initialization by subclassing SchemaGenerator
983+
and setting `schema_init`.
985984

986-
```python
987-
from rest_framework_json_api.schemas.openapi import AutoSchema
988-
989-
TODO: resolve this with upstream changes:
985+
Here's an example that fills out OAS `info` and `servers` objects.
990986

991-
openapi_schema = {
992-
'info': {
993-
'version': '1.0',
994-
'title': 'my demo API',
995-
'description': 'A demonstration of [OAS 3.0](https://www.openapis.org) AutoSchema',
996-
'contact': {
997-
'name': 'my name'
998-
},
999-
'license': {
1000-
'name': 'BSD 2 clause',
1001-
'url': 'https://github.com/django-json-api/django-rest-framework-json-api/blob/master/LICENSE',
1002-
}
1003-
},
1004-
'servers': [
1005-
{'url': 'https://localhost/v1', 'description': 'local docker'},
1006-
{'url': 'http://localhost:8000/v1', 'description': 'local dev'},
1007-
{'url': 'https://api.example.com/v1', 'description': 'demo server'},
1008-
{'url': '{serverURL}', 'description': 'provide your server URL',
1009-
'variables': {'serverURL': {'default': 'http://localhost:8000/v1'}}}
1010-
]
1011-
}
987+
```python
988+
from rest_framework_json_api.schemas.openapi import SchemaGenerator as JSONAPISchemaGenerator
1012989

1013990

1014-
class MyViewSet(ModelViewSet):
1015-
schema = AutoSchema(openapi_schema=openapi_schema)
991+
class SchemaGenerator(JSONAPISchemaGenerator):
992+
"""
993+
Describe my OAS schema info in detail and list the servers where it can be found
994+
"""
995+
#: fill in some of the openapi schema
996+
schema_init = {
997+
'info': {
998+
'version': '1.0',
999+
'title': 'my demo API',
1000+
'description': 'A demonstration of [OAS 3.0](https://www.openapis.org)',
1001+
'contact': {
1002+
'name': 'my name'
1003+
},
1004+
'license': {
1005+
'name': 'BSD 2 clause',
1006+
'url': 'https://github.com/django-json-api/django-rest-framework-json-api/blob/master/LICENSE',
1007+
}
1008+
},
1009+
'servers': [
1010+
{'url': 'https://localhost/v1', 'description': 'local docker'},
1011+
{'url': 'http://localhost:8000/v1', 'description': 'local dev'},
1012+
{'url': 'https://api.example.com/v1', 'description': 'demo server'},
1013+
{'url': '{serverURL}', 'description': 'provide your server URL',
1014+
'variables': {'serverURL': {'default': 'http://localhost:8000/v1'}}}
1015+
]
1016+
}
10161017
```
10171018

10181019
### Get Schema in View
@@ -1025,20 +1026,16 @@ from rest_framework_json_api.schemas.openapi import SchemaGenerator
10251026
...
10261027

10271028
urlpatterns = [
1028-
path('openapi', get_schema_view(
1029-
title="Example API",
1030-
description="API for all things …",
1031-
version="1.0.0",
1032-
generator_class=SchemaGenerator
1033-
), name='openapi-schema'),
1029+
path('openapi', get_schema_view(generator_class='myapp.views.SchemaGenerator'), name='openapi-schema'),
10341030
...
10351031
]
10361032
```
10371033
### Get Schema on Command Line
10381034
To generate an OAS schema document, use something like:
10391035

10401036
```text
1041-
$ django-admin generateschema --settings=example.settings >myschema.yaml
1037+
$ django-admin generateschema --settings=example.settings \
1038+
--generator_class myapp.views.SchemaGenerator >myschema.yaml
10421039
```
10431040

10441041
You can then use any number of OAS tools such as

rest_framework_json_api/schemas/openapi.py

+15-21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import warnings
2+
from deepmerge import always_merger
23
from urllib.parse import urljoin
34

45
from django.db.models.fields import related_descriptors as rd
@@ -12,7 +13,7 @@
1213
from rest_framework_json_api import serializers
1314
from rest_framework_json_api.views import RelationshipView
1415

15-
#: static OAS 3.0 component definitions that are referenced by AutoSchema.
16+
#: static OAS 3.0 component definitions that are referenced by SchemaGenerator.
1617
JSONAPI_COMPONENTS = {
1718
'schemas': {
1819
'jsonapi': {
@@ -270,11 +271,18 @@ class SchemaGenerator(drf_openapi.SchemaGenerator):
270271
"""
271272
Extend DRF's SchemaGenerator to implement jsonapi-flavored generateschema command
272273
"""
274+
#: JSONAPI component defintions that are referenced by the generated OAS schema.
275+
jsonapi_components = JSONAPI_COMPONENTS
276+
#: optional added OAS schema initialization. Make a subclass and override as needed.
277+
schema_init = {}
278+
273279
def __init__(self, *args, **kwargs):
274-
self.openapi_schema = {}
275280
super().__init__(*args, **kwargs)
276281
if not hasattr(self, 'check_duplicate_operation_id'):
277282
self.check_duplicate_operation_id = lambda paths: None
283+
# static JSONAPI fields that get $ref'd to in the view mappings
284+
# merge in our reference data on top of anything provided by the init.
285+
self.schema_init = always_merger.merge(self.schema_init, {'components': self.jsonapi_components})
278286

279287
def get_schema(self, request=None, public=False):
280288
"""
@@ -283,7 +291,6 @@ def get_schema(self, request=None, public=False):
283291
"""
284292
self._initialise_endpoints()
285293
components_schemas = {}
286-
components_schemas.update(JSONAPI_COMPONENTS)
287294

288295
# Iterate endpoints generating per method path operations.
289296
paths = {}
@@ -334,9 +341,6 @@ def get_schema(self, request=None, public=False):
334341

335342
paths.setdefault(path, {})
336343
paths[path][method.lower()] = operation
337-
if hasattr(view.schema, 'openapi_schema'): # TODO: still needed?
338-
# TODO: shallow or deep merge?
339-
self.openapi_schema = {**self.openapi_schema, **view.schema.openapi_schema}
340344

341345
self.check_duplicate_operation_id(paths)
342346

@@ -345,10 +349,11 @@ def get_schema(self, request=None, public=False):
345349
'openapi': '3.0.2',
346350
'info': self.get_info(),
347351
'paths': paths,
348-
'components': components_schemas,
352+
'components': {
353+
'schemas': components_schemas,
354+
}
349355
}
350-
351-
return schema
356+
return always_merger.merge(schema, self.schema_init)
352357

353358
def _expand_relationships(self, path, method, view):
354359
"""
@@ -437,23 +442,12 @@ class AutoSchema(drf_openapi.AutoSchema):
437442
"""
438443
content_types = ['application/vnd.api+json']
439444

440-
def __init__(self, openapi_schema=None, **kwargs):
445+
def __init__(self, **kwargs):
441446
"""
442447
Initialize the JSONAPI OAS schema generator
443448
:param openapi_schema: dict: OAS 3.0 document with initial values.
444449
"""
445450
super().__init__(**kwargs)
446-
#: allow initialization of OAS schema doc TODO: still needed?
447-
if openapi_schema is None:
448-
openapi_schema = {}
449-
self.openapi_schema = openapi_schema
450-
# static JSONAPI fields that get $ref'd to in the view mappings
451-
jsonapi_ref = {
452-
'components': JSONAPI_COMPONENTS
453-
}
454-
# merge in our reference data on top of anything provided by the init.
455-
# TODO: shallow or deep merge?
456-
self.openapi_schema = {**self.openapi_schema, **jsonapi_ref}
457451

458452
# DRF >= 3.12 (not yet released) has changed a bunch of private methods to public.
459453
# Accommodate those renamings for DRF < 3.12

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def get_package_data(package):
9696
extras_require={
9797
'django-polymorphic': ['django-polymorphic>=2.0'],
9898
'django-filter': ['django-filter>=2.0'],
99-
'openapi': ['pyyaml>=5.3', 'uritemplate>=3.0.1']
99+
'openapi': ['pyyaml>=5.3', 'uritemplate>=3.0.1', 'deepmerge>=0.1.0',]
100100
},
101101
setup_requires=wheel,
102102
python_requires=">=3.5",

0 commit comments

Comments
 (0)