-
Notifications
You must be signed in to change notification settings - Fork 263
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
Webhooks with serializer's GET
specification
#1280
Comments
Hi, think I understood your post but I don't really see any problems yet.
Not sure yet where exactly the problem is since this does what was asked for, right? Why would you need another Have you read the docstring for OpenApiWebhook? drf-spectacular/drf_spectacular/utils.py Lines 324 to 327 in f741899
|
Hi @tfranzel , I was aware of that documentation and I hope I've been understanding that correctly, because I believe it describes my problem precisely. So, let's say I have an endpoint where the user can get some data, and also want to provide the user with a webhook that sends exactly that data when the data is created. What I want to send via the serializer is exactly the same payload the user can get from the GET endpoint, which is the response schema of that serializer. But by having to set it up in the request portion of the webhook schema (which makes total sense, I'm not saying this is wrong), the user gets the request schema of the serializer, which is different from the response. Let me set a (hopefully) more clear example. Let's say I have this set up: from rest_framework import serializers
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiExample
from drf_spectacular.types import OpenApiTypes
class NestedSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField()
internal_code = serializers.CharField(write_only=True)
class ComplexSerializer(serializers.Serializer):
title = serializers.CharField()
description = serializers.CharField(required=False)
created_at = serializers.DateTimeField(read_only=True)
nested_data = NestedSerializer(many=True)
class MyAPIView(APIView):
@extend_schema(
request=ComplexSerializer,
responses={200: ComplexSerializer}
)
def post(self, request):
# Logic for POST
pass
@extend_schema(
responses={200: ComplexSerializer}
)
def get(self, request):
# Logic for GET
pass
# Webhook definition
my_webhook= OpenApiWebhook(
name="My Webhook",
decorator=extend_schema(
description="Webhook for new data",
request=ComplexSerializer, # This is where the problem occurs,
responses={200: None},
),
) These will be the results of the GET endpoint schema vs the webhook schema. As you can see there are differences because the serializer is different from a request perspective than from a response perspective:
Here's a specific breakdown of the above:
So this is what I mean that, for me to properly document what the user receives in the webhook, I would need create another version of all the serializers, where I modify (or flip) the read only and write only fields just to be able to document this webhook properly. Expected outcome:What I want is to keep this request definition, as it makes total sense to define the webhook as a POST request, but that somehow Hopefully I've made a bit more sense this time. Please also let me know if this makes sense at all or if I'm completely oblivious to something important. |
Thank you @dontic for the thorough explanation. I have not looked at this in a while so I had to refresh my memory. Just from my memory I would have thought I had the same understanding as you before. However, it turns out that SwaggerUI at least sees this exactly inverted. Also, if you look at the schema, you can see that we actually don't do that much in the webhook. We simply reuse the component from the view. Only then does SwaggerUI decide to present the I just checked and webhooks:
Foo:
post:
description: Foo
request.
summary: A complex object.
tags:
- webhooks
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Complex'
required: true
responses:
'200':
content:
application/json:
schema:
type: string
description: '' As you pointed out, one might create a "mirror" copy of the serializer (flip There is also a chance that SwaggerUI just went with the interpretation of the person who implemented the feature. Might be worth searching in their issue list for clues. Pretty sure someone else stumbled over that before. Let me know what you think. |
@tfranzel you are totally right. I failed to question and check if this is inherited from OpenAPI or SwaggerUI. Thanks for the thorough explanation. Indeed, it seems that this issue was brought up to the OpenAPI people in: Where they state they actually had a long thought on this issue when implementing webhooks. In the end, it seems that tools such as SwaggerUI are the ones responsible to interprete how the webhook payload is displayed. For that reason, I opened an issue for SwaggerUI since I couldn't find anything describing the same exact behavior: In the meantime the only decent workarounds I found:
Feel free to close this issue as it's not relevant to |
Thanks @dontic for doing the research and opening the issue.
Strongly disagree with that. The spec should say how this is to be understood. This is not some literature class where everybody can have a different interpretation. There needs to be a common understanding on what happens. For normal requests/responses, nobody asks this question because it is so obvious how it is to be read. This is simply not the case for webhooks/callbacks, where people see this differently depending on where their mental model places the origin. 3 UIs - 2 interpretations.
I tend to agree with your perspective and Redoc for that matter. I'll close this as it is an upstream issue on which we have no control here. |
Thanks for expanding on that! I also do think this should be clarified upstream, in my opinion it's similar to providing a dictionary and not providing the grammar rules on how to put the words together to form a sentence, so then everyone makes their own language in the end. This motivates me to put a comment in that issue from OpenAPI. |
Here's my situation:
I have a specific serializer that I only use in a read-only
GET
endpoint. But I also have implemented a functionality that sends this data through a webhook when generated.This serializer has another 3 nested serializers which also have read-only fields, the logic is quite complex.
As a simple example lets say I have these 2 serializers:
This is the schema I would have in my GET endpoint for a specific dog:
And this is the data that I would also want to send to the webhook.
But the webhook implementation requires me to define the serializer in a "request" or
POST
"mode":This results in the webhook
POST
request body definition being:I get that I could define a different serializer or schema just for my webhook, in this case it would be simple, the problem is that in my specific use case it would be quite a hassle.
This would also mean code repetition in most cases.
Would there be a way to implement some context to give specifically to webhook endpoints so we can use the "read" or
GET
definitions of other serializers?The text was updated successfully, but these errors were encountered: