Skip to content

Commit e7b68a9

Browse files
authored
Merge pull request #258 from microsoft/users/tedchamb/ct
add support for returning continuationToken for methods using IPagedList pattern
2 parents 5f12ffa + 7675147 commit e7b68a9

File tree

21 files changed

+771
-97
lines changed

21 files changed

+771
-97
lines changed

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@
1414

1515
# Shell scripts
1616
*.sh eol=lf
17+
18+
# Python
19+
*.py eol=lf

azure-devops/azure/devops/client.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,23 +124,26 @@ def _create_request_message(self, http_method, location_id, route_values=None,
124124
route_values = {}
125125
route_values['area'] = location.area
126126
route_values['resource'] = location.resource_name
127-
url = self._transform_route_template(location.route_template, route_values)
127+
route_template = self._remove_optional_route_parameters(location.route_template,
128+
route_values)
128129
logger.debug('Route template: %s', location.route_template)
130+
url = self._client.format_url(route_template, **route_values)
131+
import pprint
132+
pprint.pprint("url=" + url)
129133
request = ClientRequest(method=http_method, url=self._client.format_url(url))
130134
if query_parameters:
131135
request.format_parameters(query_parameters)
132136
return request
133137

134138
@staticmethod
135-
def _transform_route_template(route_template, route_values):
139+
def _remove_optional_route_parameters(route_template, route_values):
136140
new_template = ''
137141
route_template = route_template.replace('{*', '{')
138142
for path_segment in route_template.split('/'):
139143
if (len(path_segment) <= 2 or not path_segment[0] == '{'
140-
or not path_segment[len(path_segment) - 1] == '}'):
144+
or not path_segment[len(path_segment) - 1] == '}'
145+
or path_segment[1:len(path_segment) - 1] in route_values):
141146
new_template = new_template + '/' + path_segment
142-
elif path_segment[1:len(path_segment) - 1] in route_values:
143-
new_template = new_template + '/' + route_values[path_segment[1:len(path_segment) - 1]]
144147
return new_template
145148

146149
def _get_resource_location(self, location_id):
@@ -270,11 +273,18 @@ def _handle_error(self, request, response):
270273
raise AzureDevOpsClientRequestError(full_message_format.format(error_message=error_message,
271274
status_code=response.status_code))
272275

276+
def _get_continuation_token(self, response):
277+
if self._continuation_token_header_key in response.headers:
278+
return response.headers[self._continuation_token_header_key]
279+
else:
280+
return None
281+
273282
@staticmethod
274283
def _normalize_url(url):
275284
return url.rstrip('/').lower()
276285

277286
_locations_cache = {}
287+
_continuation_token_header_key = 'X-MS-ContinuationToken'
278288
_session_header_key = 'X-TFS-Session'
279289
_session_data = {_session_header_key: str(uuid.uuid4())}
280290

