Skip to content

Commit

Permalink
feat: add /openapi (OAS schema) and schema docs (#17)
Browse files Browse the repository at this point in the history
This will make the REST API self describing
  • Loading branch information
ngurenyaga committed Nov 9, 2021
1 parent 550d3b6 commit 1b7e0bf
Show file tree
Hide file tree
Showing 12 changed files with 275 additions and 9 deletions.
14 changes: 14 additions & 0 deletions config/api_router.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
from django.conf import settings
from rest_framework.routers import DefaultRouter, SimpleRouter

from mycarehub.clients.views import (
ClientFacilityViewSet,
ClientViewSet,
IdentifierViewSet,
RelatedPersonViewSet,
SecurityQuestionResponseViewSet,
SecurityQuestionViewSet,
)
from mycarehub.common.views import FacilityViewSet, UserFacilityViewSet
from mycarehub.users.api.views import UserViewSet

Expand All @@ -12,6 +20,12 @@
router.register("users", UserViewSet)
router.register("facilities", FacilityViewSet)
router.register("user_facilities", UserFacilityViewSet)
router.register("identifiers", IdentifierViewSet)
router.register("security_questions", SecurityQuestionViewSet)
router.register("security_question_responses", SecurityQuestionResponseViewSet)
router.register("related_persons", RelatedPersonViewSet)
router.register("clients", ClientViewSet)
router.register("client_facilities", ClientFacilityViewSet)

app_name = "api"
urlpatterns = router.urls
1 change: 1 addition & 0 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"rest_framework",
"rest_framework.authtoken",
"rest_framework_datatables",
"drf_generators",
"corsheaders",
"mjml",
"oauth2_provider",
Expand Down
34 changes: 34 additions & 0 deletions mycarehub/clients/migrations/0002_auto_20211109_0922.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 3.2.9 on 2021-11-09 06:22

from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

dependencies = [
('clients', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='client',
name='enrollment_date',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='clientfacility',
name='assigned',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='identifier',
name='valid_from',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='securityquestionresponse',
name='timestamp',
field=models.DateTimeField(default=django.utils.timezone.now),
),
]
9 changes: 5 additions & 4 deletions mycarehub/clients/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.db.models.enums import TextChoices
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from mycarehub.common.models import AbstractBase
Expand Down Expand Up @@ -30,7 +31,7 @@ class IdentifierUse(models.TextChoices):
choices=IdentifierUse.choices, max_length=64, null=False, blank=False
)
description = models.TextField()
valid_from = models.DateTimeField(auto_now_add=True)
valid_from = models.DateTimeField(default=timezone.now)
valid_to = models.DateTimeField(null=True, blank=True)
is_primary_identifier = models.BooleanField(default=False)

Expand All @@ -51,7 +52,7 @@ class ResponseType(models.TextChoices):
class SecurityQuestionResponse(AbstractBase):
user = models.ForeignKey(get_user_model(), on_delete=models.PROTECT)
question = models.ForeignKey(SecurityQuestion, on_delete=models.PROTECT)
timestamp = models.DateTimeField(auto_now_add=True)
timestamp = models.DateTimeField(default=timezone.now)
response = models.TextField() # should be hashed

class Meta:
Expand Down Expand Up @@ -157,7 +158,7 @@ class Languages(TextChoices):

# a client must have an enrollment date.
# if it is not known, integration code should set a sensible default.
enrollment_date = models.DateTimeField(null=False, blank=False, auto_now_add=True)
enrollment_date = models.DateTimeField(null=False, blank=False, default=timezone.now)

# the client's FHIR health record ID
# optional because the client's FHIR health record is created after the client is enrolled
Expand Down Expand Up @@ -214,5 +215,5 @@ class ClientFacility(AbstractBase):

client = models.ForeignKey(Client, on_delete=models.PROTECT)
facility = models.ForeignKey(Facility, on_delete=models.PROTECT)
assigned = models.DateTimeField(auto_now_add=False)
assigned = models.DateTimeField(default=timezone.now)
transferred_out = models.DateTimeField(null=True, blank=True)
46 changes: 46 additions & 0 deletions mycarehub/clients/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from rest_framework.serializers import ModelSerializer

