Add helpentry api

This commit is contained in:
Griatch 2021-05-23 15:16:55 +02:00
parent 8b834a6016
commit 231deab392
5 changed files with 199 additions and 44 deletions

View file

@ -5,6 +5,7 @@ that is retrieved in GET requests. By default, Django Rest Framework uses the
documentation specifically regarding DRF integration.
https://django-filter.readthedocs.io/en/latest/guide/rest_framework.html
"""
from typing import Union
from django.db.models import Q
@ -25,6 +26,7 @@ def get_tag_query(tag_type: Union[str, None], key: str) -> Q:
Returns:
A Q object that for searching by this tag type and name
"""
return Q(db_tags__db_tagtype=tag_type) & Q(db_tags__db_key__iexact=key)
@ -32,6 +34,7 @@ def get_tag_query(tag_type: Union[str, None], key: str) -> Q:
class TagTypeFilter(CharFilter):
"""
This class lets you create different filters for tags of a specified db_tagtype.
"""
tag_type = None
@ -60,11 +63,14 @@ SHARED_FIELDS = ["db_key", "db_typeclass_path", "db_tags__db_key", "db_tags__db_
class BaseTypeclassFilterSet(FilterSet):
"""A parent class with filters for aliases and permissions"""
"""
A parent class with filters for aliases and permissions
"""
name = CharFilter(lookup_expr="iexact", method="filter_name", field_name="db_key")
alias = AliasFilter(lookup_expr="iexact")
permission = PermissionFilter(lookup_expr="iexact")
name = CharFilter(lookup_expr="iexact", method="filter_name", field_name="db_key")
@staticmethod
def filter_name(queryset, name, value):
@ -84,7 +90,10 @@ class BaseTypeclassFilterSet(FilterSet):
class ObjectDBFilterSet(BaseTypeclassFilterSet):
"""This adds filters for ObjectDB instances - characters, rooms, exits, etc"""
"""
This adds filters for ObjectDB instances - characters, rooms, exits, etc
"""
class Meta:
model = ObjectDB
@ -103,7 +112,9 @@ class AccountDBFilterSet(BaseTypeclassFilterSet):
class Meta:
model = AccountDB
fields = SHARED_FIELDS + ["username", "db_is_connected", "db_is_bot"]
fields = [fi for fi in (SHARED_FIELDS + ["username", "db_is_connected", "db_is_bot"])
if fi != 'db_key']
class ScriptDBFilterSet(BaseTypeclassFilterSet):
@ -121,3 +132,16 @@ class ScriptDBFilterSet(BaseTypeclassFilterSet):
"db_persistent",
"db_interval",
]
class HelpFilterSet(FilterSet):
"""
Filter for help entries
"""
name = CharFilter(lookup_expr="iexact", method="filter_name", field_name="db_key")
category = CharFilter(lookup_expr="iexact", method="filter_name", field_name="db_category")
alias = AliasFilter(lookup_expr="iexact")

View file

