Beispiel #1
0
def test_namespace_versioning_urlpatterns_simplification(no_warnings):
    class NamespaceVersioningAPIView(generics.RetrieveUpdateDestroyAPIView):
        versioning_class = NamespaceVersioning
        serializer_class = Xv1Serializer
        queryset = VersioningModel.objects.all()

    urls = (
        path('x/<int:pk>/', NamespaceVersioningAPIView.as_view()),
        path('y/<pk>/', NamespaceVersioningAPIView.as_view()),
        re_path('z/(?P<pk>[0-9A-Fa-f-]+)/',
                NamespaceVersioningAPIView.as_view()),
    )
    generator = SchemaGenerator(
        patterns=[
            path('v1/<int:some_param>/', include((urls, 'v1'))),
        ],
        api_version='v1',
    )
    schema = generator.get_schema(request=None, public=True)

    for s in ['x', 'y', 'z']:
        parameters = schema['paths'][f'/v1/{{some_param}}/{s}/{{id}}/']['get'][
            'parameters']
        parameters = {p['name']: p for p in parameters}
        assert parameters['id']['schema']['type'] == 'integer'
        assert parameters['some_param']['schema']['type'] == 'integer'
def test_serializer_name_reuse(capsys):
    from rest_framework import routers

    from drf_spectacular.generators import SchemaGenerator
    router = routers.SimpleRouter()

    def x1():
        class XSerializer(serializers.Serializer):
            uuid = serializers.UUIDField()

        return XSerializer

    def x2():
        class XSerializer(serializers.Serializer):
            integer = serializers.IntegerField()

        return XSerializer

    class X1Viewset(mixins.ListModelMixin, viewsets.GenericViewSet):
        serializer_class = x1()

    router.register('x1', X1Viewset, basename='x1')

    class X2Viewset(mixins.ListModelMixin, viewsets.GenericViewSet):
        serializer_class = x2()

    router.register('x2', X2Viewset, basename='x2')

    generator = SchemaGenerator(patterns=router.urls)
    generator.get_schema(request=None, public=True)

    stderr = capsys.readouterr().err
    assert 'Encountered 2 components with identical names "X" and different classes' in stderr
Beispiel #3
0
def test_serializer_retrieval_from_view(no_warnings):
    class UnusedSerializer(serializers.Serializer):
        pass  # pragma: no cover

    class XSerializer(serializers.Serializer):
        id = serializers.UUIDField()

    class YSerializer(serializers.Serializer):
        id = serializers.UUIDField()

    class X1Viewset(mixins.ListModelMixin, viewsets.GenericViewSet):
        serializer_class = UnusedSerializer

        def get_serializer(self):
            return XSerializer()

    class X2Viewset(mixins.ListModelMixin, viewsets.GenericViewSet):
        def get_serializer_class(self):
            return YSerializer

    router = routers.SimpleRouter()
    router.register('x1', X1Viewset, basename='x1')
    router.register('x2', X2Viewset, basename='x2')
    generator = SchemaGenerator(patterns=router.urls)
    schema = generator.get_schema(request=None, public=True)
    validate_schema(schema)
    assert len(schema['components']['schemas']) == 2
    assert 'Unused' not in schema['components']['schemas']
def test_serializer_name_reuse(warnings):
    from rest_framework import routers

    from drf_spectacular.generators import SchemaGenerator
    router = routers.SimpleRouter()

    def x1():
        class XSerializer(serializers.Serializer):
            uuid = serializers.UUIDField()

        return XSerializer

    def x2():
        class XSerializer(serializers.Serializer):
            integer = serializers.IntegerField

        return XSerializer

    class X1Viewset(mixins.ListModelMixin, viewsets.GenericViewSet):
        serializer_class = x1()

    router.register('x1', X1Viewset, basename='x1')

    class X2Viewset(mixins.ListModelMixin, viewsets.GenericViewSet):
        serializer_class = x2()

    router.register('x2', X2Viewset, basename='x2')

    generator = SchemaGenerator(patterns=router.urls)
    generator.get_schema(request=None, public=True)
Beispiel #5
0
def generate_schema(route,
                    viewset=None,
                    view=None,
                    view_function=None,
                    patterns=None):
    from django.urls import path
    from rest_framework import routers
    from rest_framework.viewsets import ViewSetMixin

    from drf_spectacular.generators import SchemaGenerator

    if viewset:
        assert issubclass(viewset, ViewSetMixin)
        router = routers.SimpleRouter()
        router.register(route, viewset, basename=route)
        patterns = router.urls
    elif view:
        patterns = [path(route, view.as_view())]
    elif view_function:
        patterns = [path(route, view_function)]
    else:
        assert route is None and isinstance(patterns, list)

    generator = SchemaGenerator(patterns=patterns)
    schema = generator.get_schema(request=None, public=True)
    validate_schema(schema)  # make sure generated schemas are always valid
    return schema
Beispiel #6
0
def test_namespace_versioning_urlpatterns_simplification(no_warnings, path_func, path_str, pattern):
    class LookupModel(models.Model):
        field = models.IntegerField()

    class LookupSerializer(serializers.ModelSerializer):
        class Meta:
            model = LookupModel
            fields = '__all__'

    class NamespaceVersioningAPIView(generics.RetrieveUpdateDestroyAPIView):
        versioning_class = NamespaceVersioning
        serializer_class = LookupSerializer
        queryset = LookupModel.objects.all()

    # make sure regex are valid
    if path_func == re_path:
        re.compile(pattern)

    patterns_v1 = [path_func(pattern, NamespaceVersioningAPIView.as_view())]
    generator = SchemaGenerator(
        patterns=[path('v1/<int:some_param>/', include((patterns_v1, 'v1')))],
        api_version='v1',
    )
    schema = generator.get_schema(request=None, public=True)

    parameters = schema['paths']['/v1/{some_param}/' + path_str]['get']['parameters']
    for p in parameters:
        assert p['schema']['type'] == 'integer'
Beispiel #7
0
def test_schema_contains_only_urlpatterns_first_match(no_warnings):
    class XSerializer(serializers.Serializer):
        integer = serializers.IntegerField()

    class XAPIView(APIView):
        @extend_schema(responses=XSerializer)
        def get(self, request):
            pass  # pragma: no cover

    class YSerializer(serializers.Serializer):
        integer = serializers.DateTimeField()

    class YAPIView(APIView):
        @extend_schema(responses=YSerializer)
        def get(self, request):
            pass  # pragma: no cover

    urlpatterns = [
        path('api/x/', XAPIView.as_view()),  # only first occurrence is used
        path('api/x/', YAPIView.as_view()),
    ]
    generator = SchemaGenerator(patterns=urlpatterns)
    schema = generator.get_schema(request=None, public=True)
    validate_schema(schema)
    assert len(schema['components']['schemas']) == 1
    assert 'X' in schema['components']['schemas']
    operation = schema['paths']['/api/x/']['get']
    assert '#/components/schemas/X' in get_response_schema(operation)['$ref']
Beispiel #8
0
def test_basics(no_warnings):
    router = routers.SimpleRouter()
    router.register('albums', AlbumModelViewset, basename="album")
    generator = SchemaGenerator(patterns=router.urls)
    schema = generator.get_schema(request=None, public=True)

    assert_schema(schema, 'tests/test_basic.yml')
Beispiel #9
0
def test_url_path_versioning(no_warnings, viewset_cls, version):
    router = routers.SimpleRouter()
    router.register('x', viewset_cls, basename='x')
    generator = SchemaGenerator(
        patterns=[re_path(r'^(?P<version>[v1|v2]+)/', include((router.urls, 'x')))],
        api_version=version,
    )
    schema = generator.get_schema(request=None, public=True)
    assert_schema(schema, f'tests/test_versioning_{version}.yml')
Beispiel #10
0
def test_accept_header_versioning(no_warnings, viewset_cls, version):
    router = routers.SimpleRouter()
    router.register('x', viewset_cls, basename='x')
    generator = SchemaGenerator(
        patterns=[
            path('', include((router.urls, 'x'))),
        ],
        api_version=version,
    )
    schema = generator.get_schema(request=None, public=True)
    assert_schema(schema, f'tests/test_versioning_accept_{version}.yml')
