Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

input request on schema generator shadows actual required request #370

Closed
sergei-maertens opened this issue Apr 26, 2021 · 10 comments
Closed
Labels
bug Something isn't working fix confirmation pending issue has been fixed and confirmation from issue reporter is pending

Comments

@sergei-maertens
Copy link
Contributor

Note that this is untested on the current master branch, but I believe the same problem exists since the method signature hasn't really changed from 0.12.0 (which I'm on):

def parse(self, request, public):
        """ Iterate endpoints generating per method path operations. """
        result = {}
        self._initialise_endpoints()

        for path, path_regex, method, view in self._get_paths_and_endpoints(None if public else request):
            if not self.has_view_permissions(path, method, view):
                continue

            # mocked request to allow certain operations in get_queryset and get_serializer[_class]
            # without exceptions being raised due to no request.
            if not request:
                request = spectacular_settings.GET_MOCK_REQUEST(method, path, view, request)

            view.request = request

This is causing our generated API spec (interactively) to be wrong, because of the different request serializer class based on HTTP method:

    def get_serializer(self, *args, **kwargs):
        if self.request.method == "PATCH":
            return UpdateZaakDocumentSerializer(*args, **kwargs)
        return AddZaakDocumentSerializer(*args, **kwargs)

When you generate the schema from the CLI, a mock request is present with the correct method, from:

for path, path_regex, method, view in self._get_paths_and_endpoints(None if public else request):

However, if you are using the schema view (hooked up in urls.py), then request in

def parse(self, request, public):

is actually the GET request to /api in our case, i.e. the request that fetches/renders the schema.

So, we have a mismatch here, because of:

            if not request:
                request = spectacular_settings.GET_MOCK_REQUEST(method, path, view, request)

            view.request = request

-> method is PATCH, but request.method is GET

This causes the API schema to pick up the AddZaakDocumentSerializer instead of the UpdateZaakDocumentSerializer. Of course, we can fix this with @extend_schema, but that's a bit of a shame.

Is there a reason we cannot always use the mock request for schema generation?

@sergei-maertens
Copy link
Contributor Author

Looks like #250 might fix this, I'll try it out first

@tfranzel
Copy link
Owner

most recent release should also be fine for testing this, as master does not contain any related changes.

at least it looks like #250 addressed this issue. i believe there are still ways to break it so we may have to still iterate.

@sergei-maertens
Copy link
Contributor Author

Tested on 0.15.1 and this version does not fix it, unfortunately :( it is using the input_request instead.

@sergei-maertens
Copy link
Contributor Author

sergei-maertens commented Apr 26, 2021

It's a tricky one, since you want the input request with the actual authenticated user if you only want to display API documentation they have access to.

Perhaps you could grab the input_request, clone it, and alter the relevant attributes: method and path?

def build_mock_request(method, path, view, original_request, **kwargs): does seem to be prepared for this, with the (unused) original_request parameter.

Overriding that setting doesn't work as a workaround, because the mock request function is only called if there's no input_request.

So I propose:

  1. Always call the mock request factory, people with different needs can already swap this out for another callable
  2. Change the current implementation of the mock request factory to essentially clone the input request if available, and alter method + path, and use the current behaviour if there's no input_request

I'd be fine with just 1. as well, since then I can just provide my own callable

@tfranzel
Copy link
Owner

haha, wishful thinking didn't help. 😄

Perhaps you could grab the input_request, clone it, and alter the relevant attributes: method and path?

the solution is most likely something to that effect. i'll also look into it.

fyi: you should update in any case as we had dozens of fixes/features since 0.12.0

@sergei-maertens
Copy link
Contributor Author

I've edited by previous comment, but it seems that we're roughly on the same line :)

fyi: you should update in any case as we had dozens of fixes/features since 0.12.0

done that just now, and it went pretty smooth thanks to swagger_fake_view :)

sergei-maertens added a commit to GemeenteUtrecht/zaakafhandelcomponent that referenced this issue Apr 26, 2021
This is a bug in drf-spectacular, issue created on github:
tfranzel/drf-spectacular#370
@tfranzel tfranzel added the bug Something isn't working label Apr 26, 2021
tfranzel added a commit that referenced this issue May 2, 2021
we now always generate a mocked request, not just when schema is
genenerated over CLI. use incoming request as reference if available.
@tfranzel tfranzel added the fix confirmation pending issue has been fixed and confirmation from issue reporter is pending label May 2, 2021
@tfranzel
Copy link
Owner

tfranzel commented May 2, 2021

@sergei-maertens the change was a bit more involved, but overall reduced complexity imho. this should now behave exactly as one would supect.

  • GET_MOCK_REQUEST is now always called no matter what.
  • most headers are carried over
  • authenticated user is carried over

please check if that works for you.

@tfranzel
Copy link
Owner

tfranzel commented May 6, 2021

@sergei-maertens did you have time to give this a try? i would like to have someone else check this before i do a release.

@sergei-maertens
Copy link
Contributor Author

I haven't had time yet! Maybe next week I can try it out, but no guarantees since I'm involved with other projects atm :P

@sergei-maertens
Copy link
Contributor Author

@tfranzel just checked out 0.16.0 locally and it appears to work as intended! I'll create a new issue if I spot any new bugs :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fix confirmation pending issue has been fixed and confirmation from issue reporter is pending
Projects
None yet
Development

No branches or pull requests

2 participants