Skip to content

Commit abd23c6

Browse files
fixes from convo with Matt
1 parent 8f07591 commit abd23c6

File tree

5 files changed

+50
-24
lines changed

5 files changed

+50
-24
lines changed

apps/fhir/bluebutton/v2/urls.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@
1717
# Patient ReadView
1818
re_path(
1919
r'Patient/(?P<resource_id>[^/]+)',
20-
PatientViewSet.as_view({'get': 'retrieve'}, version=2),
20+
PatientViewSet.as_view({'get': 'read'}, version=2),
2121
name='bb_oauth_fhir_patient_read_or_update_or_delete_v2',
2222
),
2323
# Patient SearchView
2424
re_path(
2525
r'Patient[/]?',
26-
PatientViewSet.as_view({'get': 'list'}, version=2),
26+
PatientViewSet.as_view({'get': 'search'}, version=2),
2727
name='bb_oauth_fhir_patient_search_v2',
2828
),
2929
# Coverage ReadView

apps/fhir/bluebutton/v3/urls.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919
# Patient ReadView
2020
re_path(
2121
r'Patient/(?P<resource_id>[^/]+)',
22-
waffle_switch('v3_endpoints')(PatientViewSet.as_view({'get': 'retrieve'}, version=3)),
22+
waffle_switch('v3_endpoints')(PatientViewSet.as_view({'get': 'read'}, version=3)),
2323
name='bb_oauth_fhir_patient_read_or_update_or_delete_v3',
2424
),
2525
# Patient SearchView
2626
re_path(
2727
r'Patient[/]?',
28-
waffle_switch('v3_endpoints')(PatientViewSet.as_view({'get': 'list'}, version=3)),
28+
waffle_switch('v3_endpoints')(PatientViewSet.as_view({'get': 'search'}, version=3)),
2929
name='bb_oauth_fhir_patient_search_v3',
3030
),
3131
# Coverage ReadView

apps/fhir/bluebutton/views/insurancecard_viewset.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ class DigitalInsuranceCardViewSet(ResourceViewSet):
2424
viewsets: django-rest-framework ViewSet base class
2525
"""
2626

27-
version = 1
2827
resource_type = 'Bundle'
2928

3029
required_coverage_search_scopes = ['patient/Coverage.rs', 'patient/Coverage.s', 'patient/Coverage.read']
@@ -40,7 +39,7 @@ class DigitalInsuranceCardViewSet(ResourceViewSet):
4039
HasDigitalInsuranceCardScope,
4140
]
4241

43-
def __init__(self, version=1, **kwargs):
42+
def __init__(self, version, **kwargs):
4443
super().__init__(version)
4544

4645
def initial(self, request, *args, **kwargs):

apps/fhir/bluebutton/views/patient_viewset.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
from apps.fhir.bluebutton.views.viewsets_base import ResourceViewSet
2-
from apps.fhir.bluebutton.views.search import HasSearchScope, SearchView
2+
from voluptuous import (
3+
Required,
4+
All,
5+
Match,
6+
Range,
7+
Coerce,
8+
)
9+
from apps.fhir.bluebutton.constants import DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE
310
from apps.authorization.permissions import DataAccessGrantPermission
411
from apps.capabilities.permissions import TokenHasProtectedCapability
512
from apps.fhir.bluebutton.permissions import (
@@ -11,6 +18,18 @@
1118
from rest_framework import permissions
1219

1320

21+
class HasSearchScope(permissions.BasePermission):
22+
def has_permission(self, request, view) -> bool: # type: ignore
23+
required_scopes = getattr(view, 'required_scopes', None)
24+
if required_scopes is None:
25+
return True
26+
27+
if hasattr(request, 'auth') and request.auth is not None:
28+
token_scopes = request.auth.scope
29+
return any(scope in token_scopes for scope in required_scopes)
30+
return False
31+
32+
1433
class PatientViewSet(ResourceViewSet):
1534
"""Patient django-rest-framework ViewSet experiment
1635
@@ -19,13 +38,19 @@ class PatientViewSet(ResourceViewSet):
1938
viewsets: django-rest-framework ViewSet base class
2039
"""
2140

22-
version = 1
2341
resource_type = 'Patient'
2442

2543
# TODO - I don't love the separation here, could be indicative that we don't want to move to resource based ViewSets, or that
2644
# we need a better base class, or these differences should be defined in PatientViewSet.
27-
SEARCH_QUERY_TRANSFORMS = getattr(SearchView, 'QUERY_TRANSFORMS', {})
28-
SEARCH_QUERY_SCHEMA = {**getattr(SearchView, 'QUERY_SCHEMA', {}), '_id': str, 'identifier': str}
45+
REGEX_LASTUPDATED_VALUE = r'^((lt)|(le)|(gt)|(ge)).+'
46+
SEARCH_QUERY_TRANSFORMS = {
47+
'count': '_count',
48+
}
49+
SEARCH_QUERY_SCHEMA = {
50+
'startIndex': Coerce(int),
51+
Required('_count', default=DEFAULT_PAGE_SIZE): All(Coerce(int), Range(min=0, max=MAX_PAGE_SIZE)), # type: ignore
52+
'_lastUpdated': [Match(REGEX_LASTUPDATED_VALUE, msg='the _lastUpdated operator is not valid')]
53+
}
2954

3055
SEARCH_PERMISSION_CLASSES = (
3156
permissions.IsAuthenticated,
@@ -48,6 +73,9 @@ class PatientViewSet(ResourceViewSet):
4873

4974
required_scopes = ['patient/Patient.read', 'patient/Patient.rs', 'patient/Patient.s']
5075

76+
def __init__(self, version, **kwargs):
77+
super().__init__(version)
78+
5179
def build_url(self, fhir_settings, resource_type, resource_id=None, *args, **kwargs):
5280
if fhir_settings.fhir_url.endswith('v1/fhir/'):
5381
# only if called by tests

apps/fhir/bluebutton/views/viewsets_base.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,11 @@ class ResourceViewSet(FhirDataView, viewsets.ViewSet):
1313
viewsets: django-rest-framework ViewSet base class
1414
"""
1515

