Add `timer` field data to OpenAPI schema (#474)

* Add timer as an actual field on the duration serializer

This change enables the timer field to be part of the generated OpenAPI
schema.
This commit is contained in:
Christopher Charbonneau Wells 2022-06-06 06:47:57 -07:00 committed by GitHub
parent 5ab2fce0d0
commit 1a19f05130
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 43 deletions

View File

@ -1,29 +0,0 @@
# -*- coding: utf-8 -*-
from collections import OrderedDict
from rest_framework.response import Response
class TimerFieldSupportMixin:
def options(self, request, *args, **kwargs):
"""
Add information about the optional "timer" field.
"""
meta = self.metadata_class()
data = meta.determine_metadata(request, self)
post = data.get("actions").get("POST") # type: OrderedDict
post["timer"] = OrderedDict(
{
"type": "integer",
"required": False,
"read_only": False,
"label": "Timer",
"details": "ID for an existing Timer, may be used in place of the "
"`start`, `end`, and/or `child` fields. ",
}
)
details = "Required unless a value is provided in the `timer` field."
post["child"]["details"] = details
post["start"]["details"] = details
post["end"]["details"] = details
return Response(data)

View File

@ -39,16 +39,30 @@ class CoreModelWithDurationSerializer(CoreModelSerializer):
child = serializers.PrimaryKeyRelatedField( child = serializers.PrimaryKeyRelatedField(
allow_null=True, allow_null=True,
allow_empty=True, help_text="Required unless a Timer value is provided.",
queryset=models.Child.objects.all(), queryset=models.Child.objects.all(),
required=False, required=False,
) )
timer = serializers.PrimaryKeyRelatedField(
allow_null=True,
help_text="May be used in place of the Start, End, and/or Child values.",
queryset=models.Timer.objects.all(),
required=False,
write_only=True,
)
class Meta: class Meta:
abstract = True abstract = True
extra_kwargs = { extra_kwargs = {
"start": {"required": False}, "start": {
"end": {"required": False}, "help_text": "Required unless a Timer value is provided.",
"required": False,
},
"end": {
"help_text": "Required unless a Timer value is provided.",
"required": False,
},
} }
def validate(self, attrs): def validate(self, attrs):
@ -56,11 +70,12 @@ class CoreModelWithDurationSerializer(CoreModelSerializer):
# of "start" and "end" fields as well as "child" if it is set on the # of "start" and "end" fields as well as "child" if it is set on the
# Timer entry. # Timer entry.
timer = None timer = None
if "timer" in self.initial_data: if "timer" in attrs:
try: # Remove the "timer" attribute (super validation would fail as it
timer = models.Timer.objects.get(pk=self.initial_data["timer"]) # is not a true field on the model).
except models.Timer.DoesNotExist: timer = attrs["timer"]
raise ValidationError({"timer": ["Timer does not exist."]}) attrs.pop("timer")
if timer.end: if timer.end:
end = timer.end end = timer.end
else: else:
@ -142,6 +157,7 @@ class FeedingSerializer(CoreModelWithDurationSerializer, TaggableSerializer):
"child", "child",
"start", "start",
"end", "end",
"timer",
"duration", "duration",
"type", "type",
"method", "method",
@ -172,7 +188,17 @@ class NoteSerializer(CoreModelSerializer, TaggableSerializer):
class SleepSerializer(CoreModelWithDurationSerializer, TaggableSerializer): class SleepSerializer(CoreModelWithDurationSerializer, TaggableSerializer):
class Meta(CoreModelWithDurationSerializer.Meta): class Meta(CoreModelWithDurationSerializer.Meta):
model = models.Sleep model = models.Sleep
fields = ("id", "child", "start", "end", "duration", "nap", "notes", "tags") fields = (
"id",
"child",
"start",
"end",
"timer",
"duration",
"nap",
"notes",
"tags",
)
class TagSerializer(serializers.HyperlinkedModelSerializer): class TagSerializer(serializers.HyperlinkedModelSerializer):
@ -220,7 +246,16 @@ class TimerSerializer(CoreModelSerializer):
class TummyTimeSerializer(CoreModelWithDurationSerializer, TaggableSerializer): class TummyTimeSerializer(CoreModelWithDurationSerializer, TaggableSerializer):
class Meta(CoreModelWithDurationSerializer.Meta): class Meta(CoreModelWithDurationSerializer.Meta):
model = models.TummyTime model = models.TummyTime
fields = ("id", "child", "start", "end", "duration", "milestone", "tags") fields = (
"id",
"child",
"start",
"end",
"timer",
"duration",
"milestone",
"tags",
)
class UserSerializer(serializers.ModelSerializer): class UserSerializer(serializers.ModelSerializer):

View File

@ -6,7 +6,6 @@ from rest_framework.response import Response
from core import models from core import models
from . import serializers, filters from . import serializers, filters
from .mixins import TimerFieldSupportMixin
class BMIViewSet(viewsets.ModelViewSet): class BMIViewSet(viewsets.ModelViewSet):
@ -37,7 +36,7 @@ class DiaperChangeViewSet(viewsets.ModelViewSet):
filterset_class = filters.DiaperChangeFilter filterset_class = filters.DiaperChangeFilter
class FeedingViewSet(TimerFieldSupportMixin, viewsets.ModelViewSet): class FeedingViewSet(viewsets.ModelViewSet):
queryset = models.Feeding.objects.all() queryset = models.Feeding.objects.all()
serializer_class = serializers.FeedingSerializer serializer_class = serializers.FeedingSerializer
filterset_class = filters.FeedingFilter filterset_class = filters.FeedingFilter
@ -67,7 +66,7 @@ class PumpingViewSet(viewsets.ModelViewSet):
filterset_class = filters.PumpingFilter filterset_class = filters.PumpingFilter
class SleepViewSet(TimerFieldSupportMixin, viewsets.ModelViewSet): class SleepViewSet(viewsets.ModelViewSet):
queryset = models.Sleep.objects.all() queryset = models.Sleep.objects.all()
serializer_class = serializers.SleepSerializer serializer_class = serializers.SleepSerializer
filterset_class = filters.SleepFilter filterset_class = filters.SleepFilter
@ -104,7 +103,7 @@ class TimerViewSet(viewsets.ModelViewSet):
return Response(self.serializer_class(timer).data) return Response(self.serializer_class(timer).data)
class TummyTimeViewSet(TimerFieldSupportMixin, viewsets.ModelViewSet): class TummyTimeViewSet(viewsets.ModelViewSet):
queryset = models.TummyTime.objects.all() queryset = models.TummyTime.objects.all()
serializer_class = serializers.TummyTimeSerializer serializer_class = serializers.TummyTimeSerializer
filterset_class = filters.TummyTimeFilter filterset_class = filters.TummyTimeFilter

View File

@ -4684,12 +4684,20 @@ components:
child: child:
type: integer type: integer
nullable: true nullable: true
description: Required unless a Timer value is provided.
start: start:
type: string type: string
format: date-time format: date-time
description: Required unless a Timer value is provided.
end: end:
type: string type: string
format: date-time format: date-time
description: Required unless a Timer value is provided.
timer:
type: integer
writeOnly: true
nullable: true
description: May be used in place of the Start, End, and/or Child values.
duration: duration:
type: string type: string
readOnly: true readOnly: true
@ -4809,12 +4817,20 @@ components:
child: child:
type: integer type: integer
nullable: true nullable: true
description: Required unless a Timer value is provided.
start: start:
type: string type: string
format: date-time format: date-time
description: Required unless a Timer value is provided.
end: end:
type: string type: string
format: date-time format: date-time
description: Required unless a Timer value is provided.
timer:
type: integer
writeOnly: true
nullable: true
description: May be used in place of the Start, End, and/or Child values.
duration: duration:
type: string type: string
readOnly: true readOnly: true
@ -4905,12 +4921,20 @@ components:
child: child:
type: integer type: integer
nullable: true nullable: true
description: Required unless a Timer value is provided.
start: start:
type: string type: string
format: date-time format: date-time
description: Required unless a Timer value is provided.
end: end:
type: string type: string
format: date-time format: date-time
description: Required unless a Timer value is provided.
timer:
type: integer
writeOnly: true
nullable: true
description: May be used in place of the Start, End, and/or Child values.
duration: duration:
type: string type: string
readOnly: true readOnly: true