from mycarehub.clients.models import (
Client,
ClientFacility,
Identifier,
RelatedPerson,
SecurityQuestion,
SecurityQuestionResponse,
)


class IdentifierSerializer(ModelSerializer):
class Meta:
model = Identifier
fields = "__all__"


class SecurityQuestionSerializer(ModelSerializer):
class Meta:
model = SecurityQuestion
fields = "__all__"


class SecurityQuestionResponseSerializer(ModelSerializer):
class Meta:
model = SecurityQuestionResponse
fields = "__all__"


class RelatedPersonSerializer(ModelSerializer):
class Meta:
model = RelatedPerson
fields = "__all__"


class ClientSerializer(ModelSerializer):
class Meta:
model = Client
fields = "__all__"


class ClientFacilitySerializer(ModelSerializer):
class Meta:
model = ClientFacility
fields = "__all__"
67 changes: 67 additions & 0 deletions mycarehub/clients/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from django.urls import reverse
from faker import Faker
from model_bakery import baker
from rest_framework.test import APITestCase

from mycarehub.common.tests.test_api import LoggedInMixin

from .models import Identifier

fake = Faker()


class IdentifierViewsetTest(LoggedInMixin, APITestCase):
def setUp(self):
self.url_list = reverse("api:identifier-list")
self.comparison_field = "identifier_value"
self.url_detail_base = "api:identifier-detail"
self.instance = baker.make(
Identifier,
identifier_type="NATIONAL_ID",
identifier_value=fake.ssn(),
identifier_use="OFFICIAL",
description=fake.text(),
is_primary_identifier=True,
organisation=self.global_organisation,
)
self.data = {
"identifier_type": "NATIONAL_ID",
"identifier_value": fake.ssn(),
"identifier_use": "OFFICIAL",
"description": fake.text(),
"is_primary_identifier": True,
"organisation": self.global_organisation.pk,
}
self.detail_url = reverse(self.url_detail_base, kwargs={"pk": self.instance.pk})
super().setUp()

def test_create(self):
response = self.client.post(self.url_list, self.data)
assert response.status_code == 201, response.json()
assert response.data[self.comparison_field] == self.data[self.comparison_field]

def test_list(self):
response = self.client.get(self.url_list)
assert response.status_code == 200, response.json()
assert response.data["count"] >= 1, response.json()

values = [a[self.comparison_field] for a in response.data["results"]]
assert getattr(self.instance, self.comparison_field) in values

def test_retrieve(self):
response = self.client.get(self.detail_url)
assert response.status_code == 200, response.json()
assert response.data[self.comparison_field] == getattr(
self.instance, self.comparison_field
)

def test_patch(self):
patch = {self.comparison_field: fake.ssn()}
response = self.client.patch(self.detail_url, patch)
assert response.status_code == 200, response.json()
assert response.data[self.comparison_field] == patch[self.comparison_field]

def test_put(self):
response = self.client.put(self.detail_url, self.data)
assert response.status_code == 200, response.json()
assert response.data[self.comparison_field] == self.data[self.comparison_field]
48 changes: 48 additions & 0 deletions mycarehub/clients/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from rest_framework.viewsets import ModelViewSet

from mycarehub.clients.models import (
Client,
ClientFacility,
Identifier,
RelatedPerson,
SecurityQuestion,
SecurityQuestionResponse,
)
from mycarehub.clients.serializers import (
ClientFacilitySerializer,
ClientSerializer,
IdentifierSerializer,
RelatedPersonSerializer,
SecurityQuestionResponseSerializer,
SecurityQuestionSerializer,
)


class IdentifierViewSet(ModelViewSet):
queryset = Identifier.objects.order_by("pk")
serializer_class = IdentifierSerializer


class SecurityQuestionViewSet(ModelViewSet):
queryset = SecurityQuestion.objects.order_by("pk")
serializer_class = SecurityQuestionSerializer


