Skip to content

Commit bf8a1a9

Browse files
s7oevfilak-sap
authored andcommitted
service: add support for inlinecount
1 parent 3b680b3 commit bf8a1a9

2 files changed

Lines changed: 212 additions & 7 deletions

File tree

pyodata/v2/service.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
# Fallback to urllib2
2222
from urllib2 import quote
2323

24-
from pyodata.exceptions import HttpError, PyODataException, ExpressionError
24+
from pyodata.exceptions import HttpError, PyODataException, ExpressionError, ProgramError
2525
from . import model
2626

2727
LOGGER_NAME = 'pyodata.service'
@@ -603,6 +603,7 @@ def __init__(self, url, connection, handler, last_segment):
603603

604604
self._logger = logging.getLogger(LOGGER_NAME)
605605
self._count = None
606+
self._inlinecount = None
606607
self._top = None
607608
self._skip = None
608609
self._order_by = None
@@ -612,9 +613,12 @@ def __init__(self, url, connection, handler, last_segment):
612613
self._last_segment = last_segment
613614
self._logger.debug('New instance of QueryRequest for last segment: %s', self._last_segment)
614615

615-
def count(self):
616-
"""Sets a flag to return the number of items."""
617-
self._count = True
616+
def count(self, inline=False):
617+
"""Sets a flag to return the number of items. Can be inline with results or just the count."""
618+
if inline:
619+
self._inlinecount = True
620+
else:
621+
self._count = True
618622
return self
619623

620624
def expand(self, expand):
@@ -688,6 +692,9 @@ def get_query_params(self):
688692
if self._expand is not None:
689693
qparams['$expand'] = self._expand
690694

695+
if self._inlinecount:
696+
qparams['$inlinecount'] = 'allpages'
697+
691698
return qparams
692699

693700

@@ -1238,6 +1245,24 @@ def filter(self, *args, **kwargs):
12381245
return self
12391246

12401247

1248+
class ListWithTotalCount(list):
1249+
"""A list with the additional property total_count"""
1250+
1251+
def __init__(self, total_count):
1252+
super(ListWithTotalCount, self).__init__()
1253+
self._total_count = total_count
1254+
1255+
@property
1256+
def total_count(self):
1257+
"""Count of all entities"""
1258+
if self._total_count is None:
1259+
raise ProgramError('The collection does not include Total Count '
1260+
'of items because the request was made without '
1261+
'specifying "count(inline=True)".')
1262+
1263+
return self._total_count
1264+
1265+
12411266
class EntitySetProxy:
12421267
"""EntitySet Proxy"""
12431268

@@ -1362,7 +1387,6 @@ def get_entity_handler(response):
13621387

13631388
def get_entities(self):
13641389
"""Get all entities"""
1365-
13661390
def get_entities_handler(response):
13671391
"""Gets entity set from HTTP Response"""
13681392

@@ -1376,12 +1400,16 @@ def get_entities_handler(response):
13761400
return content
13771401

13781402
entities = content['d']
1403+
total_count = None
1404+
13791405
if isinstance(entities, dict):
1406+
if '__count' in entities:
1407+
total_count = int(entities['__count'])
13801408
entities = entities['results']
13811409

13821410
self._logger.info('Fetched %d entities', len(entities))
13831411

1384-
result = []
1412+
result = ListWithTotalCount(total_count)
13851413
for props in entities:
13861414
entity = EntityProxy(self._service, self._entity_set, self._entity_set.entity_type, props)
13871415
result.append(entity)

tests/test_service_v2.py

Lines changed: 178 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import pyodata.v2.model
1010
import pyodata.v2.service
11-
from pyodata.exceptions import PyODataException, HttpError, ExpressionError
11+
from pyodata.exceptions import PyODataException, HttpError, ExpressionError, ProgramError
1212
from pyodata.v2.service import EntityKey, EntityProxy, GetEntitySetFilter, ODataHttpResponse, HTTP_CODE_OK
1313

1414
from tests.conftest import assert_request_contains_header, contents_of_fixtures_file
@@ -1621,6 +1621,183 @@ def test_get_entity_set_query_filter_property_error(service):
16211621
assert e_info.value.args[0] == 'Foo'
16221622

16231623

1624+
@responses.activate
1625+
def test_inlinecount(service):
1626+
"""Check getting entities with $inlinecount"""
1627+
1628+
# pylint: disable=redefined-outer-name
1629+
1630+
responses.add(
1631+
responses.GET,
1632+
"{0}/Employees?$inlinecount=allpages".format(service.url),
1633+
json={'d': {
1634+
'__count': 3,
1635+
'results': [
1636+
{
1637+
'ID': 21,
1638+
'NameFirst': 'George',
1639+
'NameLast': 'Doe'
1640+
},{
1641+
'ID': 22,
1642+
'NameFirst': 'John',
1643+
'NameLast': 'Doe'
1644+
},{
1645+
'ID': 23,
1646+
'NameFirst': 'Rob',
1647+
'NameLast': 'Ickes'
1648+
}
1649+
]
1650+
}},
1651+
status=200)
1652+
1653+
request = service.entity_sets.Employees.get_entities().count(inline=True)
1654+
1655+
assert isinstance(request, pyodata.v2.service.GetEntitySetRequest)
1656+
1657+
assert request.execute().total_count == 3
1658+
1659+
1660+
@responses.activate
1661+
def test_inlinecount_with_skip(service):
1662+
"""Check getting entities with $inlinecount with $skip"""
1663+
1664+
# pylint: disable=redefined-outer-name
1665+
1666+
responses.add(
1667+
responses.GET,
1668+
"{0}/Employees?$inlinecount=allpages&$skip=1".format(service.url),
1669+
json={'d': {
1670+
'__count': 3,
1671+
'results': [
1672+
{
1673+
'ID': 22,
1674+
'NameFirst': 'John',
1675+
'NameLast': 'Doe'
1676+
},{
1677+
'ID': 23,
1678+
'NameFirst': 'Rob',
1679+
'NameLast': 'Ickes'
1680+
}
1681+
]
1682+
}},
1683+
status=200)
1684+
1685+
request = service.entity_sets.Employees.get_entities().skip(1).count(inline=True)
1686+
1687+
assert isinstance(request, pyodata.v2.service.GetEntitySetRequest)
1688+
1689+
assert request.execute().total_count == 3
1690+
1691+
1692+
@responses.activate
1693+
def test_navigation_inlinecount(service):
1694+
"""Check getting entities with $inlinecount via navigation property"""
1695+
1696+
# pylint: disable=redefined-outer-name
1697+
1698+
responses.add(
1699+
responses.GET,
1700+
"{0}/Employees(23)/Addresses?$inlinecount=allpages".format(service.url),
1701+
json={'d': {
1702+
'__count': 3,
1703+
'results': [
1704+
{
1705+
'ID': 456,
1706+
'Street': 'Baker Street',
1707+
'City': 'London'
1708+
},{
1709+
'ID': 457,
1710+
'Street': 'Lowth Road',
1711+
'City': 'London'
1712+
},{
1713+
'ID': 458,
1714+
'Street': 'Warner Road',
1715+
'City': 'Manchester'
1716+
}
1717+
]
1718+
}},
1719+
status=200)
1720+
1721+
addresses = service.entity_sets.Employees.get_entity(23).nav('Addresses').get_entities()
1722+
request = addresses.count(inline=True)
1723+
1724+
assert isinstance(request, pyodata.v2.service.GetEntitySetRequest)
1725+
1726+
assert request.execute().total_count == 3
1727+
1728+
1729+
@responses.activate
1730+
def test_inlinecount_with_filter(service):
1731+
"""Check getting entities with $inlinecount and $filter"""
1732+
1733+
# pylint: disable=redefined-outer-name
1734+
1735+
responses.add(
1736+
responses.GET,
1737+
"{0}/Employees(23)/Addresses?$inlinecount=allpages&%24filter=City%20eq%20%27London%27".format(service.url),
1738+
json={'d': {
1739+
'__count': 2,
1740+
'results': [
1741+
{
1742+
'ID': 456,
1743+
'Street': 'Baker Street',
1744+
'City': 'London'
1745+
},{
1746+
'ID': 457,
1747+
'Street': 'Lowth Road',
1748+
'City': 'London'
1749+
}
1750+
]
1751+
}},
1752+
status=200)
1753+
1754+
addresses = service.entity_sets.Employees.get_entity(23).nav('Addresses').get_entities()
1755+
request = addresses.filter(addresses.City == 'London').count(inline=True)
1756+
1757+
assert isinstance(request, pyodata.v2.service.GetEntitySetRequest)
1758+
1759+
assert request.execute().total_count == 2
1760+
1761+
1762+
@responses.activate
1763+
def test_total_count_exception(service):
1764+
"""Check getting entities without $inlinecount and then requesting total_count"""
1765+
1766+
# pylint: disable=redefined-outer-name
1767+
1768+
responses.add(
1769+
responses.GET,
1770+
"{0}/Employees".format(service.url),
1771+
json={'d': {
1772+
'results': [
1773+
{
1774+
'ID': 21,
1775+
'NameFirst': 'George',
1776+
'NameLast': 'Doe'
1777+
},{
1778+
'ID': 22,
1779+
'NameFirst': 'John',
1780+
'NameLast': 'Doe'
1781+
},{
1782+
'ID': 23,
1783+
'NameFirst': 'Rob',
1784+
'NameLast': 'Ickes'
1785+
}
1786+
]
1787+
}},
1788+
status=200)
1789+
1790+
request = service.entity_sets.Employees.get_entities()
1791+
1792+
assert isinstance(request, pyodata.v2.service.GetEntitySetRequest)
1793+
1794+
with pytest.raises(ProgramError) as e_info:
1795+
request.execute().total_count
1796+
1797+
assert str(e_info.value) == ('The collection does not include Total Count of items because '
1798+
'the request was made without specifying "count(inline=True)".')
1799+
1800+
16241801
@responses.activate
16251802
def test_count(service):
16261803
"""Check getting $count"""

0 commit comments

Comments
 (0)