Skip to content

Commit

Permalink
Merge pull request #589 from Dashron/oas-v2
Browse files Browse the repository at this point in the history
OAS Generation for APIV2
  • Loading branch information
augustjohnson authored Oct 23, 2024
2 parents cdb36ba + 4a24ecf commit ed28901
Show file tree
Hide file tree
Showing 26 changed files with 885 additions and 16,779 deletions.
20 changes: 17 additions & 3 deletions .github/workflows/rdme-openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,24 @@ jobs:
rdme-openapi:
runs-on: ubuntu-latest
steps:
- name: Check out repo 📚
uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: install pipenv
run: |
pip install pipenv
- name: install dependencies
run: |
pipenv install
- name: build oas file
run: |
pipenv run python manage.py spectacular --file openapi-schema.yml
- name: Run `openapi` command for v1🚀
uses: readmeio/rdme@v8
with:
rdme: openapi openapi-schema.yml --key=${{ secrets.README_API_KEY }} --id=641f6d9e0ffbcd06c0e7343c
rdme: openapi openapi-schema.yml --key=${{ secrets.README_API_KEY }} --id=6715b7fb7960ee004eb4a8cf
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ server/whoosh_index/
# pyenv env file
.python-version

api/tests/approved_files/*.recieved.*
api/tests/approved_files/*.recieved.*
openapi-schema.yml
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ newrelic = "*"
requests = "*"
whitenoise = "*"
gunicorn = "*"
drf-spectacular = "*"

[dev-packages]
pytest = "*"
Expand Down
691 changes: 475 additions & 216 deletions Pipfile.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ You can use our Dockerfile as inspiration, but it likely will not work without s

After completing a build, you can generate an OAS file to be used by another application.
```bash
pipenv run ./manage.py generateschema --generator_class api.schema_generator.Open5eSchemaGenerator > openapi-schema.yml` to build the OAS file.
pipenv run python manage.py spectacular --color --file openapi-schema.yml` to build the OAS file.
```

# Contributing
Expand Down
17 changes: 0 additions & 17 deletions api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from rest_framework import viewsets
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.schemas.openapi import AutoSchema

from api import models
from api import serializers
Expand Down Expand Up @@ -96,7 +95,6 @@ class DocumentViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of documents.
retrieve: API endpoint for returning a particular document.
"""
schema = AutoSchema(operation_id_base='V1Document')
queryset = models.Document.objects.all().order_by("pk")
serializer_class = serializers.DocumentSerializer
search_fields = ['title', 'desc']
Expand All @@ -113,7 +111,6 @@ class SpellViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of spells.
retrieve: API endpoint for returning a particular spell.
"""
schema = AutoSchema(operation_id_base='V1Spell')
queryset = models.Spell.objects.all().order_by("pk")
filterset_class=filters.SpellFilter
serializer_class = serializers.SpellSerializer
Expand All @@ -140,7 +137,6 @@ class SpellListViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of spell lists.
retrieve: API endpoint for returning a particular spell list.
"""
schema = AutoSchema(operation_id_base='V1SpellList')
queryset = models.SpellList.objects.all().order_by("pk")
serializer_class = serializers.SpellListSerializer
filterset_class = filters.SpellListFilter
Expand All @@ -152,7 +148,6 @@ class MonsterViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of monsters.
retrieve: API endpoint for returning a particular monster.
"""
schema = AutoSchema(operation_id_base='V1Monster')
queryset = models.Monster.objects.all().order_by("pk")
filterset_class = filters.MonsterFilter

Expand All @@ -164,7 +159,6 @@ class BackgroundViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of backgrounds.
retrieve: API endpoint for returning a particular background.
"""
schema = AutoSchema(operation_id_base='V1Background')
queryset = models.Background.objects.all().order_by("pk")
serializer_class = serializers.BackgroundSerializer
ordering_fields = '__all__'
Expand All @@ -178,7 +172,6 @@ class PlaneViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of planes.
retrieve: API endpoint for returning a particular plane.
"""
schema = AutoSchema(operation_id_base='V1Plane')
queryset = models.Plane.objects.all().order_by("pk")
serializer_class = serializers.PlaneSerializer
filterset_class = filters.PlaneFilter
Expand All @@ -190,7 +183,6 @@ class SectionViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of sections.
retrieve: API endpoint for returning a particular section.
"""
schema = AutoSchema(operation_id_base='V1Section')
queryset = models.Section.objects.all().order_by("pk")
serializer_class = serializers.SectionSerializer
ordering_fields = '__all__'
Expand All @@ -204,7 +196,6 @@ class FeatViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of feats.
retrieve: API endpoint for returning a particular feat.
"""
schema = AutoSchema(operation_id_base='V1Feat')
queryset = models.Feat.objects.all().order_by("pk")
serializer_class = serializers.FeatSerializer
filterset_class = filters.FeatFilter
Expand All @@ -216,7 +207,6 @@ class ConditionViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of conditions.
retrieve: API endpoint for returning a particular condition.
"""
schema = AutoSchema(operation_id_base='V1Condition')
queryset = models.Condition.objects.all().order_by("pk")
serializer_class = serializers.ConditionSerializer
search_fields = ['name', 'desc']
Expand All @@ -228,7 +218,6 @@ class RaceViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of races.
retrieve: API endpoint for returning a particular race.
"""
schema = AutoSchema(operation_id_base='V1Race')
queryset = models.Race.objects.all().order_by("pk")
serializer_class = serializers.RaceSerializer
filterset_class = filters.RaceFilter
Expand All @@ -241,7 +230,6 @@ class SubraceViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint that allows viewing of Subraces.
retrieve: API endpoint for returning a particular subrace.
"""
schema = AutoSchema(operation_id_base='V1Subrace')
queryset = models.Subrace.objects.all().order_by("pk")
serializer_class = serializers.SubraceSerializer
search_fields = ['name', 'desc']
Expand All @@ -256,7 +244,6 @@ class CharClassViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of classes and archetypes.
retrieve: API endpoint for returning a particular class or archetype.
"""
schema = AutoSchema(operation_id_base='V1Class')
queryset = models.CharClass.objects.all().order_by("pk")
serializer_class = serializers.CharClassSerializer
filterset_class = filters.CharClassFilter
Expand All @@ -269,7 +256,6 @@ class ArchetypeViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint that allows viewing of Archetypes.
retrieve: API endpoint for returning a particular archetype.
"""
schema = AutoSchema(operation_id_base='V1Archetype')
queryset = models.Archetype.objects.all().order_by("pk")
serializer_class = serializers.ArchetypeSerializer
search_fields = ['name', 'desc']
Expand All @@ -284,7 +270,6 @@ class MagicItemViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of magic items.
retrieve: API endpoint for returning a particular magic item.
"""
schema = AutoSchema(operation_id_base='V1MagicItem')
queryset = models.MagicItem.objects.all().order_by("pk")
serializer_class = serializers.MagicItemSerializer
filterset_class = filters.MagicItemFilter
Expand All @@ -296,7 +281,6 @@ class WeaponViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of weapons.
retrieve: API endpoint for returning a particular weapon.
"""
schema = AutoSchema(operation_id_base='V1Weapon')
queryset = models.Weapon.objects.all().order_by("pk")
serializer_class = serializers.WeaponSerializer
filterset_class = filters.WeaponFilter
Expand All @@ -308,7 +292,6 @@ class ArmorViewSet(viewsets.ReadOnlyModelViewSet):
list: API endpoint for returning a list of armor.
retrieve: API endpoint for returning a particular armor.
"""
schema = AutoSchema(operation_id_base='V1Armor')
queryset = models.Armor.objects.all().order_by("pk")
serializer_class = serializers.ArmorSerializer
filterset_class = filters.ArmorFilter
Expand Down
67 changes: 66 additions & 1 deletion api_v2/models/abstracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
from .enums import SAVING_THROW_MAXIMUM, SAVING_THROW_MINIMUM
from .enums import SKILL_BONUS_MINIMUM, SKILL_BONUS_MAXIMUM
from .enums import PASSIVE_SCORE_MAXIMUM
from drf_spectacular.utils import extend_schema_field, inline_serializer
from drf_spectacular.types import OpenApiTypes
from rest_framework import serializers
import decimal

# FIELDS USED ACROSS MULTIPLE MODELS

Expand Down Expand Up @@ -49,7 +53,7 @@ def distance_field(null=True):
return models.FloatField(
null=null,
blank=True,
validators=[MinValueValidator(0)],
validators=[MinValueValidator(decimal.Decimal(0.0))],
help_text="Used to measure distance."
)

Expand Down Expand Up @@ -140,6 +144,7 @@ class HasPrerequisite(models.Model):
help_text='Prerequisite for the game content item.')

@property
@extend_schema_field(OpenApiTypes.BOOL)
def has_prerequisite(self):
return self.prerequisite not in ("", None)

Expand Down Expand Up @@ -202,6 +207,17 @@ class HasAbilities(models.Model):
ability_score_charisma = ability_score_field(
'Integer representing the charisma ability.')

@extend_schema_field(inline_serializer(
name="ability_scores",
fields={
"strength": serializers.IntegerField(),
"dexterity": serializers.IntegerField(),
"constitution": serializers.IntegerField(),
"intelligence": serializers.IntegerField(),
"wisdom": serializers.IntegerField(),
"charisma": serializers.IntegerField(),
})
)
def get_ability_scores(self):
return {
'strength': self.ability_score_strength,
Expand Down Expand Up @@ -239,6 +255,18 @@ def modifier_wisdom(self):
def modifier_charisma(self):
return ability_modifier(self.ability_score_charisma)

@extend_schema_field(inline_serializer(
name="ability_modifiers",
fields={
# todo: technically they're all typed as any, but they're `floor`'d, so they should come out as integers
"strength": serializers.IntegerField(),
"dexterity": serializers.IntegerField(),
"constitution": serializers.IntegerField(),
"intelligence": serializers.IntegerField(),
"wisdom": serializers.IntegerField(),
"charisma": serializers.IntegerField(),
})
)
def get_modifiers(self):
return {
'strength': self.modifier_strength,
Expand Down Expand Up @@ -272,6 +300,19 @@ def get_modifiers(self):
saving_throw_charisma = saving_throw_field(
'Signed integer added to charisma saving throws.')


@extend_schema_field(inline_serializer(
name="saving_throws",
fields={
# todo: all of these are "or none"
"strength": serializers.IntegerField(),
"dexterity": serializers.IntegerField(),
"constitution": serializers.IntegerField(),
"intelligence": serializers.IntegerField(),
"wisdom": serializers.IntegerField(),
"charisma": serializers.IntegerField(),
})
)
def get_saving_throws(self):
return {
'strength': self.saving_throw_strength,
Expand Down Expand Up @@ -341,6 +382,30 @@ def get_saving_throws(self):
skill_bonus_survival = skill_bonus_field(
'Signed integer added to survival skill checks.')

@extend_schema_field(inline_serializer(
name="skill_bonuses",
fields={
# todo: all of these are typed as also none
'acrobatics': serializers.IntegerField(),
'animal_handling': serializers.IntegerField(),
'arcana': serializers.IntegerField(),
'athletics': serializers.IntegerField(),
'deception': serializers.IntegerField(),
'history': serializers.IntegerField(),
'insight': serializers.IntegerField(),
'intimidation': serializers.IntegerField(),
'investigation': serializers.IntegerField(),
'medicine': serializers.IntegerField(),
'nature': serializers.IntegerField(),
'perception': serializers.IntegerField(),
'performance': serializers.IntegerField(),
'persuasion': serializers.IntegerField(),
'religion': serializers.IntegerField(),
'sleight_of_hand': serializers.IntegerField(),
'stealth': serializers.IntegerField(),
'survival': serializers.IntegerField(),
}
))
def get_skill_bonuses(self):
return {
'acrobatics': self.skill_bonus_acrobatics,
Expand Down
6 changes: 5 additions & 1 deletion api_v2/models/alignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,27 @@

from .abstracts import HasName, HasDescription
from .document import FromDocument

from drf_spectacular.utils import extend_schema_field
from drf_spectacular.types import OpenApiTypes

class Alignment(HasName, HasDescription, FromDocument):
"""This is the model for an alignment, which is way to describe the
moral and personal attitudes of a creature."""

@property
@extend_schema_field(OpenApiTypes.STR)
def short_name(self):
short_name = ""
for word in self.name.split(" "):
short_name += word[0].upper()
return short_name

@property
@extend_schema_field(OpenApiTypes.STR)
def morality(self):
return self.name.split(" ")[-1].lower()

@property
@extend_schema_field(OpenApiTypes.STR)
def societal_attitude(self):
return self.name.split(" ")[0].lower()
4 changes: 4 additions & 0 deletions api_v2/models/armor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from django.db import models
from .abstracts import HasName
from .document import FromDocument
from drf_spectacular.utils import extend_schema_field
from drf_spectacular.types import OpenApiTypes


class Armor(HasName, FromDocument):
Expand Down Expand Up @@ -40,6 +42,7 @@ class Armor(HasName, FromDocument):
help_text='Integer representing the dexterity modifier cap.')

@property
@extend_schema_field(OpenApiTypes.STR)
def category(self):
category = 'heavy'
if self.ac_add_dexmod:
Expand All @@ -50,6 +53,7 @@ def category(self):


@property
@extend_schema_field(OpenApiTypes.STR)
def ac_display(self):
"""Display text for armor class."""
ac_string = str(self.ac_base)
Expand Down
Loading

0 comments on commit ed28901

Please sign in to comment.