@ -7,6 +7,7 @@ those decisions in the hands of clients, and are more focused on converting
data from the server to JSON (serialization) for a response, and validating
and converting JSON data sent from clients to our enpoints into python objects,
often django model instances, that we can use (deserialization).
"""
from rest_framework import serializers
@ -16,9 +17,14 @@ from evennia.accounts.accounts import DefaultAccount
from evennia.scripts.models import ScriptDB
from evennia.typeclasses.attributes import Attribute
from evennia.typeclasses.tags import Tag
from evennia.help.models import HelpEntry
class AttributeSerializer(serializers.ModelSerializer):
"""
Serialize Attribute views.
"""
value_display = serializers.SerializerMethodField(source="value")
db_value = serializers.CharField(write_only=True, required=False)
@ -35,6 +41,7 @@ class AttributeSerializer(serializers.ModelSerializer):
Returns:
The Attribute's value in string format
"""
if obj.db_strvalue:
return obj.db_strvalue
@ -53,13 +60,17 @@ class SimpleObjectDBSerializer(serializers.ModelSerializer):
fields = ["id", "db_key"]
class TypeclassSerializerMixin(object):
"""Mixin that contains types shared by typeclasses. A note about tags, aliases, and permissions. You
might note that the methods and fields are defined here, but they're included explicitly in each child
class. What gives? It's a DRF error: serializer method fields which are inherited do not resolve correctly
in child classes, and as of this current version (3.11) you must have them in the child classes explicitly
to avoid field errors. Similarly, the child classes must contain the attribute serializer explicitly to
not have them render PK-related fields.
class TypeclassSerializerMixin:
"""
Mixin that contains types shared by typeclasses. A note about tags,
aliases, and permissions. You might note that the methods and fields are
defined here, but they're included explicitly in each child class. What
gives? It's a DRF error: serializer method fields which are inherited do
not resolve correctly in child classes, and as of this current version
(3.11) you must have them in the child classes explicitly to avoid field
errors. Similarly, the child classes must contain the attribute serializer
explicitly to not have them render PK-related fields.
"""
shared_fields = [
@ -135,7 +146,24 @@ class TypeclassSerializerMixin(object):
return AttributeSerializer(obj.nicks.all(), many=True).data
class TypeclassListSerializerMixin:
"""
Shortened serializer for list views.
"""
shared_fields = [
"id",
"db_key",
"db_typeclass_path",
]
class ObjectDBSerializer(TypeclassSerializerMixin, serializers.ModelSerializer):
"""
Serializing Objects.
"""
attributes = serializers.SerializerMethodField()
nicks = serializers.SerializerMethodField()
contents = serializers.SerializerMethodField()
@ -182,8 +210,25 @@ class ObjectDBSerializer(TypeclassSerializerMixin, serializers.ModelSerializer):
return SimpleObjectDBSerializer(non_exits, many=True).data
class ObjectListSerializer(TypeclassListSerializerMixin, serializers.ModelSerializer):
"""
Shortened representation for listings.]
"""
class Meta:
model = DefaultObject
fields = [
"db_location",
"db_home",
] + TypeclassListSerializerMixin.shared_fields
read_only_fields = ["id"]
class AccountSerializer(TypeclassSerializerMixin, serializers.ModelSerializer):
"""This uses the DefaultAccount object to have access to the sessions property"""
"""
This uses the DefaultAccount object to have access to the sessions property
"""
attributes = serializers.SerializerMethodField()
nicks = serializers.SerializerMethodField()
@ -211,7 +256,23 @@ class AccountSerializer(TypeclassSerializerMixin, serializers.ModelSerializer):
read_only_fields = ["id"]
class AccountListSerializer(TypeclassListSerializerMixin, serializers.ModelSerializer):
"""
A shortened form for listing.
"""
class Meta:
model = DefaultAccount
fields = ["username"] + [
fi for fi in TypeclassListSerializerMixin.shared_fields if fi != "db_key"]
read_only_fields = ["id"]
class ScriptDBSerializer(TypeclassSerializerMixin, serializers.ModelSerializer):
"""
Serializing Account.
"""
attributes = serializers.SerializerMethodField()
tags = serializers.SerializerMethodField()
aliases = serializers.SerializerMethodField()
@ -227,3 +288,47 @@ class ScriptDBSerializer(TypeclassSerializerMixin, serializers.ModelSerializer):
"db_repeats",
] + TypeclassSerializerMixin.shared_fields
read_only_fields = ["id"]
class ScriptListSerializer(TypeclassListSerializerMixin, serializers.ModelSerializer):
"""
Shortened form for listing.
"""
class Meta:
model = ScriptDB
fields = [
"db_interval",
"db_persistent",
"db_start_delay",
"db_is_active",
"db_repeats",
] + TypeclassListSerializerMixin.shared_fields
read_only_fields = ["id"]
class HelpSerializer(TypeclassSerializerMixin, serializers.ModelSerializer):
"""
Serializers Help entries (not a typeclass).
"""
tags = serializers.SerializerMethodField()
aliases = serializers.SerializerMethodField()
class Meta:
model = HelpEntry
fields = [
"id", "db_key", "db_help_category", "db_entrytext", "db_date_created",
"tags", "aliases"
]
class HelpListSerializer(TypeclassListSerializerMixin, serializers.ModelSerializer):
"""
Shortened form for listings.
"""
class Meta:
model = HelpEntry
fields = [
"id", "db_key", "db_help_category", "db_date_created",
]

View file

@ -1,4 +1,7 @@
"""Tests for the REST API"""
"""
Tests for the REST API.
"""
from evennia.utils.test_resources import EvenniaTest
from evennia.web.api import serializers
from rest_framework.test import APIClient

View file

@ -22,25 +22,19 @@ from django.views.generic import TemplateView
from rest_framework.schemas import get_schema_view
from evennia.web.api.root import APIRootRouter
from evennia.web.api.views import (
ObjectDBViewSet,
AccountDBViewSet,
CharacterViewSet,
ExitViewSet,
RoomViewSet,
ScriptDBViewSet,
)
from evennia.web.api import views
app_name = "api"
router = APIRootRouter()
router.trailing_slash = "/?"
router.register(r"accounts", AccountDBViewSet, basename="account")
router.register(r"objects", ObjectDBViewSet, basename="object")
router.register(r"characters", CharacterViewSet, basename="character")
router.register(r"exits", ExitViewSet, basename="exit")
router.register(r"rooms", RoomViewSet, basename="room")
router.register(r"scripts", ScriptDBViewSet, basename="script")
router.register(r"accounts", views.AccountDBViewSet, basename="account")
router.register(r"objects", views.ObjectDBViewSet, basename="object")
router.register(r"characters", views.CharacterViewSet, basename="character")
router.register(r"exits", views.ExitViewSet, basename="exit")
router.register(r"rooms", views.RoomViewSet, basename="room")
router.register(r"scripts", views.ScriptDBViewSet, basename="script")
router.register(r"helpentries", views.HelpViewSet, basename="script")
urlpatterns = router.urls

View file

@ -15,17 +15,29 @@ from evennia.objects.models import ObjectDB
from evennia.objects.objects import DefaultCharacter, DefaultExit, DefaultRoom
from evennia.accounts.models import AccountDB
from evennia.scripts.models import ScriptDB
from evennia.web.api.serializers import (
ObjectDBSerializer,
AccountSerializer,
ScriptDBSerializer,
AttributeSerializer,
)
from evennia.web.api.filters import ObjectDBFilterSet, AccountDBFilterSet, ScriptDBFilterSet
from evennia.help.models import HelpEntry
from evennia.web.api import serializers
from evennia.web.api import filters
from evennia.web.api.permissions import EvenniaPermission
class TypeclassViewSetMixin:
class GeneralViewSetMixin:
"""
Mixin for both typeclass- and non-typeclass entities.
"""
def get_serializer_class(self):
"""
Allow different serializers for certain actions.
"""
if self.action == 'list':
if hasattr(self, "list_serializer_class"):
return self.list_serializer_class
return self.serializer_class
class TypeclassViewSetMixin(GeneralViewSetMixin):
"""
This mixin adds some shared functionality to each viewset of a typeclass. They all use the same
permission classes and filter backend. You can override any of these in your own viewsets.
@ -53,7 +65,7 @@ class TypeclassViewSetMixin:
it if no db_value is provided.
"""
attr = AttributeSerializer(data=request.data)
attr = serializers.AttributeSerializer(data=request.data)
obj = self.get_object()
if attr.is_valid(raise_exception=True):
key = attr.validated_data["db_key"]
@ -69,7 +81,7 @@ class TypeclassViewSetMixin:
else:
handler.remove(key=key, category=category)
return Response(
AttributeSerializer(obj.db_attributes.all(), many=True).data,
serializers.AttributeSerializer(obj.db_attributes.all(), many=True).data,
status=status.HTTP_200_OK,
)
return Response(attr.errors, status=status.HTTP_400_BAD_REQUEST)
@ -86,9 +98,10 @@ class ObjectDBViewSet(TypeclassViewSetMixin, ModelViewSet):
# instances. Serializers are similar to django forms, used for the
# transmitting of data (typically json).
serializer_class = ObjectDBSerializer
serializer_class = serializers.ObjectDBSerializer
queryset = ObjectDB.objects.all()
filterset_class = ObjectDBFilterSet
filterset_class = filters.ObjectDBFilterSet
list_serializer_class = serializers.ObjectListSerializer
class CharacterViewSet(ObjectDBViewSet):
@ -96,10 +109,10 @@ class CharacterViewSet(ObjectDBViewSet):
Characters are a type of Object commonly used as player avatars in-game.
"""
queryset = DefaultCharacter.objects.typeclass_search(
DefaultCharacter.path, include_children=True
)
list_serializer_class = serializers.ObjectListSerializer
class RoomViewSet(ObjectDBViewSet):
@ -109,6 +122,7 @@ class RoomViewSet(ObjectDBViewSet):
"""
queryset = DefaultRoom.objects.typeclass_search(DefaultRoom.path, include_children=True)
list_serializer_class = serializers.ObjectListSerializer
class ExitViewSet(ObjectDBViewSet):
@ -119,6 +133,7 @@ class ExitViewSet(ObjectDBViewSet):
"""
queryset = DefaultExit.objects.typeclass_search(DefaultExit.path, include_children=True)
list_serializer_class = serializers.ObjectListSerializer
class AccountDBViewSet(TypeclassViewSetMixin, ModelViewSet):
@ -127,9 +142,10 @@ class AccountDBViewSet(TypeclassViewSetMixin, ModelViewSet):
"""
serializer_class = AccountSerializer
serializer_class = serializers.AccountSerializer
queryset = AccountDB.objects.all()
filterset_class = AccountDBFilterSet
filterset_class = filters.AccountDBFilterSet
list_serializer_class = serializers.AccountListSerializer
class ScriptDBViewSet(TypeclassViewSetMixin, ModelViewSet):
@ -139,6 +155,19 @@ class ScriptDBViewSet(TypeclassViewSetMixin, ModelViewSet):
"""
serializer_class = ScriptDBSerializer
serializer_class = serializers.ScriptDBSerializer
queryset = ScriptDB.objects.all()
filterset_class = ScriptDBFilterSet
filterset_class = filters.ScriptDBFilterSet
list_serializer_class = serializers.ScriptListSerializer
class HelpViewSet(GeneralViewSetMixin, ModelViewSet):
"""
Database-stored help entries.
Note that command auto-help and file-based help entries are not accessible this way.
"""
serializer_class = serializers.HelpSerializer
queryset = HelpEntry.objects.all()
filterset_class = filters.HelpFilterSet
list_serializer_class = serializers.HelpListSerializer