Beispiel #11
0
def test_rest_auth(no_warnings):
    urlpatterns = [
        path('rest-auth/', include('dj_rest_auth.urls')),
        path('rest-auth/registration/', include('dj_rest_auth.registration.urls')),
    ]

    generator = SchemaGenerator(patterns=urlpatterns)
    schema = generator.get_schema(request=None, public=True)

    assert_schema(
        schema, 'tests/contrib/test_rest_auth.yml', transforms=transforms
    )
def test_fields(no_warnings):
    from django.core.validators import URLValidator

    generator = SchemaGenerator()
    schema = generator.get_schema(request=None, public=True)

    # url pattern changed between django 3.0 and 3.1
    field_url = schema['components']['schemas']['AllFields']['properties']['field_url']
    assert field_url['pattern'] == URLValidator.regex.pattern
    field_url['pattern'] = 'URL_REGEX_PATTERN'

    assert_schema(schema, 'tests/test_fields.yml')
def test_drf_jwt(no_warnings):
    router = routers.SimpleRouter()
    router.register('x', XViewset, basename="x")

    urlpatterns = [
        *router.urls,
        path('api-token-auth/', obtain_jwt_token, name='get_token'),
    ]

    generator = SchemaGenerator(patterns=urlpatterns)
    schema = generator.get_schema(request=None, public=True)

    assert_schema(schema, 'tests/contrib/test_drf_jwt.yml')
def test_regex_path_parameter_discovery(no_warnings):
    @extend_schema(responses=OpenApiTypes.FLOAT)
    @api_view(['GET'])
    def pi(request, foo):
        pass  # pragma: no cover

    urlpatterns = [url(r'^/pi/<int:precision>', pi)]
    generator = SchemaGenerator(patterns=urlpatterns)
    schema = generator.get_schema(request=None, public=True)
    parameter = schema['paths']['/pi/{precision}']['get']['parameters'][0]
    assert parameter['name'] == 'precision'
    assert parameter['in'] == 'path'
    assert parameter['schema']['type'] == 'integer'
Beispiel #15
0
def test_simplejwt(no_warnings):
    router = routers.SimpleRouter()
    router.register('x', XViewset, basename="x")

    urlpatterns = [
        *router.urls,
        path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
        path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    ]

    generator = SchemaGenerator(patterns=urlpatterns)
    schema = generator.get_schema(request=None, public=True)

    assert_schema(schema, 'tests/contrib/test_simplejwt.yml')
def test_oauth2_toolkit(no_warnings):
    router = routers.SimpleRouter()
    router.register('x', XViewset, basename="x")

    urlpatterns = [
        *router.urls,
        path('o/', include('oauth2_provider.urls',
                           namespace='oauth2_provider')),
    ]

    generator = SchemaGenerator(patterns=urlpatterns)
    schema = generator.get_schema(request=None, public=True)

    assert_schema(schema, 'tests/contrib/test_oauth_toolkit.yml')
Beispiel #17
0
def test_append_extra_components(no_warnings):
    class XSerializer(serializers.Serializer):
        id = serializers.UUIDField()

    class XAPIView(APIView):
        @extend_schema(responses={200: XSerializer})
        def get(self, request):
            pass  # pragma: no cover

    generator = SchemaGenerator(patterns=[
        url(r'^x$', XAPIView.as_view(), name='x'),
    ])
    schema = generator.get_schema(request=None, public=True)
    assert len(schema['components']['schemas']) == 2
    validate_schema(schema)
Beispiel #18
0
def generate_schema(route, viewset=None, view=None):
    from django.urls import path
    from rest_framework import routers
    from drf_spectacular.generators import SchemaGenerator

    patterns = []
    if viewset:
        router = routers.SimpleRouter()
        router.register(route, viewset, basename=route)
        patterns = router.urls
    if view:
        patterns = [path(route, view.as_view())]

    generator = SchemaGenerator(patterns=patterns)
    return generator.get_schema(request=None, public=True)
