Skip to content

Commit

Permalink
Merge pull request #18 from jokiefer/feature/add-support-for-drf-spec…
Browse files Browse the repository at this point in the history
…tacular-0.26

add tox as testing suite
  • Loading branch information
jokiefer authored Dec 12, 2023
2 parents d65f9d7 + 8765b27 commit cace58c
Show file tree
Hide file tree
Showing 15 changed files with 145 additions and 42 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ jobs:
fetch-depth: 0
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install tox
- name: Run Tests
run: |
python manage.py test
tox
- name: Build
run: |
python setup.py sdist bdist_wheel
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@ dist/

# virtualenv
.venv

.tox/

demofile.txt
demofile2.txt
12 changes: 12 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@
],
"django": true,
"justMyCode": false
},
{
"name": "Python: tox",
"type": "python",
"request": "launch",
"module": "tox",
"args": [
"-e",
"drf-spectacular27"
],
"django": true,
"justMyCode": false
}
]
}
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
],
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": true
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
},
"files.associations": {
"*.yaml": "home-assistant"
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ Note that in line with [Django REST framework policy](https://www.django-rest-fr
any parts of the framework not mentioned in the documentation should generally be considered private API, and may be subject to change.


## [0.4.0] - 2023-12-12


### Fixed

- `ordered` helper function in test suite, which now successfully replace line breaks

### Added

- adds support for drf-spectacular versions above 0.25
- adds support for drf-spectacular versions above 0.25

## [0.3.3] - 2023-11-07

### Fixed
Expand Down
23 changes: 23 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@ drf-spectecular-json-api

open api 3 schema generator for `drf-json-api <https://github.com/django-json-api/django-rest-framework-json-api>`_ package based on `drf-spectacular <https://github.com/tfranzel/drf-spectacular>`__ package.

Tested with various dependency version
--------------------------------------

.. list-table:: Tested for versions in combination:
:widths: 25 25 50
:header-rows: 1

* - python
- django
- drf-spectacular
* - 3.8
- 4.0
- 0.25.x
* - 3.9
- 4.1
- 0.26.x
* - 3.10
- 4.2
- 0.27.x
* - 3.11
- 5.0
-

Installation
------------

Expand Down
2 changes: 1 addition & 1 deletion drf_spectacular_jsonapi/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.3.3'
__version__ = '0.4.0'
3 changes: 3 additions & 0 deletions drf_spectacular_jsonapi/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
class DRFSpectacularJsonApiConfig(AppConfig):
name = 'drf_spectacular_jsonapi'
verbose_name = "drf-spectacular-jsonapi"

def ready(self):
import drf_spectacular_jsonapi.openapi # noqa: F408
20 changes: 19 additions & 1 deletion drf_spectacular_jsonapi/schemas/openapi.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from typing import Dict, List, Tuple

from django.utils.translation import gettext_lazy as _
from drf_spectacular.contrib.django_filters import DjangoFilterExtension
from drf_spectacular.drainage import add_trace_message
from drf_spectacular.openapi import AutoSchema
from drf_spectacular.plumbing import (ResolvedComponent, build_array_type,
build_parameter_type, is_list_serializer)
build_parameter_type, get_manager,
get_view_model, is_list_serializer)
from rest_framework_json_api.serializers import SparseFieldsetsMixin
from rest_framework_json_api.utils import (format_field_name,
get_resource_name,
Expand All @@ -14,6 +17,20 @@
from drf_spectacular_jsonapi.schemas.utils import get_primary_key_of_serializer


class DjangoJsonApiFilterExtension(DjangoFilterExtension):
target_class = 'rest_framework_json_api.django_filters.backends.DjangoFilterBackend'
priority = 1

def resolve_filter_field(self, *args, **kwargs):
result = super().resolve_filter_field(*args, **kwargs)
for item in result:
name = item["name"]
if "filter[" not in name:
name = f"filter[{name}]"
item["name"] = name
return result


class JsonApiAutoSchema(AutoSchema):
"""
Extend DRF's spectacular AutoSchema for JSON:API serialization.
Expand All @@ -36,6 +53,7 @@ def _get_filter_parameters(self) -> Dict:
See also json:api docs: https://jsonapi.org/format/#fetching-sorting
"""
parameters = super()._get_filter_parameters()

sort_param = next(
(parameter for parameter in parameters if parameter["name"] == "sort"), None)
if sort_param and hasattr(self.view, "ordering_fields") and self.view.ordering_fields:
Expand Down
6 changes: 3 additions & 3 deletions requirements/base.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Django>=3.2
drf-spectacular>=0.26.1
drf-extensions>=0.7.1
djangorestframework>=3.13
djangorestframework-jsonapi>=6.0.0
drf-spectacular==0.25.1
drf-extensions==0.7.1
djangorestframework-jsonapi>=6.0.0
11 changes: 2 additions & 9 deletions requirements/testing.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
pytest>=5.3.5
pytest-django>=3.8.0
pytest-cov>=2.8.1
flake8==6.0.0
mypy>=0.770
django-stubs>=1.8.0,<1.10.0
djangorestframework-stubs>=1.1.0
types-PyYAML>=0.1.6
isort>=5.0.4
autopep8==2.0.0
djangorestframework-jsonapi[django-filter]==6.0.0
djangorestframework-jsonapi[django-filter]>=6.0.0
tox>=4.11.4
11 changes: 7 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
with open("README.rst", "r", encoding="utf-8") as fh:
long_description = fh.read()

with open('requirements/base.txt') as fh:
requirements = [r for r in fh.read().split('\n') if not r.startswith('#')]


def get_version(package):
"""
Expand All @@ -43,7 +40,13 @@ def get_version(package):
packages=[p for p in find_namespace_packages(
exclude=('tests*',)) if p.startswith(package)],
include_package_data=True,
install_requires=requirements,
install_requires=[
"Django>=3.2",
"drf-spectacular>=0.25.0",
"drf-extensions>=0.7.1",
"djangorestframework>=3.13",
"djangorestframework-jsonapi>=6.0.0"
],
classifiers=[
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
Expand Down
2 changes: 2 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
'PREPROCESSING_HOOKS': [
"drf_spectacular_jsonapi.hooks.fix_nested_path_parameters"
],
# drf-spectacular >0.26 added this option which is default True. This feature is not needed and in my pov not best practice to add enum choices...
"ENUM_GENERATE_CHOICE_DESCRIPTION": False
}


Expand Down
47 changes: 27 additions & 20 deletions tests/tests_schema.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import json

import rest_framework_json_api
from django.test.testcases import SimpleTestCase
from drf_spectacular.generators import SchemaGenerator
from drf_spectacular.validation import validate_schema
Expand All @@ -12,8 +15,12 @@ def ordered(self, obj):
return sorted((k, self.ordered(v)) for k, v in obj.items())
if isinstance(obj, list):
return sorted(self.ordered(x) for x in obj)
if isinstance(obj, tuple):
# nested call
for item in obj:
self.ordered(item)
if isinstance(obj, str):
obj.replace("\n", "")
obj = obj.replace("\n", "")
return obj

def setUp(self) -> None:
Expand All @@ -23,6 +30,8 @@ def setUp(self) -> None:
# make sure generated schemas are always valid
validate_schema(self.schema)

self.json_api_site_package_version = rest_framework_json_api.VERSION


class TestSchemaOutputForSimpleModelSerializer(SimpleSchemaTestCase):

Expand Down Expand Up @@ -191,7 +200,7 @@ def test_get_parameters(self):
'in': 'query',
'name': 'sort',
'required': False,
'description': 'Which field to use when ordering the results.',
'description': 'Which field to use when ordering the results.' if self.json_api_site_package_version == "6.0.0" else '[list of fields to sort by](https://jsonapi.org/format/#fetching-sorting)',
'schema': {'type': 'array', 'items': {'type': 'string', 'enum': ['id', 'title', '-id', '-title']}},
'explode': False
},
Expand All @@ -205,18 +214,16 @@ def test_get_parameters(self):
{
'in': 'query',
'name': 'filter[genre]',
'required': False,
'description': 'genre',
'schema': {'type': 'string', 'enum': ["POP", "ROCK"]}
'description': 'Wich kind of genre this Album represents\n',
'schema': {'type': 'string', 'title': 'Nice Genre', 'enum': ["POP", "ROCK"]}
},
{
'in': 'query',
'name': 'filter[title.contains]',
'required': False,
'description': 'title__contains',
'name': 'filter[title__contains]',
'schema': {'type': 'string'}
}
])

self.assertEqual(expected, calculated)

def test_post_request_body(self):
Expand All @@ -226,12 +233,10 @@ def test_post_request_body(self):
"#/components/schemas/AlbumRequest"
)

calculated = self.ordered(
self.schema["components"]["schemas"]["AlbumRequest"])
expected = self.ordered(
{
"type": "object",
"properties": {
calculated = self.schema["components"]["schemas"]["AlbumRequest"]
expected = {
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -309,11 +314,12 @@ def test_post_request_body(self):
"required": ["type"],
"additionalProperties": False
}
},
"required": ["data"],
}
)
self.assertEqual(expected, calculated)
},
"required": ["data"],
}

self.assertJSONEqual(json.dumps(
calculated, sort_keys=True), json.dumps(expected, sort_keys=True))

self.assertEqual(
self.schema["paths"]["/songs/"]["post"]["requestBody"]["content"]["application/vnd.api+json"]["schema"]["$ref"],
Expand Down Expand Up @@ -413,7 +419,8 @@ def test_post_request_body(self):
"required": ["data"],
}
)
self.assertEqual(expected, calculated)
self.assertJSONEqual(json.dumps(
calculated, sort_keys=True), json.dumps(expected, sort_keys=True))

def test_patch_request_body(self):
"""Tests if the request body matches the json:api payload schema"""
Expand Down
25 changes: 25 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[tox]
requires =
tox>=4
env_list =
py{38,39,310,311}-django{40,41,42,50}-drf-spectacular{25,26,27}

[testenv]
description = run unit tests
deps=
drf-spectacular27: drf-spectacular>=0.27.0,<0.28.0
drf-spectacular26: drf-spectacular>=0.26.0,<0.27.0
drf-spectacular25: drf-spectacular>=0.25.0,<0.26.0
django50: Django>=5.0,<5.1
django42: Django>=4.2,<4.3
django41: Django>=4.1,<4.2
django40: Django>=4.0,<4.1
djangorestframework>=3.13
djangorestframework-jsonapi>=6.0.0
djangorestframework-jsonapi[django-filter]>=6.0.0

setenv =
PYTHONPATH = {toxinidir}

commands =
python manage.py test {posargs}

0 comments on commit cace58c

Please sign in to comment.