-
Notifications
You must be signed in to change notification settings - Fork 1
Marshaling intermediary relationships in nested includes and using the driver where appropriate #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
cmanallen
wants to merge
17
commits into
master
Choose a base branch
from
include
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
00c7aa2
Ignoring null values and added get_pagination_values method
cmanallen b6fea91
Added default pagination values coverage
cmanallen b552e86
Added pagination management module
cmanallen 47a7755
Added get_pagination_links method
cmanallen bdbe37d
Refactored pagination coverage
cmanallen c7ac896
Using default values in pagination links response
cmanallen 0c5e85c
Updated the changelog
cmanallen 91417dd
Corrected pathing validation
cmanallen 8bb9b0d
Returning intermediary relationships
cmanallen d36a3eb
Extended nested include coverage
cmanallen 22969af
Using the driver to extract the relationship
cmanallen 1cca4da
Catching attribute errors
cmanallen 16c6e1b
Updated changelog
cmanallen 76e4854
Setting schema during iteration
cmanallen 1b3841a
Updated path validation
cmanallen e544654
Revert "Updated path validation"
cmanallen 227e934
Removed unnecessary relationship validation checking
cmanallen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
# -*- coding: utf-8 -*- | ||
"""JSONAPI pagination implementation. | ||
|
||
This module validates, exposes, and acts upon pagination related data. | ||
""" | ||
import urllib | ||
|
||
|
||
class Pagination(object): | ||
_limit = 100 | ||
_offset = 0 | ||
|
||
def __init__(self, parameters): | ||
self.parameters = parameters | ||
|
||
@property | ||
def limit(self): | ||
"""The amount to limit a query by.""" | ||
return self._limit | ||
|
||
@limit.setter | ||
def limit(self, value): | ||
self._limit = value | ||
|
||
@property | ||
def offset(self): | ||
"""The amount to offset a query by.""" | ||
return self._offset | ||
|
||
@offset.setter | ||
def offset(self, value): | ||
self._offset = value | ||
|
||
@property | ||
def strategy_limit(self): | ||
"""Return the valid keys for a limit and offset based strategy.""" | ||
return ['page[limit]', 'page[offset]'] | ||
|
||
@property | ||
def strategy_page(self): | ||
"""Return the valid keys for a page based strategy.""" | ||
return ['page[size]', 'page[number]'] | ||
|
||
@property | ||
def strategy_cursor(self): | ||
"""Return the valid keys for a cusor based strategy. | ||
|
||
Cursor based strategies have not been implemented. | ||
""" | ||
return ['page[cursor]'] | ||
|
||
def get_links_object(self, base_url, total): | ||
"""Return a pagination links object.""" | ||
def get_page(offset): | ||
if offset == 0: | ||
return 1 | ||
return (offset / self.limit) + 1 | ||
|
||
parameters = self.parameters | ||
if ('page[offset]' not in parameters and | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cmanallen All of these string references should be class constants (more DRY). |
||
'page[number]' not in parameters): | ||
parameters['page[offset]'] = self.offset | ||
if 'page[limit]' not in parameters and 'page[size]' not in parameters: | ||
parameters['page[limit]'] = self.limit | ||
|
||
last_offset = total - self.limit | ||
next_offset = min(self.offset + self.limit, total - self.limit) | ||
prev_offset = max(self.offset - self.limit, 0) | ||
|
||
self_params = urllib.urlencode(parameters) | ||
|
||
first_obj = parameters | ||
first_obj = self._update_if_exists('page[offset]', 0, first_obj) | ||
first_obj = self._update_if_exists('page[number]', 1, first_obj) | ||
first_params = urllib.urlencode(first_obj) | ||
|
||
last_obj = parameters | ||
last_obj = self._update_if_exists( | ||
'page[offset]', last_offset, last_obj) | ||
last_obj = self._update_if_exists( | ||
'page[number]', get_page(last_offset), last_obj) | ||
last_params = urllib.urlencode(last_obj) | ||
|
||
next_obj = parameters | ||
next_obj = self._update_if_exists( | ||
'page[offset]', next_offset, next_obj) | ||
next_obj = self._update_if_exists( | ||
'page[number]', get_page(next_offset), next_obj) | ||
next_params = urllib.urlencode(next_obj) | ||
|
||
prev_obj = parameters | ||
prev_obj = self._update_if_exists( | ||
'page[offset]', prev_offset, prev_obj) | ||
prev_obj = self._update_if_exists( | ||
'page[number]', get_page(prev_offset), prev_obj) | ||
prev_params = urllib.urlencode(prev_obj) | ||
|
||
return { | ||
'self': '{}?{}'.format(base_url, self_params), | ||
'first': '{}?{}'.format(base_url, first_params), | ||
'last': '{}?{}'.format(base_url, last_params), | ||
'next': '{}?{}'.format(base_url, next_params), | ||
'prev': '{}?{}'.format(base_url, prev_params) | ||
} | ||
|
||
def get_pagination_values(self): | ||
"""Return the limit and offset values.""" | ||
return self.limit, self.offset | ||
|
||
def set_pagination_values(self): | ||
"""Set the limit and offset values.""" | ||
pagination_keys = self._extract_pagination_keys() | ||
for key in pagination_keys: | ||
value = self.parameters.get(key) | ||
if not value: | ||
continue | ||
if key in ['page[limit]', 'page[size]']: | ||
self.limit = int(value) | ||
else: | ||
self.offset = int(value) | ||
if 'page[number]' in pagination_keys: | ||
self.offset = max(self.offset * self.limit - self.limit, 1) | ||
return self | ||
|
||
def paginate_query(self, query): | ||
"""Return a paginated query object.""" | ||
return query.offset(self.offset).limit(self.limit) | ||
|
||
def validate_parameters(self): | ||
"""Validate the provided pagination strategy.""" | ||
errors = [] | ||
pagination_keys = self._extract_pagination_keys() | ||
errors.extend(self._validate_pagination_keys(pagination_keys)) | ||
errors.extend(self._validate_pagination_values(pagination_keys)) | ||
return errors | ||
|
||
def _extract_pagination_keys(self): | ||
pagination_keys = [] | ||
for parameter in self.parameters: | ||
if parameter in self.strategy_limit: | ||
pagination_keys.append(parameter) | ||
elif parameter in self.strategy_page: | ||
pagination_keys.append(parameter) | ||
return pagination_keys | ||
|
||
def _update_if_exists(self, key, value, obj): | ||
if key in obj: | ||
obj[key] = value | ||
return obj | ||
|
||
def _validate_pagination_keys(self, pagination_keys): | ||
errors = [] | ||
if len(pagination_keys) > 2: | ||
for key in pagination_keys: | ||
errors.append({ | ||
'detail': 'More than one pagination strategy specified.', | ||
'source': {'parameter': key} | ||
}) | ||
elif (len(pagination_keys) == 2 and | ||
set(pagination_keys) != set(self.strategy_page) and | ||
set(pagination_keys) != set(self.strategy_limit)): | ||
for key in pagination_keys: | ||
errors.append({ | ||
'detail': 'Mismatched pagination strategies specified.', | ||
'source': {'parameter': key} | ||
}) | ||
return errors | ||
|
||
def _validate_pagination_values(self, pagination_keys): | ||
errors = [] | ||
for key, value in self.parameters.iteritems(): | ||
if key not in pagination_keys: | ||
continue | ||
try: | ||
if not value: | ||
continue | ||
int(value) | ||
except ValueError: | ||
errors.append({ | ||
'detail': 'Value must be type number.', | ||
'source': {'parameter': key} | ||
}) | ||
return errors |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cmanallen Minor typo.