|
7 | 7 |
|
8 | 8 | from collections import OrderedDict |
9 | 9 | from django.conf import settings |
10 | | -from django.conf.urls import url |
| 10 | +from django.conf.urls import url, include |
11 | 11 | from django.core.exceptions import PermissionDenied |
12 | 12 | from django.http import HttpResponse, Http404 |
| 13 | +from django.utils.functional import lazy_property |
13 | 14 | from django.views.decorators.csrf import csrf_exempt |
14 | 15 | from odin.codecs import json_codec |
15 | 16 | from odin.exceptions import CodecDecodeError, ValidationError |
@@ -388,3 +389,73 @@ def options_response(self, request, route_key, **kwargs): |
388 | 389 | response = HttpResponse(status=204) |
389 | 390 | response['Allow'] = ','.join(method for method, route in routes.items() if route != 'options_response') |
390 | 391 | return response |
| 392 | + |
| 393 | + |
| 394 | +class ApiCollection(object): |
| 395 | + """ |
| 396 | + Collection of several resource API's. |
| 397 | + Along with helper methods for building URL patterns. |
| 398 | + :: |
| 399 | + urlpatterns += Api( |
| 400 | + ApiCollection( |
| 401 | + UserApi(), |
| 402 | + MyApi(), |
| 403 | + ) |
| 404 | + ).patterns() |
| 405 | + """ |
| 406 | + def __init__(self, *resource_apis, **kwargs): |
| 407 | + self.api_name = kwargs.pop('api_name', 'api') |
| 408 | + self.resource_apis = resource_apis |
| 409 | + |
| 410 | + @lazy_property |
| 411 | + def urls(self): |
| 412 | + urls = [] |
| 413 | + for resource_api in self.resource_apis: |
| 414 | + urls.extend(resource_api.urls) |
| 415 | + return urls |
| 416 | + |
| 417 | + def include(self, namespace=None): |
| 418 | + return include(self.urls, namespace) |
| 419 | + |
| 420 | + def patterns(self, api_name=None): |
| 421 | + api_name = api_name or self.api_name |
| 422 | + return [url(r'^%s/' % api_name, self.include())] |
| 423 | + |
| 424 | + |
| 425 | +class ApiVersion(ApiCollection): |
| 426 | + """ |
| 427 | + A versioned collection of several resource API's. |
| 428 | + Along with helper methods for building URL patterns. |
| 429 | + """ |
| 430 | + def __init__(self, *resource_apis, **kwargs): |
| 431 | + kwargs.setdefault('api_name', kwargs.pop('version', 'v1')) |
| 432 | + super(ApiVersion, self).__init__(*resource_apis, **kwargs) |
| 433 | + |
| 434 | + |
| 435 | +class Api(object): |
| 436 | + """ |
| 437 | + An API (made up of versions). |
| 438 | + :: |
| 439 | + urlpatterns += Api( |
| 440 | + ApiVersion( |
| 441 | + UserApi(), |
| 442 | + MyApi(), |
| 443 | + version='v1', |
| 444 | + ) |
| 445 | + ).patterns() |
| 446 | + """ |
| 447 | + def __init__(self, *versions, **kwargs): |
| 448 | + self.versions = versions |
| 449 | + self.api_name = kwargs.get('api_name', 'api') |
| 450 | + |
| 451 | + def patterns(self): |
| 452 | + urls = [url(r'^%s/%s/' % (self.api_name, v.api_name), v.include()) for v in self.versions] |
| 453 | + urls.append(url(r'^%s/$' % self.api_name, self._unknown_version)) |
| 454 | + return urls |
| 455 | + |
| 456 | + def _unknown_version(self, _): |
| 457 | + supported_versions = [v.api_name for v in self.versions] |
| 458 | + return HttpResponse( |
| 459 | + "Unsupported API version. Available versions: %s" % ', '.join(supported_versions), |
| 460 | + status=418 # I'm a teapot... Is technically a bad request but makes sense to have a different status code. |
| 461 | + ) |
0 commit comments