Add api customization templates
This commit is contained in:
parent
cc9f42a398
commit
4250ca1a29
24 changed files with 334 additions and 170 deletions
|
|
@ -1,5 +1,8 @@
|
|||
# Evennia API
|
||||
|
||||
This folder contains the code implementing the REST-API of Evennia, based on
|
||||
Django Rest Framework.
|
||||
|
||||
## Synopsis
|
||||
|
||||
An API, or [Application Programming Interface][wiki-api], is a way of establishing rules
|
||||
|
|
@ -18,7 +21,7 @@ can convert into python objects for you, a process called deserialization.
|
|||
When returning a response, it can also convert python objects into JSON
|
||||
strings to send back to a client, which is called serialization. Because it's
|
||||
such a common task to want to handle [CRUD][crud] operations for the django models that you use to represent database
|
||||
objects (such as your Character typeclass, Room typeclass, etc), DRF makes
|
||||
objects (such as your Character typeclass, Room typeclass, etc), DRF makes
|
||||
this process very easy by letting you define [Serializers][serializers]
|
||||
that largely automate the process of serializing your in-game objects into
|
||||
JSON representations for sending them to a client, or for turning a JSON string
|
||||
|
|
@ -54,7 +57,7 @@ user has permission to perform retrieve/update/delete actions upon them.
|
|||
To start with, you can view a synopsis of endpoints by making a GET request
|
||||
to the `yourgame/api/` endpoint by using the excellent [requests library][requests]:
|
||||
|
||||
```pythonstub
|
||||
```python
|
||||
>>> import requests
|
||||
>>> r = requests.get("https://www.mygame.com/api", auth=("user", "pw"))
|
||||
>>> r.json()
|
||||
|
|
@ -131,16 +134,16 @@ object:
|
|||
>>> response = requests.put("https://www.mygame.com/api/objects/214",
|
||||
data=data, auth=("Alsoauser", "Badpassword"))
|
||||
>>> response.json()
|
||||
{"db_key": "An even SHINIER sword", "id": 214, "db_location": 50, ...}
|
||||
{"db_key": "An even SHINIER sword", "id": 214, "db_location": 50, ...}
|
||||
|
||||
```
|
||||
```
|
||||
By making a PUT request to the endpoint that includes the object ID, it becomes
|
||||
a request to update the object with the specified data you pass along.
|
||||
|
||||
In most cases, you won't be making API requests to the backend with python,
|
||||
but with Javascript from your frontend application.
|
||||
There are many Javascript libraries which are meant to make this process
|
||||
easier for requests from the frontend, such as [AXIOS][axios], or using
|
||||
There are many Javascript libraries which are meant to make this process
|
||||
easier for requests from the frontend, such as [AXIOS][axios], or using
|
||||
the native [Fetch][fetch].
|
||||
|
||||
[wiki-api]: https://en.wikipedia.org/wiki/Application_programming_interface
|
||||
|
|
|
|||
|
|
@ -321,6 +321,7 @@ class HelpSerializer(TypeclassSerializerMixin, serializers.ModelSerializer):
|
|||
"id", "db_key", "db_help_category", "db_entrytext", "db_date_created",
|
||||
"tags", "aliases"
|
||||
]
|
||||
read_only_fields = ["id"]
|
||||
|
||||
class HelpListSerializer(TypeclassListSerializerMixin, serializers.ModelSerializer):
|
||||
"""
|
||||
|
|
@ -332,3 +333,4 @@ class HelpListSerializer(TypeclassListSerializerMixin, serializers.ModelSerializ
|
|||
fields = [
|
||||
"id", "db_key", "db_help_category", "db_date_created",
|
||||
]
|
||||
read_only_fields = ["id"]
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class TestEvenniaRESTApi(EvenniaTest):
|
|||
def get_view_details(self, action):
|
||||
"""Helper function for generating list of named tuples"""
|
||||
View = namedtuple(
|
||||
"View", ["view_name", "obj", "list", "serializer", "create_data", "retrieve_data"]
|
||||
"View", ["view_name", "obj", "list", "serializer", "list_serializer", "create_data", "retrieve_data"]
|
||||
)
|
||||
views = [
|
||||
View(
|
||||
|
|
@ -47,6 +47,7 @@ class TestEvenniaRESTApi(EvenniaTest):
|
|||
self.obj1,
|
||||
[self.obj1, self.char1, self.exit, self.room1, self.room2, self.obj2, self.char2],
|
||||
serializers.ObjectDBSerializer,
|
||||
serializers.ObjectListSerializer,
|
||||
{"db_key": "object-create-test-name"},
|
||||
serializers.ObjectDBSerializer(self.obj1).data,
|
||||
),
|
||||
|
|
@ -55,6 +56,7 @@ class TestEvenniaRESTApi(EvenniaTest):
|
|||
self.char1,
|
||||
[self.char1, self.char2],
|
||||
serializers.ObjectDBSerializer,
|
||||
serializers.ObjectListSerializer,
|
||||
{"db_key": "character-create-test-name"},
|
||||
serializers.ObjectDBSerializer(self.char1).data,
|
||||
),
|
||||
|
|
@ -63,6 +65,7 @@ class TestEvenniaRESTApi(EvenniaTest):
|
|||
self.exit,
|
||||
[self.exit],
|
||||
serializers.ObjectDBSerializer,
|
||||
serializers.ObjectListSerializer,
|
||||
{"db_key": "exit-create-test-name"},
|
||||
serializers.ObjectDBSerializer(self.exit).data,
|
||||
),
|
||||
|
|
@ -71,6 +74,7 @@ class TestEvenniaRESTApi(EvenniaTest):
|
|||
self.room1,
|
||||
[self.room1, self.room2],
|
||||
serializers.ObjectDBSerializer,
|
||||
serializers.ObjectListSerializer,
|
||||
{"db_key": "room-create-test-name"},
|
||||
serializers.ObjectDBSerializer(self.room1).data,
|
||||
),
|
||||
|
|
@ -79,6 +83,7 @@ class TestEvenniaRESTApi(EvenniaTest):
|
|||
self.script,
|
||||
[self.script],
|
||||
serializers.ScriptDBSerializer,
|
||||
serializers.ScriptListSerializer,
|
||||
{"db_key": "script-create-test-name"},
|
||||
serializers.ScriptDBSerializer(self.script).data,
|
||||
),
|
||||
|
|
@ -87,6 +92,7 @@ class TestEvenniaRESTApi(EvenniaTest):
|
|||
self.account2,
|
||||
[self.account, self.account2],
|
||||
serializers.AccountSerializer,
|
||||
serializers.AccountListSerializer,
|
||||
{"username": "account-create-test-name"},
|
||||
serializers.AccountSerializer(self.account2).data,
|
||||
),
|
||||
|
|
@ -135,7 +141,7 @@ class TestEvenniaRESTApi(EvenniaTest):
|
|||
response = self.client.get(view_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertCountEqual(
|
||||
response.data["results"], [view.serializer(obj).data for obj in view.list]
|
||||
response.data["results"], [view.list_serializer(obj).data for obj in view.list]
|
||||
)
|
||||
|
||||
def test_create(self):
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ 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")
|
||||
router.register(r"helpentries", views.HelpViewSet, basename="helpentry")
|
||||
|
||||
urlpatterns = router.urls
|
||||
|
||||
|
|
|
|||
|
|
@ -36,8 +36,10 @@ folder and edit it to add/remove links to the menu.
|
|||
{% endif %}
|
||||
|
||||
{% if user.is_staff %}
|
||||
<li><a class="nav-link" href="{% url 'admin:index' %}">Admin</a></li>
|
||||
<li><a class="nav-link" href="/api">API</a></li>
|
||||
<li><a class="nav-link" href="{% url 'admin:index' %}">Admin</a></li>
|
||||
{% if rest_api_enabled %}
|
||||
<li><a class="nav-link" href="/api">API</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@ urlpatterns = [
|
|||
path("webclient/", include("evennia.web.webclient.urls")),
|
||||
# admin
|
||||
path("admin/", include("evennia.web.admin.urls")),
|
||||
# api
|
||||
path("api/", include("evennia.web.api.urls")),
|
||||
# favicon
|
||||
path("favicon.ico", RedirectView.as_view(url="/media/images/favicon.ico", permanent=False)),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"""
|
||||
This file defines global variables that will always be available in a view
|
||||
context without having to repeatedly include it.
|
||||
context without having to repeatedly include it.
|
||||
|
||||
For this to work, this file is included in the settings file, in the
|
||||
TEMPLATE_CONTEXT_PROCESSORS tuple.
|
||||
TEMPLATES["OPTIONS"]["context_processors"] list.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -20,6 +20,7 @@ GAME_ENTITIES = ["Objects", "Scripts", "Comms", "Help"]
|
|||
GAME_SETUP = ["Permissions", "Config"]
|
||||
CONNECTIONS = ["Irc"]
|
||||
WEBSITE = ["Flatpages", "News", "Sites"]
|
||||
REST_API_ENABLED = False
|
||||
|
||||
# Determine the site name and server version
|
||||
def set_game_name_and_slogan():
|
||||
|
|
@ -31,7 +32,7 @@ def set_game_name_and_slogan():
|
|||
This function is used for unit testing the values of the globals.
|
||||
|
||||
"""
|
||||
global GAME_NAME, GAME_SLOGAN, SERVER_VERSION
|
||||
global GAME_NAME, GAME_SLOGAN, SERVER_VERSION, REST_API_ENABLED
|
||||
try:
|
||||
GAME_NAME = settings.SERVERNAME.strip()
|
||||
except AttributeError:
|
||||
|
|
@ -42,6 +43,7 @@ def set_game_name_and_slogan():
|
|||
except AttributeError:
|
||||
GAME_SLOGAN = SERVER_VERSION
|
||||
|
||||
REST_API_ENABLED = settings.REST_API_ENABLED
|
||||
|
||||
def set_webclient_settings():
|
||||
"""
|
||||
|
|
@ -98,4 +100,5 @@ def general_context(request):
|
|||
"websocket_enabled": WEBSOCKET_CLIENT_ENABLED,
|
||||
"websocket_port": WEBSOCKET_PORT,
|
||||
"websocket_url": WEBSOCKET_URL,
|
||||
"rest_api_enabled": REST_API_ENABLED,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,13 +9,12 @@ class TestGeneralContext(TestCase):
|
|||
|
||||
@patch("evennia.web.utils.general_context.GAME_NAME", "test_name")
|
||||
@patch("evennia.web.utils.general_context.GAME_SLOGAN", "test_game_slogan")
|
||||
@patch(
|
||||
"evennia.web.utils.general_context.WEBSOCKET_CLIENT_ENABLED",
|
||||
"websocket_client_enabled_testvalue",
|
||||
)
|
||||
@patch("evennia.web.utils.general_context.WEBSOCKET_CLIENT_ENABLED",
|
||||
"websocket_client_enabled_testvalue")
|
||||
@patch("evennia.web.utils.general_context.WEBCLIENT_ENABLED", "webclient_enabled_testvalue")
|
||||
@patch("evennia.web.utils.general_context.WEBSOCKET_PORT", "websocket_client_port_testvalue")
|
||||
@patch("evennia.web.utils.general_context.WEBSOCKET_URL", "websocket_client_url_testvalue")
|
||||
@patch("evennia.web.utils.general_context.REST_API_ENABLED", True)
|
||||
def test_general_context(self):
|
||||
request = RequestFactory().get("/")
|
||||
request.user = AnonymousUser()
|
||||
|
|
@ -39,6 +38,7 @@ class TestGeneralContext(TestCase):
|
|||
"websocket_enabled": "websocket_client_enabled_testvalue",
|
||||
"websocket_port": "websocket_client_port_testvalue",
|
||||
"websocket_url": "websocket_client_url_testvalue",
|
||||
"rest_api_enabled": True,
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -48,6 +48,7 @@ class TestGeneralContext(TestCase):
|
|||
def test_set_game_name_and_slogan(self, mock_get_version, mock_settings):
|
||||
mock_get_version.return_value = "version 1"
|
||||
# test default/fallback values
|
||||
mock_settings.REST_API_ENABLED = False
|
||||
general_context.set_game_name_and_slogan()
|
||||
self.assertEqual(general_context.GAME_NAME, "Evennia")
|
||||
self.assertEqual(general_context.GAME_SLOGAN, "version 1")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue