Skip to content

Commit be68d67

Browse files
committed
feat: add support for application list pagination
Application list results are now paginated. Add properties and helper classes for accessing application list pages. Signed-off-by: Subin Shekhar <[email protected]>
1 parent 3bc613a commit be68d67

File tree

4 files changed

+391
-51
lines changed

4 files changed

+391
-51
lines changed

examples/test_ibm_analytics_engine_api_v3_examples.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -318,15 +318,21 @@ def test_list_applications_example(self):
318318
print("\nlist_applications() result:")
319319
# begin-list_applications
320320

321-
response = ibm_analytics_engine_api_service.list_applications(
322-
instance_id="e64c907a-e82f-46fd-addc-ccfafbd28b09",
321+
all_results = []
322+
pager = ApplicationsPager(
323+
client=ibm_analytics_engine_api_service,
324+
instance_id='e64c907a-e82f-46fd-addc-ccfafbd28b09',
325+
state=['accepted','running','finished','failed'],
326+
limit=10,
323327
)
324-
application_collection = response.get_result()
328+
while pager.has_next():
329+
next_page = pager.get_next()
330+
assert next_page is not None
331+
all_results.extend(next_page)
325332

326-
print(json.dumps(application_collection, indent=2))
333+
print(json.dumps(all_results, indent=2))
327334

328335
# end-list_applications
329-
330336
except ApiException as e:
331337
pytest.fail(str(e))
332338

iaesdk/ibm_analytics_engine_api_v3.py

Lines changed: 215 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,15 @@ def create_application(
570570
response = self.send(request, **kwargs)
571571
return response
572572

573-
def list_applications(self, instance_id: str, *, state: List[str] = None, **kwargs) -> DetailedResponse:
573+
574+
def list_applications(self,
575+
instance_id: str,
576+
*,
577+
state: List[str] = None,
578+
limit: int = None,
579+
start: str = None,
580+
**kwargs
581+
) -> DetailedResponse:
574582
"""
575583
List all Spark applications.
576584
@@ -581,6 +589,10 @@ def list_applications(self, instance_id: str, *, state: List[str] = None, **kwar
581589
associated with the Spark application(s).
582590
:param List[str] state: (optional) List of Spark application states that
583591
will be used to filter the response.
592+
:param int limit: (optional) Number of application entries to be included
593+
in the response.
594+
:param str start: (optional) Token used to fetch the next or the previous
595+
page of the applications list.
584596
:param dict headers: A `dict` containing the request headers
585597
:return: A `DetailedResponse` containing the result, headers and HTTP status code.
586598
:rtype: DetailedResponse with `dict` result representing a `ApplicationCollection` object
@@ -597,7 +609,9 @@ def list_applications(self, instance_id: str, *, state: List[str] = None, **kwar
597609
headers.update(sdk_headers)
598610

599611
params = {
600-
"state": convert_list(state),
612+
'state': convert_list(state),
613+
'limit': limit,
614+
'start': start
601615
}
602616

603617
if "headers" in kwargs:
@@ -1318,25 +1332,61 @@ class StateEnum(str, Enum):
13181332

13191333
class ApplicationCollection:
13201334
"""
1321-
An array of application details.
1322-
1323-
:attr List[Application] applications: (optional) List of applications.
1335+
A paginated collection of applications.
1336+
1337+
:attr List[Application] applications: List of applications.
1338+
:attr PageLink first: (optional) A reference to a page in a paginated
1339+
collection.
1340+
:attr PageLink next: (optional) A reference to a page in a paginated collection.
1341+
:attr PageLink previous: (optional) A reference to a page in a paginated
1342+
collection.
1343+
:attr int limit: The maximum number of results in this page of the collection.
13241344
"""
13251345

1326-
def __init__(self, *, applications: List["Application"] = None) -> None:
1346+
def __init__(self,
1347+
applications: List['Application'],
1348+
limit: int,
1349+
*,
1350+
first: 'PageLink' = None,
1351+
next: 'PageLink' = None,
1352+
previous: 'PageLink' = None) -> None:
13271353
"""
13281354
Initialize a ApplicationCollection object.
13291355
1330-
:param List[Application] applications: (optional) List of applications.
1356+
:param List[Application] applications: List of applications.
1357+
:param int limit: The maximum number of results in this page of the
1358+
collection.
1359+
:param PageLink first: (optional) A reference to a page in a paginated
1360+
collection.
1361+
:param PageLink next: (optional) A reference to a page in a paginated
1362+
collection.
1363+
:param PageLink previous: (optional) A reference to a page in a paginated
1364+
collection.
13311365
"""
13321366
self.applications = applications
1367+
self.first = first
1368+
self.next = next
1369+
self.previous = previous
1370+
self.limit = limit
13331371

13341372
@classmethod
13351373
def from_dict(cls, _dict: Dict) -> "ApplicationCollection":
13361374
"""Initialize a ApplicationCollection object from a json dictionary."""
13371375
args = {}
1338-
if "applications" in _dict:
1339-
args["applications"] = [Application.from_dict(v) for v in _dict.get("applications")]
1376+
if 'applications' in _dict:
1377+
args['applications'] = [Application.from_dict(x) for x in _dict.get('applications')]
1378+
else:
1379+
raise ValueError('Required property \'applications\' not present in ApplicationCollection JSON')
1380+
if 'first' in _dict:
1381+
args['first'] = PageLink.from_dict(_dict.get('first'))
1382+
if 'next' in _dict:
1383+
args['next'] = PageLink.from_dict(_dict.get('next'))
1384+
if 'previous' in _dict:
1385+
args['previous'] = PageLink.from_dict(_dict.get('previous'))
1386+
if 'limit' in _dict:
1387+
args['limit'] = _dict.get('limit')
1388+
else:
1389+
raise ValueError('Required property \'limit\' not present in ApplicationCollection JSON')
13401390
return cls(**args)
13411391

13421392
@classmethod
@@ -1347,14 +1397,16 @@ def _from_dict(cls, _dict):
13471397
def to_dict(self) -> Dict:
13481398
"""Return a json dictionary representing this model."""
13491399
_dict = {}
1350-
if hasattr(self, "applications") and self.applications is not None:
1351-
applications_list = []
1352-
for v in self.applications:
1353-
if isinstance(v, dict):
1354-
applications_list.append(v)
1355-
else:
1356-
applications_list.append(v.to_dict())
1357-
_dict["applications"] = applications_list
1400+
if hasattr(self, 'applications') and self.applications is not None:
1401+
_dict['applications'] = [x.to_dict() for x in self.applications]
1402+
if hasattr(self, 'first') and self.first is not None:
1403+
_dict['first'] = self.first.to_dict()
1404+
if hasattr(self, 'next') and self.next is not None:
1405+
_dict['next'] = self.next.to_dict()
1406+
if hasattr(self, 'previous') and self.previous is not None:
1407+
_dict['previous'] = self.previous.to_dict()
1408+
if hasattr(self, 'limit') and self.limit is not None:
1409+
_dict['limit'] = self.limit
13581410
return _dict
13591411

13601412
def _to_dict(self):
@@ -3014,8 +3066,74 @@ def __ne__(self, other: "LoggingConfigurationResponseLogServer") -> bool:
30143066
"""Return `true` when self and other are not equal, false otherwise."""
30153067
return not self == other
30163068

3069+
class PageLink():
3070+
"""
3071+
A reference to a page in a paginated collection.
3072+
3073+
:attr str href: A url which returns a specific page of a collection.
3074+
:attr str start: (optional) A token which loads a specific page of a collection
3075+
when it is provided the url of the collection.
3076+
"""
3077+
3078+
def __init__(self,
3079+
href: str,
3080+
*,
3081+
start: str = None) -> None:
3082+
"""
3083+
Initialize a PageLink object.
3084+
3085+
:param str href: A url which returns a specific page of a collection.
3086+
:param str start: (optional) A token which loads a specific page of a
3087+
collection when it is provided the url of the collection.
3088+
"""
3089+
self.href = href
3090+
self.start = start
3091+
3092+
@classmethod
3093+
def from_dict(cls, _dict: Dict) -> 'PageLink':
3094+
"""Initialize a PageLink object from a json dictionary."""
3095+
args = {}
3096+
if 'href' in _dict:
3097+
args['href'] = _dict.get('href')
3098+
else:
3099+
raise ValueError('Required property \'href\' not present in PageLink JSON')
3100+
if 'start' in _dict:
3101+
args['start'] = _dict.get('start')
3102+
return cls(**args)
3103+
3104+
@classmethod
3105+
def _from_dict(cls, _dict):
3106+
"""Initialize a PageLink object from a json dictionary."""
3107+
return cls.from_dict(_dict)
3108+
3109+
def to_dict(self) -> Dict:
3110+
"""Return a json dictionary representing this model."""
3111+
_dict = {}
3112+
if hasattr(self, 'href') and self.href is not None:
3113+
_dict['href'] = self.href
3114+
if hasattr(self, 'start') and self.start is not None:
3115+
_dict['start'] = self.start
3116+
return _dict
3117+
3118+
def _to_dict(self):
3119+
"""Return a json dictionary representing this model."""
3120+
return self.to_dict()
3121+
3122+
def __str__(self) -> str:
3123+
"""Return a `str` version of this PageLink object."""
3124+
return json.dumps(self.to_dict(), indent=2)
3125+
3126+
def __eq__(self, other: 'PageLink') -> bool:
3127+
"""Return `true` when self and other are equal, false otherwise."""
3128+
if not isinstance(other, self.__class__):
3129+
return False
3130+
return self.__dict__ == other.__dict__
30173131

3018-
class ResourceConsumptionLimitsResponse:
3132+
def __ne__(self, other: 'PageLink') -> bool:
3133+
"""Return `true` when self and other are not equal, false otherwise."""
3134+
return not self == other
3135+
3136+
class ResourceConsumptionLimitsResponse():
30193137
"""
30203138
Resource consumption limits for the instance.
30213139
@@ -3245,6 +3363,84 @@ class StateEnum(str, Enum):
32453363
"""
32463364
State of the Spark history server.
32473365
"""
3248-
3366+
32493367
STARTED = "started"
32503368
STOPPED = "stopped"
3369+
3370+
3371+
##############################################################################
3372+
# Pagers
3373+
##############################################################################
3374+
3375+
class ApplicationsPager():
3376+
"""
3377+
ApplicationsPager can be used to simplify the use of the "list_applications" method.
3378+
"""
3379+
3380+
def __init__(self,
3381+
*,
3382+
client: IbmAnalyticsEngineApiV3,
3383+
instance_id: str,
3384+
state: List[str] = None,
3385+
limit: int = None,
3386+
) -> None:
3387+
"""
3388+
Initialize a ApplicationsPager object.
3389+
:param str instance_id: The identifier of the Analytics Engine instance
3390+
associated with the Spark application(s).
3391+
:param List[str] state: (optional) List of Spark application states that
3392+
will be used to filter the response.
3393+
:param int limit: (optional) Number of application entries to be included
3394+
in the response.
3395+
"""
3396+
self._has_next = True
3397+
self._client = client
3398+
self._page_context = { 'next': None }
3399+
self._instance_id = instance_id
3400+
self._state = state
3401+
self._limit = limit
3402+
3403+
def has_next(self) -> bool:
3404+
"""
3405+
Returns true if there are potentially more results to be retrieved.
3406+
"""
3407+
return self._has_next
3408+
3409+
def get_next(self) -> List[dict]:
3410+
"""
3411+
Returns the next page of results.
3412+
:return: A List[dict], where each element is a dict that represents an instance of Application.
3413+
:rtype: List[dict]
3414+
"""
3415+
if not self.has_next():
3416+
raise StopIteration(message='No more results available')
3417+
3418+
result = self._client.list_applications(
3419+
instance_id=self._instance_id,
3420+
state=self._state,
3421+
limit=self._limit,
3422+
start=self._page_context.get('next'),
3423+
).get_result()
3424+
3425+
next = None
3426+
next_page_link = result.get('next')
3427+
if next_page_link is not None:
3428+
next = next_page_link.get('start')
3429+
self._page_context['next'] = next
3430+
if next is None:
3431+
self._has_next = False
3432+
3433+
return result.get('applications')
3434+
3435+
def get_all(self) -> List[dict]:
3436+
"""
3437+
Returns all results by invoking get_next() repeatedly
3438+
until all pages of results have been retrieved.
3439+
:return: A List[dict], where each element is a dict that represents an instance of Application.
3440+
:rtype: List[dict]
3441+
"""
3442+
results = []
3443+
while self.has_next():
3444+
next_page = self.get_next()
3445+
results.extend(next_page)
3446+
return results

test/integration/test_ibm_analytics_engine_api_v3.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,43 @@ def test_create_application(self):
197197
def test_list_applications(self):
198198
response = self.ibm_analytics_engine_api_service.list_applications(
199199
instance_id=self.instance_id,
200-
state=["accepted", "submitted", "waiting", "running", "finished", "failed"],
200+
state=['accepted','running','finished','failed','stopped'],
201+
limit=1
201202
)
202203

203-
assert response.get_status_code() == 200
204-
application_collection = response.get_result()
204+
assert list_applications_response.get_status_code() == 200
205+
application_collection = list_applications_response.get_result()
205206
assert application_collection is not None
206207

208+
@needscredentials
209+
def test_list_applications_with_pager(self):
210+
all_results = []
211+
212+
# Test get_next().
213+
pager = ApplicationsPager(
214+
client=self.ibm_analytics_engine_api_service,
215+
instance_id=self.instance_id,
216+
state=['accepted','running','finished','failed','stopped'],
217+
limit=10,
218+
)
219+
while pager.has_next():
220+
next_page = pager.get_next()
221+
assert next_page is not None
222+
all_results.extend(next_page)
223+
224+
# Test get_all().
225+
pager = ApplicationsPager(
226+
client=self.ibm_analytics_engine_api_service,
227+
instance_id=self.instance_id,
228+
state=['accepted','running','finished','failed','stopped'],
229+
limit=10,
230+
)
231+
all_items = pager.get_all()
232+
assert all_items is not None
233+
234+
assert len(all_results) == len(all_items)
235+
print(f'\nlist_applications() returned a total of {len(all_results)} items(s) using ApplicationsPager.')
236+
207237
@needscredentials
208238
def test_get_application(self):
209239
response = self.ibm_analytics_engine_api_service.get_application(

0 commit comments

Comments
 (0)