Beispiel #19
0
def test_drf_format_suffix_parameter_exclude(no_warnings):
    from rest_framework.urlpatterns import format_suffix_patterns

    @extend_schema(responses=OpenApiTypes.FLOAT)
    @api_view(['GET'])
    def view_func(request, format=None):
        pass  # pragma: no cover

    urlpatterns = format_suffix_patterns([
        path('pi', view_func),
    ])
    generator = SchemaGenerator(patterns=urlpatterns)
    schema = generator.get_schema(request=None, public=True)
    validate_schema(schema)
    assert list(schema['paths'].keys()) == ['/pi']
Beispiel #20
0
def test_oauth2_toolkit(no_warnings):
    router = routers.SimpleRouter()
    router.register('TokenHasReadWriteScope', TokenHasReadWriteScopeViewset, basename="x1")
    router.register('TokenHasResourceScope', TokenHasResourceScopeViewset, basename="x2")
    router.register('IsAuthenticatedOrTokenHasScope', IsAuthenticatedOrTokenHasScopeViewset, basename="x3")

    urlpatterns = [
        *router.urls,
        path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
    ]

    generator = SchemaGenerator(patterns=urlpatterns)
    schema = generator.get_schema(request=None, public=True)

    assert_schema(schema, 'tests/contrib/test_oauth_toolkit.yml')
Beispiel #21
0
class DrfSpectacularSchemaLoader(BaseSchemaLoader):
    """
    Loads OpenAPI schema generated by drf_spectacular.
    """
    def __init__(self, field_key_map: Optional[Dict[str, str]] = None) -> None:
        super().__init__(field_key_map=field_key_map)
        from drf_spectacular.generators import SchemaGenerator

        self.schema_generator = SchemaGenerator()

    def load_schema(self) -> dict:
        """
        Loads generated schema from drf_spectacular and returns it as a dict.
        """
        return loads(dumps(self.schema_generator.get_schema(public=True)))

    def resolve_path(self, endpoint_path: str,
                     method: str) -> Tuple[str, ResolverMatch]:
        from drf_spectacular.settings import spectacular_settings

        de_parameterized_path, resolved_path = super().resolve_path(
            endpoint_path=endpoint_path, method=method)
        return (
            de_parameterized_path[len(spectacular_settings.
                                      SCHEMA_PATH_PREFIX or ""):],
            resolved_path,
        )
Beispiel #22
0
def test_mock_request_symmetry_plain(no_warnings):
    response = APIClient().get('/api/schema-plain/',
                               **{'HTTP_X_SPECIAL_HEADER': '1'})
    assert response.status_code == 200
    schema_online = yaml.load(response.content, Loader=yaml.SafeLoader)
    schema_offline = SchemaGenerator().get_schema(public=True)
    assert schema_offline == schema_online
def test_operation_id_collision_resolution(capsys):
    @extend_schema(responses=OpenApiTypes.FLOAT)
    @api_view(['GET'])
    def view_func(request, format=None):
        pass  # pragma: no cover

    urlpatterns = [
        path('pi/<int:foo>', view_func),
        path('pi/', view_func),
    ]
    generator = SchemaGenerator(patterns=urlpatterns)
    schema = generator.get_schema(request=None, public=True)

    assert schema['paths']['/pi/']['get']['operationId'] == 'pi_retrieve'
    assert schema['paths']['/pi/{foo}']['get']['operationId'] == 'pi_retrieve_2'
    assert 'operationId "pi_retrieve" has collisions' in capsys.readouterr().err
Beispiel #24
0
def test_free_form_responses(no_warnings):
    class XAPIView(APIView):
        @extend_schema(responses={200: OpenApiTypes.OBJECT})
        def get(self, request):
            pass  # pragma: no cover

    class YAPIView(APIView):
        @extend_schema(responses=OpenApiTypes.OBJECT)
        def get(self, request):
            pass  # pragma: no cover

    generator = SchemaGenerator(patterns=[
        url(r'^x$', XAPIView.as_view(), name='x'),
        url(r'^y$', YAPIView.as_view(), name='y'),
    ])
    schema = generator.get_schema(request=None, public=True)
    validate_schema(schema)