class SecurityQuestionResponseViewSet(ModelViewSet):
queryset = SecurityQuestionResponse.objects.order_by("pk")
serializer_class = SecurityQuestionResponseSerializer


class RelatedPersonViewSet(ModelViewSet):
queryset = RelatedPerson.objects.order_by("pk")
serializer_class = RelatedPersonSerializer


class ClientViewSet(ModelViewSet):
queryset = Client.objects.order_by("pk")
serializer_class = ClientSerializer


class ClientFacilityViewSet(ModelViewSet):
queryset = ClientFacility.objects.order_by("pk")
serializer_class = ClientFacilitySerializer
19 changes: 19 additions & 0 deletions mycarehub/common/migrations/0004_alter_auditlog_timestamp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 3.2.9 on 2021-11-09 06:22

from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

dependencies = [
('common', '0003_auto_20211108_2023'),
]

operations = [
migrations.AlterField(
model_name='auditlog',
name='timestamp',
field=models.DateTimeField(default=django.utils.timezone.now),
),
]
3 changes: 2 additions & 1 deletion mycarehub/common/models/common_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django.db.models import Q
from django.db.models.fields.json import JSONField
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from ..constants import COUNTRY_CODES, WHITELIST_COUNTIES
Expand Down Expand Up @@ -498,7 +499,7 @@ class AuditLog(AbstractBase):
fraud?
"""

timestamp = models.DateTimeField(auto_now_add=True)
timestamp = models.DateTimeField(default=timezone.now)
record_type = models.TextField()
notes = models.TextField()
payload = JSONField()
34 changes: 34 additions & 0 deletions mycarehub/users/migrations/0003_auto_20211109_0922.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 3.2.9 on 2021-11-09 06:22

from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

dependencies = [
('users', '0002_auto_20211108_2027'),
]

operations = [
migrations.AlterField(
model_name='metric',
name='timestamp',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='termsofservice',
name='valid_from',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='userpin',
name='valid_from',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='userpin',
name='valid_to',
field=models.DateTimeField(default=django.utils.timezone.now),
),
]
8 changes: 4 additions & 4 deletions mycarehub/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def default_organisation():

class TermsOfService(Model):
text = TextField()
valid_from = DateTimeField(auto_now_add=True)
valid_from = DateTimeField(default=timezone.now)
valid_to = DateTimeField(null=True, blank=True)
active = BooleanField(default=True)
created = DateTimeField(default=timezone.now)
Expand Down Expand Up @@ -163,8 +163,8 @@ class UserPIN(Model):

user = ForeignKey(User, on_delete=PROTECT)
hashed_pin = TextField()
valid_from = DateTimeField(auto_now_add=True)
valid_to = DateTimeField(auto_now_add=True)
valid_from = DateTimeField(default=timezone.now)
valid_to = DateTimeField(default=timezone.now)
user_type = CharField(choices=UserTypes.choices, max_length=32, null=True, blank=True)
active = BooleanField(default=True)
created = DateTimeField(default=timezone.now)
Expand All @@ -185,7 +185,7 @@ class MetricType(TextChoices):
CONTENT = "CONTENT", _("Content Metrics")
SYSTEM = "SYSTEM", _("System Metrics")

timestamp = DateTimeField(auto_now_add=True)
timestamp = DateTimeField(default=timezone.now)
payload = JSONField()
user = ForeignKey(User, on_delete=PROTECT)
metric_type = CharField(choices=MetricType.choices, max_length=32)
Expand Down
1 change: 1 addition & 0 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ django-phonenumber-field~=5.2.0
django-storages[google]~=1.12.3 # https://github.com/jschneier/django-storages
djangorestframework-datatables~=0.6.0
djangorestframework~=3.12.4 # https://github.com/encode/django-rest-framework
drf-generators~=0.5.0
django~=3.2.6 # pyup: < 3.2 # https://www.djangoproject.com/
google-api-core~=2.2.2
google-api-python-client~=2.29.0
Expand Down

0 comments on commit 1b7e0bf

Please sign in to comment.