117
117
'type' : 'string' ,
118
118
'required' : False ,
119
119
}
120
+ _PAGE_TOKEN_NAMES = ('pageToken' , 'nextPageToken' )
120
121
121
122
# Parameters accepted by the stack, but not visible via discovery.
122
123
# TODO(dhermes): Remove 'userip' in 'v2'.
@@ -724,7 +725,11 @@ def method(self, **kwargs):
724
725
725
726
for name in parameters .required_params :
726
727
if name not in kwargs :
727
- raise TypeError ('Missing required parameter "%s"' % name )
728
+ # temporary workaround for non-paging methods incorrectly requiring
729
+ # page token parameter (cf. drive.changes.watch vs. drive.changes.list)
730
+ if name not in _PAGE_TOKEN_NAMES or _findPageTokenName (
731
+ _methodProperties (methodDesc , schema , 'response' )):
732
+ raise TypeError ('Missing required parameter "%s"' % name )
728
733
729
734
for name , regex in six .iteritems (parameters .pattern_params ):
730
735
if name in kwargs :
@@ -927,13 +932,20 @@ def method(self, **kwargs):
927
932
return (methodName , method )
928
933
929
934
930
- def createNextMethod (methodName ):
935
+ def createNextMethod (methodName ,
936
+ pageTokenName = 'pageToken' ,
937
+ nextPageTokenName = 'nextPageToken' ,
938
+ isPageTokenParameter = True ):
931
939
"""Creates any _next methods for attaching to a Resource.
932
940
933
941
The _next methods allow for easy iteration through list() responses.
934
942
935
943
Args:
936
944
methodName: string, name of the method to use.
945
+ pageTokenName: string, name of request page token field.
946
+ nextPageTokenName: string, name of response page token field.
947
+ isPageTokenParameter: Boolean, True if request page token is a query
948
+ parameter, False if request page token is a field of the request body.
937
949
"""
938
950
methodName = fix_method_name (methodName )
939
951
@@ -951,24 +963,24 @@ def methodNext(self, previous_request, previous_response):
951
963
# Retrieve nextPageToken from previous_response
952
964
# Use as pageToken in previous_request to create new request.
953
965
954
- if 'nextPageToken' not in previous_response or not previous_response ['nextPageToken' ]:
966
+ nextPageToken = previous_response .get (nextPageTokenName , None )
967
+ if not nextPageToken :
955
968
return None
956
969
957
970
request = copy .copy (previous_request )
958
971
959
- pageToken = previous_response ['nextPageToken' ]
960
- parsed = list (urlparse (request .uri ))
961
- q = parse_qsl (parsed [4 ])
962
-
963
- # Find and remove old 'pageToken' value from URI
964
- newq = [(key , value ) for (key , value ) in q if key != 'pageToken' ]
965
- newq .append (('pageToken' , pageToken ))
966
- parsed [4 ] = urlencode (newq )
967
- uri = urlunparse (parsed )
968
-
969
- request .uri = uri
970
-
971
- logger .info ('URL being requested: %s %s' % (methodName ,uri ))
972
+ if isPageTokenParameter :
973
+ # Replace pageToken value in URI
974
+ request .uri = _add_query_parameter (
975
+ request .uri , pageTokenName , nextPageToken )
976
+ logger .info ('Next page request URL: %s %s' % (methodName , request .uri ))
977
+ else :
978
+ # Replace pageToken value in request body
979
+ model = self ._model
980
+ body = model .deserialize (request .body )
981
+ body [pageTokenName ] = nextPageToken
982
+ request .body = model .serialize (body )
983
+ logger .info ('Next page request body: %s %s' % (methodName , body ))
972
984
973
985
return request
974
986
@@ -1116,19 +1128,59 @@ def methodResource(self):
1116
1128
method .__get__ (self , self .__class__ ))
1117
1129
1118
1130
def _add_next_methods (self , resourceDesc , schema ):
1119
- # Add _next() methods
1120
- # Look for response bodies in schema that contain nextPageToken, and methods
1121
- # that take a pageToken parameter.
1122
- if 'methods' in resourceDesc :
1123
- for methodName , methodDesc in six .iteritems (resourceDesc ['methods' ]):
1124
- if 'response' in methodDesc :
1125
- responseSchema = methodDesc ['response' ]
1126
- if '$ref' in responseSchema :
1127
- responseSchema = schema .get (responseSchema ['$ref' ])
1128
- hasNextPageToken = 'nextPageToken' in responseSchema .get ('properties' ,
1129
- {})
1130
- hasPageToken = 'pageToken' in methodDesc .get ('parameters' , {})
1131
- if hasNextPageToken and hasPageToken :
1132
- fixedMethodName , method = createNextMethod (methodName + '_next' )
1133
- self ._set_dynamic_attr (fixedMethodName ,
1134
- method .__get__ (self , self .__class__ ))
1131
+ # Add _next() methods if and only if one of the names 'pageToken' or
1132
+ # 'nextPageToken' occurs among the fields of both the method's response
1133
+ # type either the method's request (query parameters) or request body.
1134
+ if 'methods' not in resourceDesc :
1135
+ return
1136
+ for methodName , methodDesc in six .iteritems (resourceDesc ['methods' ]):
1137
+ nextPageTokenName = _findPageTokenName (
1138
+ _methodProperties (methodDesc , schema , 'response' ))
1139
+ if not nextPageTokenName :
1140
+ continue
1141
+ isPageTokenParameter = True
1142
+ pageTokenName = _findPageTokenName (methodDesc .get ('parameters' , {}))
1143
+ if not pageTokenName :
1144
+ isPageTokenParameter = False
1145
+ pageTokenName = _findPageTokenName (
1146
+ _methodProperties (methodDesc , schema , 'request' ))
1147
+ if not pageTokenName :
1148
+ continue
1149
+ fixedMethodName , method = createNextMethod (
1150
+ methodName + '_next' , pageTokenName , nextPageTokenName ,
1151
+ isPageTokenParameter )
1152
+ self ._set_dynamic_attr (fixedMethodName ,
1153
+ method .__get__ (self , self .__class__ ))
1154
+
1155
+
1156
+ def _findPageTokenName (fields ):
1157
+ """Search field names for one like a page token.
1158
+
1159
+ Args:
1160
+ fields: container of string, names of fields.
1161
+
1162
+ Returns:
1163
+ First name that is either 'pageToken' or 'nextPageToken' if one exists,
1164
+ otherwise None.
1165
+ """
1166
+ return next ((tokenName for tokenName in _PAGE_TOKEN_NAMES
1167
+ if tokenName in fields ), None )
1168
+
1169
+ def _methodProperties (methodDesc , schema , name ):
1170
+ """Get properties of a field in a method description.
1171
+
1172
+ Args:
1173
+ methodDesc: object, fragment of deserialized discovery document that
1174
+ describes the method.
1175
+ schema: object, mapping of schema names to schema descriptions.
1176
+ name: string, name of top-level field in method description.
1177
+
1178
+ Returns:
1179
+ Object representing fragment of deserialized discovery document
1180
+ corresponding to 'properties' field of object corresponding to named field
1181
+ in method description, if it exists, otherwise empty dict.
1182
+ """
1183
+ desc = methodDesc .get (name , {})
1184
+ if '$ref' in desc :
1185
+ desc = schema .get (desc ['$ref' ], {})
1186
+ return desc .get ('properties' , {})
0 commit comments