16-
resource_type = None
17-
1816
SEARCH_PERMISSION_CLASSES = (permissions.IsAuthenticated,)
1917
READ_PERMISSION_CLASSES = (permissions.IsAuthenticated,)
2018

2119
def initial(self, request, *args, **kwargs):
20+
self.resource_type = None
2221
return super().initial(request, self.resource_type, *args, **kwargs)
2322

2423
def get_permissions(self):
@@ -31,35 +30,35 @@ def get_permissions(self):
3130
permission_classes = (permissions.IsAuthenticated,)
3231
return [permission_class() for permission_class in permission_classes]
3332

34-
def list(self, request, *args, **kwargs):
33+
def search(self, request, *args, **kwargs):
3534
out = self.fetch_data(request, self.resource_type, *args, **kwargs)
3635
return Response(out)
3736

38-
def retrieve(self, request, resource_id, *args, **kwargs):
37+
def read(self, request, resource_id, *args, **kwargs):
3938
out = self.fetch_data(request, self.resource_type, resource_id=resource_id, *args, **kwargs)
4039
return Response(out)
4140

4241
# A lot of this is copied (haphazardly) from generic, and the names and handling of the schema could be cleaned up
43-
def get_query_schema(self):
44-
if getattr(self, 'action', None) == 'list':
45-
return getattr(self, 'SEARCH_QUERY_SCHEMA', getattr(self, 'QUERY_SCHEMA', {}))
42+
def get_query_schema(self) -> dict:
43+
if getattr(self, 'action', None) == 'search':
44+
return getattr(self, 'SEARCH_QUERY_SCHEMA', getattr(self, 'READ_QUERY_TRANSFORMS', {}))
4645
return {}
4746

48-
def get_query_transforms(self):
49-
if getattr(self, 'action', None) == 'list':
50-
return getattr(self, 'SEARCH_QUERY_TRANSFORMS', getattr(self, 'QUERY_TRANSFORMS', {}))
51-
return getattr(self, 'QUERY_TRANSFORMS', {})
47+
def get_query_transforms(self) -> dict:
48+
if getattr(self, 'action', None) == 'search':
49+
return getattr(self, 'SEARCH_QUERY_TRANSFORMS', getattr(self, 'READ_QUERY_TRANSFORMS', {}))
50+
return {}
5251

53-
def map_parameters(self, params):
52+
def map_parameters(self, params) -> dict:
5453
transforms = self.get_query_transforms()
5554
for key, correct in transforms.items():
5655
val = params.pop(key, None)
5756
if val is not None:
5857
params[correct] = val
5958
return params
6059

61-
def filter_parameters(self, request):
62-
if getattr(self, 'action', None) != 'list':
60+
def filter_parameters(self, request) -> dict:
61+
if getattr(self, 'action', None) != 'search':
6362
return {}
6463

6564
params = self.map_parameters(request.query_params.dict())

0 commit comments

Comments
 (0)