azure-devops/azure/devops/released/build/build_client.py

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ def get_builds(self, project, definitions=None, queues=None, build_number=None,
212212
:param [int] build_ids: A comma-delimited list that specifies the IDs of builds to retrieve.
213213
:param str repository_id: If specified, filters to builds that built from this repository.
214214
:param str repository_type: If specified, filters to builds that built from repositories of this type.
215-
:rtype: [Build]
215+
:rtype: :class:`<GetBuildsResponseValue>`
216216
"""
217217
route_values = {}
218218
if project is not None:
@@ -268,7 +268,22 @@ def get_builds(self, project, definitions=None, queues=None, build_number=None,
268268
version='5.1',
269269
route_values=route_values,
270270
query_parameters=query_parameters)
271-
return self._deserialize('[Build]', self._unwrap_collection(response))
271+
response_value = self._deserialize('[Build]', self._unwrap_collection(response))
272+
continuation_token = self._get_continuation_token(response)
273+
return self.GetBuildsResponseValue(response_value, continuation_token)
274+
275+
class GetBuildsResponseValue(object):
276+
def __init__(self, value, continuation_token):
277+
"""
278+
Response for the get_builds method
279+
280+
:param value:
281+
:type value: :class:`<[Build]> <azure.devops.v5_1.build.models.[Build]>`
282+
:param continuation_token: The continuation token to be used to get the next page of results.
283+
:type continuation_token: str
284+
"""
285+
self.value = value
286+
self.continuation_token = continuation_token
272287

273288
def queue_build(self, build, project, ignore_warnings=None, check_in_ticket=None, source_build_id=None):
274289
"""QueueBuild.
@@ -351,7 +366,7 @@ def get_build_changes(self, project, build_id, continuation_token=None, top=None
351366
:param str continuation_token:
352367
:param int top: The maximum number of changes to return
353368
:param bool include_source_change:
354-
:rtype: [Change]
369+
:rtype: :class:`<GetBuildChangesResponseValue>`
355370
"""
356371
route_values = {}
357372
if project is not None:
@@ -370,7 +385,22 @@ def get_build_changes(self, project, build_id, continuation_token=None, top=None
370385
version='5.1',
371386
route_values=route_values,
372387
query_parameters=query_parameters)
373-
return self._deserialize('[Change]', self._unwrap_collection(response))
388+
response_value = self._deserialize('[Change]', self._unwrap_collection(response))
389+
continuation_token = self._get_continuation_token(response)
390+
return self.GetBuildChangesResponseValue(response_value, continuation_token)
391+
392+
class GetBuildChangesResponseValue(object):
393+
def __init__(self, value, continuation_token):
394+
"""
395+
Response for the get_build_changes method
396+
397+
:param value:
398+
:type value: :class:`<[Change]> <azure.devops.v5_1.build.models.[Change]>`
399+
:param continuation_token: The continuation token to be used to get the next page of results.
400+
:type continuation_token: str
401+
"""
402+
self.value = value
403+
self.continuation_token = continuation_token
374404

375405
def get_build_controller(self, controller_id):
376406
"""GetBuildController.
@@ -497,7 +527,7 @@ def get_definitions(self, project, name=None, repository_id=None, repository_typ
497527
:param str task_id_filter: If specified, filters to definitions that use the specified task.
498528
:param int process_type: If specified, filters to definitions with the given process type.
499529
:param str yaml_filename: If specified, filters to YAML definitions that match the given filename.
500-
:rtype: [BuildDefinitionReference]
530+
:rtype: :class:`<GetDefinitionsResponseValue>`
501531
"""
502532
route_values = {}
503533
if project is not None:
@@ -541,7 +571,22 @@ def get_definitions(self, project, name=None, repository_id=None, repository_typ
541571
version='5.1',
542572
route_values=route_values,
543573
query_parameters=query_parameters)
544-
return self._deserialize('[BuildDefinitionReference]', self._unwrap_collection(response))
574+
response_value = self._deserialize('[BuildDefinitionReference]', self._unwrap_collection(response))
575+
continuation_token = self._get_continuation_token(response)
576+
return self.GetDefinitionsResponseValue(response_value, continuation_token)
577+
578+
class GetDefinitionsResponseValue(object):
579+
def __init__(self, value, continuation_token):
580+
"""
581+
Response for the get_definitions method
582+
583+
:param value:
584+
:type value: :class:`<[BuildDefinitionReference]> <azure.devops.v5_1.build.models.[BuildDefinitionReference]>`
585+
:param continuation_token: The continuation token to be used to get the next page of results.
586+
:type continuation_token: str
587+
"""
588+
self.value = value
589+
self.continuation_token = continuation_token
545590

546591
def restore_definition(self, project, definition_id, deleted):
547592
"""RestoreDefinition.

azure-devops/azure/devops/released/core/core_client.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def get_projects(self, state_filter=None, top=None, skip=None, continuation_toke
140140
:param int skip:
141141
:param str continuation_token:
142142
:param bool get_default_team_image_url:
143-
:rtype: [TeamProjectReference]
143+
:rtype: :class:`<GetProjectsResponseValue>`
144144
"""
145145
query_parameters = {}
146146
if state_filter is not None:
@@ -157,7 +157,22 @@ def get_projects(self, state_filter=None, top=None, skip=None, continuation_toke
157157
location_id='603fe2ac-9723-48b9-88ad-09305aa6c6e1',
158158
version='5.1',
159159
query_parameters=query_parameters)
160-
return self._deserialize('[TeamProjectReference]', self._unwrap_collection(response))
160+
response_value = self._deserialize('[TeamProjectReference]', self._unwrap_collection(response))
161+
continuation_token = self._get_continuation_token(response)
162+
return self.GetProjectsResponseValue(response_value, continuation_token)
163+
164+
class GetProjectsResponseValue(object):
165+
def __init__(self, value, continuation_token):
166+
"""
167+
Response for the get_projects method
168+
169+
:param value:
170+
:type value: :class:`<[TeamProjectReference]> <azure.devops.v5_1.core.models.[TeamProjectReference]>`
171+
:param continuation_token: The continuation token to be used to get the next page of results.
172+
:type continuation_token: str
173+
"""
174+
self.value = value
175+
self.continuation_token = continuation_token
161176

162177
def queue_create_project(self, project_to_create):
163178
"""QueueCreateProject.

azure-devops/azure/devops/released/git/git_client_base.py

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -781,13 +781,15 @@ def get_pull_request_iteration_commits(self, repository_id, pull_request_id, ite
781781
query_parameters=query_parameters)
782782
return self._deserialize('[GitCommitRef]', self._unwrap_collection(response))
783783

784-
def get_pull_request_commits(self, repository_id, pull_request_id, project=None):
784+
def get_pull_request_commits(self, repository_id, pull_request_id, project=None, top=None, continuation_token=None):
785785
"""GetPullRequestCommits.
786786
Get the commits for the specified pull request.
787787
:param str repository_id: ID or name of the repository.
788788
:param int pull_request_id: ID of the pull request.
789789
:param str project: Project ID or project name
790-
:rtype: [GitCommitRef]
790+
:param int top: Maximum number of commits to return.
791+
:param str continuation_token: The continuation token used for pagination.
792+
:rtype: :class:`<GetPullRequestCommitsResponseValue>`
791793
"""
792794
route_values = {}
793795
if project is not None:
@@ -796,11 +798,32 @@ def get_pull_request_commits(self, repository_id, pull_request_id, project=None)
796798
route_values['repositoryId'] = self._serialize.url('repository_id', repository_id, 'str')
797799
if pull_request_id is not None:
798800
route_values['pullRequestId'] = self._serialize.url('pull_request_id', pull_request_id, 'int')
801+
query_parameters = {}
802+
if top is not None:
803+
query_parameters['$top'] = self._serialize.query('top', top, 'int')
804+
if continuation_token is not None:
805+
query_parameters['continuationToken'] = self._serialize.query('continuation_token', continuation_token, 'str')
799806
response = self._send(http_method='GET',
800807
location_id='52823034-34a8-4576-922c-8d8b77e9e4c4',
801808
version='5.1',
802-
route_values=route_values)
803-
return self._deserialize('[GitCommitRef]', self._unwrap_collection(response))
809+
route_values=route_values,
810+
query_parameters=query_parameters)
811+
response_value = self._deserialize('[GitCommitRef]', self._unwrap_collection(response))
812+
continuation_token = self._get_continuation_token(response)
813+
return self.GetPullRequestCommitsResponseValue(response_value, continuation_token)
814+
815+
class GetPullRequestCommitsResponseValue(object):
816+
def __init__(self, value, continuation_token):
817+
"""
818+
Response for the get_pull_request_commits method
819+
820+
:param value:
821+
:type value: :class:`<[GitCommitRef]> <azure.devops.v5_1.git.models.[GitCommitRef]>`
822+
:param continuation_token: The continuation token to be used to get the next page of results.
823+
:type continuation_token: str
824+
"""
825+
self.value = value
826+
self.continuation_token = continuation_token
804827

805828
def get_pull_request_iteration_changes(self, repository_id, pull_request_id, iteration_id, project=None, top=None, skip=None, compare_to=None):
806829
"""GetPullRequestIterationChanges.
@@ -1601,7 +1624,7 @@ def get_pushes(self, repository_id, project=None, skip=None, top=None, search_cr
16011624
query_parameters=query_parameters)
16021625
return self._deserialize('[GitPush]', self._unwrap_collection(response))
16031626

1604-
def get_refs(self, repository_id, project=None, filter=None, include_links=None, include_statuses=None, include_my_branches=None, latest_statuses_only=None, peel_tags=None, filter_contains=None):
1627+
def get_refs(self, repository_id, project=None, filter=None, include_links=None, include_statuses=None, include_my_branches=None, latest_statuses_only=None, peel_tags=None, filter_contains=None, top=None, continuation_token=None):
16051628
"""GetRefs.
16061629
Queries the provided repository for its refs and returns them.
16071630
:param str repository_id: The name or ID of the repository.
@@ -1613,7 +1636,9 @@ def get_refs(self, repository_id, project=None, filter=None, include_links=None,
16131636
:param bool latest_statuses_only: [optional] True to include only the tip commit status for each ref. This option requires `includeStatuses` to be true. The default value is false.
16141637
:param bool peel_tags: [optional] Annotated tags will populate the PeeledObjectId property. default is false.
16151638
:param str filter_contains: [optional] A filter to apply to the refs (contains).
1616-
:rtype: [GitRef]
1639+
:param int top: [optional] Maximum number of refs to return. It cannot be bigger than 1000. If it is not provided but continuationToken is, top will default to 100.
1640+
:param str continuation_token: The continuation token used for pagination.
1641+
:rtype: :class:`<GetRefsResponseValue>`
16171642
"""
16181643
route_values = {}
16191644
if project is not None:
@@ -1635,12 +1660,31 @@ def get_refs(self, repository_id, project=None, filter=None, include_links=None,
16351660
query_parameters['peelTags'] = self._serialize.query('peel_tags', peel_tags, 'bool')
16361661
if filter_contains is not None:
16371662
query_parameters['filterContains'] = self._serialize.query('filter_contains', filter_contains, 'str')
1663+
if top is not None:
1664+
query_parameters['$top'] = self._serialize.query('top', top, 'int')
1665+
if continuation_token is not None:
1666+
query_parameters['continuationToken'] = self._serialize.query('continuation_token', continuation_token, 'str')
16381667
response = self._send(http_method='GET',
16391668
location_id='2d874a60-a811-4f62-9c9f-963a6ea0a55b',
16401669
version='5.1',
16411670
route_values=route_values,
16421671
query_parameters=query_parameters)
1643-
return self._deserialize('[GitRef]', self._unwrap_collection(response))
1672+
response_value = self._deserialize('[GitRef]', self._unwrap_collection(response))
1673+
continuation_token = self._get_continuation_token(response)
1674+
return self.GetRefsResponseValue(response_value, continuation_token)
1675+
1676+
class GetRefsResponseValue(object):
1677+
def __init__(self, value, continuation_token):
1678+
"""
1679+
Response for the get_refs method
1680+
1681+
:param value:
1682+
:type value: :class:`<[GitRef]> <azure.devops.v5_1.git.models.[GitRef]>`
1683+
:param continuation_token: The continuation token to be used to get the next page of results.
1684+
:type continuation_token: str
1685+
"""
1686+
self.value = value
1687+
self.continuation_token = continuation_token
16441688

16451689
def update_ref(self, new_ref_info, repository_id, filter, project=None, project_id=None):
16461690
"""UpdateRef.

azure-devops/azure/devops/released/policy/policy_client.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,28 +80,49 @@ def get_policy_configuration(self, project, configuration_id):
8080
route_values=route_values)
8181
return self._deserialize('PolicyConfiguration', response)
8282

83-
def get_policy_configurations(self, project, scope=None, policy_type=None):
83+
def get_policy_configurations(self, project, scope=None, top=None, continuation_token=None, policy_type=None):
8484
"""GetPolicyConfigurations.
8585
Get a list of policy configurations in a project.
8686
:param str project: Project ID or project name
8787
:param str scope: [Provided for legacy reasons] The scope on which a subset of policies is defined.
88+
:param int top: Maximum number of policies to return.
89+
:param str continuation_token: The continuation token used for pagination.
8890
:param str policy_type: Filter returned policies to only this type
89-
:rtype: [PolicyConfiguration]
91+
:rtype: :class:`<GetPolicyConfigurationsResponseValue>`
9092
"""
9193
route_values = {}
9294
if project is not None:
9395
route_values['project'] = self._serialize.url('project', project, 'str')
9496
query_parameters = {}
9597
if scope is not None:
9698
query_parameters['scope'] = self._serialize.query('scope', scope, 'str')
99+
if top is not None:
100+
query_parameters['$top'] = self._serialize.query('top', top, 'int')
101+
if continuation_token is not None:
102+
query_parameters['continuationToken'] = self._serialize.query('continuation_token', continuation_token, 'str')
97103
if policy_type is not None:
98104
query_parameters['policyType'] = self._serialize.query('policy_type', policy_type, 'str')
99105
response = self._send(http_method='GET',
100106
location_id='dad91cbe-d183-45f8-9c6e-9c1164472121',
101107
version='5.1',
102108
route_values=route_values,
103109
query_parameters=query_parameters)
104-
return self._deserialize('[PolicyConfiguration]', self._unwrap_collection(response))
110+
response_value = self._deserialize('[PolicyConfiguration]', self._unwrap_collection(response))
111+
continuation_token = self._get_continuation_token(response)
112+
return self.GetPolicyConfigurationsResponseValue(response_value, continuation_token)
113+
114+
class GetPolicyConfigurationsResponseValue(object):
115+
def __init__(self, value, continuation_token):
116+
"""
117+
Response for the get_policy_configurations method
118+
119+
:param value:
120+
:type value: :class:`<[PolicyConfiguration]> <azure.devops.v5_1.policy.models.[PolicyConfiguration]>`
121+
:param continuation_token: The continuation token to be used to get the next page of results.
122+
:type continuation_token: str
123+
"""
124+
self.value = value
125+
self.continuation_token = continuation_token
105126

106127
def update_policy_configuration(self, configuration, project, configuration_id):
107128
"""UpdatePolicyConfiguration.

0 commit comments

Comments
 (0)