def test_rest_auth_token(no_warnings, settings):
    settings.REST_USE_JWT = True
    # flush module import cache to re-evaluate conditional import
    import dj_rest_auth.urls
    reload(dj_rest_auth.urls)

    urlpatterns = [
        # path('rest-auth/', include(urlpatterns)),
        path('rest-auth/', include('dj_rest_auth.urls')),
        path('rest-auth/registration/', include('dj_rest_auth.registration.urls')),
    ]

    generator = SchemaGenerator(patterns=urlpatterns)
    schema = generator.get_schema(request=None, public=True)

    assert_schema(
        schema, 'tests/contrib/test_rest_auth_token.yml', transforms=transforms
    )
def test_drf_format_suffix_parameter(no_warnings):
    from rest_framework.urlpatterns import format_suffix_patterns

    @extend_schema(responses=OpenApiTypes.FLOAT)
    @api_view(['GET'])
    def pi(request, format=None):
        pass  # pragma: no cover

    urlpatterns = [path(r'/pi', pi)]
    urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html'])

    generator = SchemaGenerator(patterns=urlpatterns)
    schema = generator.get_schema(request=None, public=True)

    assert len(schema['paths']) == 2
    format_parameter = schema['paths']['/pi{format}']['get']['parameters'][0]
    assert format_parameter['name'] == 'format'
    assert format_parameter['required'] is False
    assert format_parameter['in'] == 'path'
def test_basic_viewset_without_queryset_with_explicit_pk_typing(no_warnings):
    class XSerializer(serializers.Serializer):
        field = fields.IntegerField()

    class XViewset(viewsets.ViewSet):
        serializer_class = XSerializer

        def retrieve(self, request, *args, **kwargs):
            pass  # pragma: no cover

    urlpatterns = [
        path("api/<path:some_var>/<uuid:pk>/", XViewset.as_view({"get": "retrieve"}))
    ]
    generator = SchemaGenerator(patterns=urlpatterns)
    schema = generator.get_schema(request=None, public=True)
    validate_schema(schema)
    operation = schema['paths']['/api/{some_var}/{id}/']['get']
    assert operation['parameters'][0]['name'] == 'id'
    assert operation['parameters'][0]['schema']['format'] == 'uuid'
Beispiel #28
0
def test_mock_request_symmetry_version(no_warnings):
    response = APIClient().get(
        '/api/schema-versioned/', **{
            'HTTP_ACCEPT': 'application/json; version=v2',
        })
    assert response.status_code == 200
    schema_online = yaml.load(response.content, Loader=yaml.SafeLoader)
    schema_offline = SchemaGenerator(api_version='v2').get_schema(public=True)

    assert schema_offline == schema_online
    assert schema_online['info']['version'] == '0.0.0 (v2)'
Beispiel #29
0
def test_pagination_reusage(no_warnings):
    class XViewset(viewsets.ReadOnlyModelViewSet):
        queryset = SimpleModel.objects.all()
        serializer_class = SimpleSerializer
        pagination_class = pagination.LimitOffsetPagination

        @extend_schema(responses={'200': SimpleSerializer(many=True)})
        @action(methods=['GET'], detail=False)
        def custom_action(self):
            pass  # pragma: no cover

    class YViewset(XViewset):
        serializer_class = SimpleSerializer

    router = routers.SimpleRouter()
    router.register('x', XViewset, basename='x')
    router.register('y', YViewset, basename='y')
    generator = SchemaGenerator(patterns=router.urls)
    schema = generator.get_schema(request=None, public=True)
    validate_schema(schema)
Beispiel #30
0
def test_accept_header_versioning(no_warnings, viewset_cls, version, with_request):
    router = routers.SimpleRouter()
    router.register('x', viewset_cls, basename='x')
    generator = SchemaGenerator(
        patterns=[
            path('', include((router.urls, 'x'))),
        ],
        api_version=version,
    )
    if with_request:
        view = SpectacularAPIView(
            versioning_class=AcceptHeaderVersioning,
        )
        factory = APIRequestFactory()
        request = factory.get('x', content_type='application/vnd.oai.openapi+json')
        request = view.initialize_request(request)
    else:
        request = None
    schema = generator.get_schema(request=request, public=True)
    assert_schema(schema, f'tests/test_versioning_accept_{version}.yml')