| OLD | NEW |
| (Empty) | |
| 1 # Copyright (c) 2012 Andy Davidoff http://www.disruptek.com/ |
| 2 # |
| 3 # Permission is hereby granted, free of charge, to any person obtaining a |
| 4 # copy of this software and associated documentation files (the |
| 5 # "Software"), to deal in the Software without restriction, including |
| 6 # without limitation the rights to use, copy, modify, merge, publish, dis- |
| 7 # tribute, sublicense, and/or sell copies of the Software, and to permit |
| 8 # persons to whom the Software is furnished to do so, subject to the fol- |
| 9 # lowing conditions: |
| 10 # |
| 11 # The above copyright notice and this permission notice shall be included |
| 12 # in all copies or substantial portions of the Software. |
| 13 # |
| 14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| 15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- |
| 16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
| 17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| 18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 20 # IN THE SOFTWARE. |
| 21 import xml.sax |
| 22 import hashlib |
| 23 import base64 |
| 24 import string |
| 25 from boto.connection import AWSQueryConnection |
| 26 from boto.mws.exception import ResponseErrorFactory |
| 27 from boto.mws.response import ResponseFactory, ResponseElement |
| 28 from boto.handler import XmlHandler |
| 29 import boto.mws.response |
| 30 |
| 31 __all__ = ['MWSConnection'] |
| 32 |
| 33 api_version_path = { |
| 34 'Feeds': ('2009-01-01', 'Merchant', '/'), |
| 35 'Reports': ('2009-01-01', 'Merchant', '/'), |
| 36 'Orders': ('2011-01-01', 'SellerId', '/Orders/2011-01-01'), |
| 37 'Products': ('2011-10-01', 'SellerId', '/Products/2011-10-01'), |
| 38 'Sellers': ('2011-07-01', 'SellerId', '/Sellers/2011-07-01'), |
| 39 'Inbound': ('2010-10-01', 'SellerId', |
| 40 '/FulfillmentInboundShipment/2010-10-01'), |
| 41 'Outbound': ('2010-10-01', 'SellerId', |
| 42 '/FulfillmentOutboundShipment/2010-10-01'), |
| 43 'Inventory': ('2010-10-01', 'SellerId', |
| 44 '/FulfillmentInventory/2010-10-01'), |
| 45 } |
| 46 content_md5 = lambda c: base64.encodestring(hashlib.md5(c).digest()).strip() |
| 47 decorated_attrs = ('action', 'response', 'section', |
| 48 'quota', 'restore', 'version') |
| 49 |
| 50 |
| 51 def add_attrs_from(func, to): |
| 52 for attr in decorated_attrs: |
| 53 setattr(to, attr, getattr(func, attr, None)) |
| 54 return to |
| 55 |
| 56 |
| 57 def structured_lists(*fields): |
| 58 |
| 59 def decorator(func): |
| 60 |
| 61 def wrapper(self, *args, **kw): |
| 62 for key, acc in [f.split('.') for f in fields]: |
| 63 if key in kw: |
| 64 newkey = key + '.' + acc + (acc and '.' or '') |
| 65 for i in range(len(kw[key])): |
| 66 kw[newkey + str(i + 1)] = kw[key][i] |
| 67 kw.pop(key) |
| 68 return func(self, *args, **kw) |
| 69 wrapper.__doc__ = "{0}\nLists: {1}".format(func.__doc__, |
| 70 ', '.join(fields)) |
| 71 return add_attrs_from(func, to=wrapper) |
| 72 return decorator |
| 73 |
| 74 |
| 75 def http_body(field): |
| 76 |
| 77 def decorator(func): |
| 78 |
| 79 def wrapper(*args, **kw): |
| 80 if filter(lambda x: not x in kw, (field, 'content_type')): |
| 81 message = "{0} requires {1} and content_type arguments for " \ |
| 82 "building HTTP body".format(func.action, field) |
| 83 raise KeyError(message) |
| 84 kw['body'] = kw.pop(field) |
| 85 kw['headers'] = { |
| 86 'Content-Type': kw.pop('content_type'), |
| 87 'Content-MD5': content_md5(kw['body']), |
| 88 } |
| 89 return func(*args, **kw) |
| 90 wrapper.__doc__ = "{0}\nRequired HTTP Body: " \ |
| 91 "{1}".format(func.__doc__, field) |
| 92 return add_attrs_from(func, to=wrapper) |
| 93 return decorator |
| 94 |
| 95 |
| 96 def destructure_object(value, into={}, prefix=''): |
| 97 if isinstance(value, ResponseElement): |
| 98 for name, attr in value.__dict__.items(): |
| 99 if name.startswith('_'): |
| 100 continue |
| 101 destructure_object(attr, into=into, prefix=prefix + '.' + name) |
| 102 elif filter(lambda x: isinstance(value, x), (list, set, tuple)): |
| 103 for index, element in [(prefix + '.' + str(i + 1), value[i]) |
| 104 for i in range(len(value))]: |
| 105 destructure_object(element, into=into, prefix=index) |
| 106 elif isinstance(value, bool): |
| 107 into[prefix] = str(value).lower() |
| 108 else: |
| 109 into[prefix] = value |
| 110 |
| 111 |
| 112 def structured_objects(*fields): |
| 113 |
| 114 def decorator(func): |
| 115 |
| 116 def wrapper(*args, **kw): |
| 117 for field in filter(kw.has_key, fields): |
| 118 destructure_object(kw.pop(field), into=kw, prefix=field) |
| 119 return func(*args, **kw) |
| 120 wrapper.__doc__ = "{0}\nObjects: {1}".format(func.__doc__, |
| 121 ', '.join(fields)) |
| 122 return add_attrs_from(func, to=wrapper) |
| 123 return decorator |
| 124 |
| 125 |
| 126 def requires(*groups): |
| 127 |
| 128 def decorator(func): |
| 129 |
| 130 def wrapper(*args, **kw): |
| 131 hasgroup = lambda x: len(x) == len(filter(kw.has_key, x)) |
| 132 if 1 != len(filter(hasgroup, groups)): |
| 133 message = ' OR '.join(['+'.join(g) for g in groups]) |
| 134 message = "{0} requires {1} argument(s)" \ |
| 135 "".format(func.action, message) |
| 136 raise KeyError(message) |
| 137 return func(*args, **kw) |
| 138 message = ' OR '.join(['+'.join(g) for g in groups]) |
| 139 wrapper.__doc__ = "{0}\nRequired: {1}".format(func.__doc__, |
| 140 message) |
| 141 return add_attrs_from(func, to=wrapper) |
| 142 return decorator |
| 143 |
| 144 |
| 145 def exclusive(*groups): |
| 146 |
| 147 def decorator(func): |
| 148 |
| 149 def wrapper(*args, **kw): |
| 150 hasgroup = lambda x: len(x) == len(filter(kw.has_key, x)) |
| 151 if len(filter(hasgroup, groups)) not in (0, 1): |
| 152 message = ' OR '.join(['+'.join(g) for g in groups]) |
| 153 message = "{0} requires either {1}" \ |
| 154 "".format(func.action, message) |
| 155 raise KeyError(message) |
| 156 return func(*args, **kw) |
| 157 message = ' OR '.join(['+'.join(g) for g in groups]) |
| 158 wrapper.__doc__ = "{0}\nEither: {1}".format(func.__doc__, |
| 159 message) |
| 160 return add_attrs_from(func, to=wrapper) |
| 161 return decorator |
| 162 |
| 163 |
| 164 def dependent(field, *groups): |
| 165 |
| 166 def decorator(func): |
| 167 |
| 168 def wrapper(*args, **kw): |
| 169 hasgroup = lambda x: len(x) == len(filter(kw.has_key, x)) |
| 170 if field in kw and 1 > len(filter(hasgroup, groups)): |
| 171 message = ' OR '.join(['+'.join(g) for g in groups]) |
| 172 message = "{0} argument {1} requires {2}" \ |
| 173 "".format(func.action, field, message) |
| 174 raise KeyError(message) |
| 175 return func(*args, **kw) |
| 176 message = ' OR '.join(['+'.join(g) for g in groups]) |
| 177 wrapper.__doc__ = "{0}\n{1} requires: {2}".format(func.__doc__, |
| 178 field, |
| 179 message) |
| 180 return add_attrs_from(func, to=wrapper) |
| 181 return decorator |
| 182 |
| 183 |
| 184 def requires_some_of(*fields): |
| 185 |
| 186 def decorator(func): |
| 187 |
| 188 def wrapper(*args, **kw): |
| 189 if not filter(kw.has_key, fields): |
| 190 message = "{0} requires at least one of {1} argument(s)" \ |
| 191 "".format(func.action, ', '.join(fields)) |
| 192 raise KeyError(message) |
| 193 return func(*args, **kw) |
| 194 wrapper.__doc__ = "{0}\nSome Required: {1}".format(func.__doc__, |
| 195 ', '.join(fields)) |
| 196 return add_attrs_from(func, to=wrapper) |
| 197 return decorator |
| 198 |
| 199 |
| 200 def boolean_arguments(*fields): |
| 201 |
| 202 def decorator(func): |
| 203 |
| 204 def wrapper(*args, **kw): |
| 205 for field in filter(lambda x: isinstance(kw.get(x), bool), fields): |
| 206 kw[field] = str(kw[field]).lower() |
| 207 return func(*args, **kw) |
| 208 wrapper.__doc__ = "{0}\nBooleans: {1}".format(func.__doc__, |
| 209 ', '.join(fields)) |
| 210 return add_attrs_from(func, to=wrapper) |
| 211 return decorator |
| 212 |
| 213 |
| 214 def api_action(section, quota, restore, *api): |
| 215 |
| 216 def decorator(func, quota=int(quota), restore=float(restore)): |
| 217 version, accesskey, path = api_version_path[section] |
| 218 action = ''.join(api or map(str.capitalize, func.func_name.split('_'))) |
| 219 if hasattr(boto.mws.response, action + 'Response'): |
| 220 response = getattr(boto.mws.response, action + 'Response') |
| 221 else: |
| 222 response = ResponseFactory(action) |
| 223 response._action = action |
| 224 |
| 225 def wrapper(self, *args, **kw): |
| 226 kw.setdefault(accesskey, getattr(self, accesskey, None)) |
| 227 if kw[accesskey] is None: |
| 228 message = "{0} requires {1} argument. Set the " \ |
| 229 "MWSConnection.{2} attribute?" \ |
| 230 "".format(action, accesskey, accesskey) |
| 231 raise KeyError(message) |
| 232 kw['Action'] = action |
| 233 kw['Version'] = version |
| 234 return func(self, path, response, *args, **kw) |
| 235 for attr in decorated_attrs: |
| 236 setattr(wrapper, attr, locals().get(attr)) |
| 237 wrapper.__doc__ = "MWS {0}/{1} API call; quota={2} restore={3:.2f}\n" \ |
| 238 "{4}".format(action, version, quota, restore, |
| 239 func.__doc__) |
| 240 return wrapper |
| 241 return decorator |
| 242 |
| 243 |
| 244 class MWSConnection(AWSQueryConnection): |
| 245 |
| 246 ResponseError = ResponseErrorFactory |
| 247 |
| 248 def __init__(self, *args, **kw): |
| 249 kw.setdefault('host', 'mws.amazonservices.com') |
| 250 self.Merchant = kw.pop('Merchant', None) or kw.get('SellerId') |
| 251 self.SellerId = kw.pop('SellerId', None) or self.Merchant |
| 252 AWSQueryConnection.__init__(self, *args, **kw) |
| 253 |
| 254 def _required_auth_capability(self): |
| 255 return ['mws'] |
| 256 |
| 257 def post_request(self, path, params, cls, body='', headers={}, isXML=True): |
| 258 """Make a POST request, optionally with a content body, |
| 259 and return the response, optionally as raw text. |
| 260 Modelled off of the inherited get_object/make_request flow. |
| 261 """ |
| 262 request = self.build_base_http_request('POST', path, None, data=body, |
| 263 params=params, headers=headers, host=self.server_name()) |
| 264 response = self._mexe(request, override_num_retries=None) |
| 265 body = response.read() |
| 266 boto.log.debug(body) |
| 267 if not body: |
| 268 boto.log.error('Null body %s' % body) |
| 269 raise self.ResponseError(response.status, response.reason, body) |
| 270 if response.status != 200: |
| 271 boto.log.error('%s %s' % (response.status, response.reason)) |
| 272 boto.log.error('%s' % body) |
| 273 raise self.ResponseError(response.status, response.reason, body) |
| 274 if not isXML: |
| 275 digest = response.getheader('Content-MD5') |
| 276 assert content_md5(body) == digest |
| 277 return body |
| 278 obj = cls(self) |
| 279 h = XmlHandler(obj, self) |
| 280 xml.sax.parseString(body, h) |
| 281 return obj |
| 282 |
| 283 def method_for(self, name): |
| 284 """Return the MWS API method referred to in the argument. |
| 285 The named method can be in CamelCase or underlined_lower_case. |
| 286 This is the complement to MWSConnection.any_call.action |
| 287 """ |
| 288 # this looks ridiculous but it should be better than regex |
| 289 action = '_' in name and string.capwords(name, '_') or name |
| 290 attribs = [getattr(self, m) for m in dir(self)] |
| 291 ismethod = lambda m: type(m) is type(self.method_for) |
| 292 ismatch = lambda m: getattr(m, 'action', None) == action |
| 293 method = filter(ismatch, filter(ismethod, attribs)) |
| 294 return method and method[0] or None |
| 295 |
| 296 def iter_call(self, call, *args, **kw): |
| 297 """Pass a call name as the first argument and a generator |
| 298 is returned for the initial response and any continuation |
| 299 call responses made using the NextToken. |
| 300 """ |
| 301 method = self.method_for(call) |
| 302 assert method, 'No call named "{0}"'.format(call) |
| 303 return self.iter_response(method(*args, **kw)) |
| 304 |
| 305 def iter_response(self, response): |
| 306 """Pass a call's response as the initial argument and a |
| 307 generator is returned for the initial response and any |
| 308 continuation call responses made using the NextToken. |
| 309 """ |
| 310 yield response |
| 311 more = self.method_for(response._action + 'ByNextToken') |
| 312 while more and response._result.HasNext == 'true': |
| 313 response = more(NextToken=response._result.NextToken) |
| 314 yield response |
| 315 |
| 316 @boolean_arguments('PurgeAndReplace') |
| 317 @http_body('FeedContent') |
| 318 @structured_lists('MarketplaceIdList.Id') |
| 319 @requires(['FeedType']) |
| 320 @api_action('Feeds', 15, 120) |
| 321 def submit_feed(self, path, response, headers={}, body='', **kw): |
| 322 """Uploads a feed for processing by Amazon MWS. |
| 323 """ |
| 324 return self.post_request(path, kw, response, body=body, |
| 325 headers=headers) |
| 326 |
| 327 @structured_lists('FeedSubmissionIdList.Id', 'FeedTypeList.Type', |
| 328 'FeedProcessingStatusList.Status') |
| 329 @api_action('Feeds', 10, 45) |
| 330 def get_feed_submission_list(self, path, response, **kw): |
| 331 """Returns a list of all feed submissions submitted in the |
| 332 previous 90 days. |
| 333 """ |
| 334 return self.post_request(path, kw, response) |
| 335 |
| 336 @requires(['NextToken']) |
| 337 @api_action('Feeds', 0, 0) |
| 338 def get_feed_submission_list_by_next_token(self, path, response, **kw): |
| 339 """Returns a list of feed submissions using the NextToken parameter. |
| 340 """ |
| 341 return self.post_request(path, kw, response) |
| 342 |
| 343 @structured_lists('FeedTypeList.Type', 'FeedProcessingStatusList.Status') |
| 344 @api_action('Feeds', 10, 45) |
| 345 def get_feed_submission_count(self, path, response, **kw): |
| 346 """Returns a count of the feeds submitted in the previous 90 days. |
| 347 """ |
| 348 return self.post_request(path, kw, response) |
| 349 |
| 350 @structured_lists('FeedSubmissionIdList.Id', 'FeedTypeList.Type') |
| 351 @api_action('Feeds', 10, 45) |
| 352 def cancel_feed_submissions(self, path, response, **kw): |
| 353 """Cancels one or more feed submissions and returns a |
| 354 count of the feed submissions that were canceled. |
| 355 """ |
| 356 return self.post_request(path, kw, response) |
| 357 |
| 358 @requires(['FeedSubmissionId']) |
| 359 @api_action('Feeds', 15, 60) |
| 360 def get_feed_submission_result(self, path, response, **kw): |
| 361 """Returns the feed processing report. |
| 362 """ |
| 363 return self.post_request(path, kw, response, isXML=False) |
| 364 |
| 365 def get_service_status(self, **kw): |
| 366 """Instruct the user on how to get service status. |
| 367 """ |
| 368 message = "Use {0}.get_(section)_service_status(), " \ |
| 369 "where (section) is one of the following: " \ |
| 370 "{1}".format(self.__class__.__name__, |
| 371 ', '.join(map(str.lower, api_version_path.keys()))) |
| 372 raise AttributeError(message) |
| 373 |
| 374 @structured_lists('MarketplaceIdList.Id') |
| 375 @boolean_arguments('ReportOptions=ShowSalesChannel') |
| 376 @requires(['ReportType']) |
| 377 @api_action('Reports', 15, 60) |
| 378 def request_report(self, path, response, **kw): |
| 379 """Creates a report request and submits the request to Amazon MWS. |
| 380 """ |
| 381 return self.post_request(path, kw, response) |
| 382 |
| 383 @structured_lists('ReportRequestIdList.Id', 'ReportTypeList.Type', |
| 384 'ReportProcessingStatusList.Status') |
| 385 @api_action('Reports', 10, 45) |
| 386 def get_report_request_list(self, path, response, **kw): |
| 387 """Returns a list of report requests that you can use to get the |
| 388 ReportRequestId for a report. |
| 389 """ |
| 390 return self.post_request(path, kw, response) |
| 391 |
| 392 @requires(['NextToken']) |
| 393 @api_action('Reports', 0, 0) |
| 394 def get_report_request_list_by_next_token(self, path, response, **kw): |
| 395 """Returns a list of report requests using the NextToken, |
| 396 which was supplied by a previous request to either |
| 397 GetReportRequestListByNextToken or GetReportRequestList, where |
| 398 the value of HasNext was true in that previous request. |
| 399 """ |
| 400 return self.post_request(path, kw, response) |
| 401 |
| 402 @structured_lists('ReportTypeList.Type', |
| 403 'ReportProcessingStatusList.Status') |
| 404 @api_action('Reports', 10, 45) |
| 405 def get_report_request_count(self, path, response, **kw): |
| 406 """Returns a count of report requests that have been submitted |
| 407 to Amazon MWS for processing. |
| 408 """ |
| 409 return self.post_request(path, kw, response) |
| 410 |
| 411 @api_action('Reports', 10, 45) |
| 412 def cancel_report_requests(self, path, response, **kw): |
| 413 """Cancel one or more report requests, returning the count of the |
| 414 canceled report requests and the report request information. |
| 415 """ |
| 416 return self.post_request(path, kw, response) |
| 417 |
| 418 @boolean_arguments('Acknowledged') |
| 419 @structured_lists('ReportRequestIdList.Id', 'ReportTypeList.Type') |
| 420 @api_action('Reports', 10, 60) |
| 421 def get_report_list(self, path, response, **kw): |
| 422 """Returns a list of reports that were created in the previous |
| 423 90 days that match the query parameters. |
| 424 """ |
| 425 return self.post_request(path, kw, response) |
| 426 |
| 427 @requires(['NextToken']) |
| 428 @api_action('Reports', 0, 0) |
| 429 def get_report_list_by_next_token(self, path, response, **kw): |
| 430 """Returns a list of reports using the NextToken, which |
| 431 was supplied by a previous request to either |
| 432 GetReportListByNextToken or GetReportList, where the |
| 433 value of HasNext was true in the previous call. |
| 434 """ |
| 435 return self.post_request(path, kw, response) |
| 436 |
| 437 @boolean_arguments('Acknowledged') |
| 438 @structured_lists('ReportTypeList.Type') |
| 439 @api_action('Reports', 10, 45) |
| 440 def get_report_count(self, path, response, **kw): |
| 441 """Returns a count of the reports, created in the previous 90 days, |
| 442 with a status of _DONE_ and that are available for download. |
| 443 """ |
| 444 return self.post_request(path, kw, response) |
| 445 |
| 446 @requires(['ReportId']) |
| 447 @api_action('Reports', 15, 60) |
| 448 def get_report(self, path, response, **kw): |
| 449 """Returns the contents of a report. |
| 450 """ |
| 451 return self.post_request(path, kw, response, isXML=False) |
| 452 |
| 453 @requires(['ReportType', 'Schedule']) |
| 454 @api_action('Reports', 10, 45) |
| 455 def manage_report_schedule(self, path, response, **kw): |
| 456 """Creates, updates, or deletes a report request schedule for |
| 457 a specified report type. |
| 458 """ |
| 459 return self.post_request(path, kw, response) |
| 460 |
| 461 @structured_lists('ReportTypeList.Type') |
| 462 @api_action('Reports', 10, 45) |
| 463 def get_report_schedule_list(self, path, response, **kw): |
| 464 """Returns a list of order report requests that are scheduled |
| 465 to be submitted to Amazon MWS for processing. |
| 466 """ |
| 467 return self.post_request(path, kw, response) |
| 468 |
| 469 @requires(['NextToken']) |
| 470 @api_action('Reports', 0, 0) |
| 471 def get_report_schedule_list_by_next_token(self, path, response, **kw): |
| 472 """Returns a list of report requests using the NextToken, |
| 473 which was supplied by a previous request to either |
| 474 GetReportScheduleListByNextToken or GetReportScheduleList, |
| 475 where the value of HasNext was true in that previous request. |
| 476 """ |
| 477 return self.post_request(path, kw, response) |
| 478 |
| 479 @structured_lists('ReportTypeList.Type') |
| 480 @api_action('Reports', 10, 45) |
| 481 def get_report_schedule_count(self, path, response, **kw): |
| 482 """Returns a count of order report requests that are scheduled |
| 483 to be submitted to Amazon MWS. |
| 484 """ |
| 485 return self.post_request(path, kw, response) |
| 486 |
| 487 @boolean_arguments('Acknowledged') |
| 488 @requires(['ReportIdList']) |
| 489 @structured_lists('ReportIdList.Id') |
| 490 @api_action('Reports', 10, 45) |
| 491 def update_report_acknowledgements(self, path, response, **kw): |
| 492 """Updates the acknowledged status of one or more reports. |
| 493 """ |
| 494 return self.post_request(path, kw, response) |
| 495 |
| 496 @requires(['ShipFromAddress', 'InboundShipmentPlanRequestItems']) |
| 497 @structured_objects('ShipFromAddress', 'InboundShipmentPlanRequestItems') |
| 498 @api_action('Inbound', 30, 0.5) |
| 499 def create_inbound_shipment_plan(self, path, response, **kw): |
| 500 """Returns the information required to create an inbound shipment. |
| 501 """ |
| 502 return self.post_request(path, kw, response) |
| 503 |
| 504 @requires(['ShipmentId', 'InboundShipmentHeader', 'InboundShipmentItems']) |
| 505 @structured_objects('InboundShipmentHeader', 'InboundShipmentItems') |
| 506 @api_action('Inbound', 30, 0.5) |
| 507 def create_inbound_shipment(self, path, response, **kw): |
| 508 """Creates an inbound shipment. |
| 509 """ |
| 510 return self.post_request(path, kw, response) |
| 511 |
| 512 @requires(['ShipmentId']) |
| 513 @structured_objects('InboundShipmentHeader', 'InboundShipmentItems') |
| 514 @api_action('Inbound', 30, 0.5) |
| 515 def update_inbound_shipment(self, path, response, **kw): |
| 516 """Updates an existing inbound shipment. Amazon documentation |
| 517 is ambiguous as to whether the InboundShipmentHeader and |
| 518 InboundShipmentItems arguments are required. |
| 519 """ |
| 520 return self.post_request(path, kw, response) |
| 521 |
| 522 @requires_some_of('ShipmentIdList', 'ShipmentStatusList') |
| 523 @structured_lists('ShipmentIdList.Id', 'ShipmentStatusList.Status') |
| 524 @api_action('Inbound', 30, 0.5) |
| 525 def list_inbound_shipments(self, path, response, **kw): |
| 526 """Returns a list of inbound shipments based on criteria that |
| 527 you specify. |
| 528 """ |
| 529 return self.post_request(path, kw, response) |
| 530 |
| 531 @requires(['NextToken']) |
| 532 @api_action('Inbound', 30, 0.5) |
| 533 def list_inbound_shipments_by_next_token(self, path, response, **kw): |
| 534 """Returns the next page of inbound shipments using the NextToken |
| 535 parameter. |
| 536 """ |
| 537 return self.post_request(path, kw, response) |
| 538 |
| 539 @requires(['ShipmentId'], ['LastUpdatedAfter', 'LastUpdatedBefore']) |
| 540 @api_action('Inbound', 30, 0.5) |
| 541 def list_inbound_shipment_items(self, path, response, **kw): |
| 542 """Returns a list of items in a specified inbound shipment, or a |
| 543 list of items that were updated within a specified time frame. |
| 544 """ |
| 545 return self.post_request(path, kw, response) |
| 546 |
| 547 @requires(['NextToken']) |
| 548 @api_action('Inbound', 30, 0.5) |
| 549 def list_inbound_shipment_items_by_next_token(self, path, response, **kw): |
| 550 """Returns the next page of inbound shipment items using the |
| 551 NextToken parameter. |
| 552 """ |
| 553 return self.post_request(path, kw, response) |
| 554 |
| 555 @api_action('Inbound', 2, 300, 'GetServiceStatus') |
| 556 def get_inbound_service_status(self, path, response, **kw): |
| 557 """Returns the operational status of the Fulfillment Inbound |
| 558 Shipment API section. |
| 559 """ |
| 560 return self.post_request(path, kw, response) |
| 561 |
| 562 @requires(['SellerSkus'], ['QueryStartDateTime']) |
| 563 @structured_lists('SellerSkus.member') |
| 564 @api_action('Inventory', 30, 0.5) |
| 565 def list_inventory_supply(self, path, response, **kw): |
| 566 """Returns information about the availability of a seller's |
| 567 inventory. |
| 568 """ |
| 569 return self.post_request(path, kw, response) |
| 570 |
| 571 @requires(['NextToken']) |
| 572 @api_action('Inventory', 30, 0.5) |
| 573 def list_inventory_supply_by_next_token(self, path, response, **kw): |
| 574 """Returns the next page of information about the availability |
| 575 of a seller's inventory using the NextToken parameter. |
| 576 """ |
| 577 return self.post_request(path, kw, response) |
| 578 |
| 579 @api_action('Inventory', 2, 300, 'GetServiceStatus') |
| 580 def get_inventory_service_status(self, path, response, **kw): |
| 581 """Returns the operational status of the Fulfillment Inventory |
| 582 API section. |
| 583 """ |
| 584 return self.post_request(path, kw, response) |
| 585 |
| 586 @structured_objects('Address', 'Items') |
| 587 @requires(['Address', 'Items']) |
| 588 @api_action('Outbound', 30, 0.5) |
| 589 def get_fulfillment_preview(self, path, response, **kw): |
| 590 """Returns a list of fulfillment order previews based on items |
| 591 and shipping speed categories that you specify. |
| 592 """ |
| 593 return self.post_request(path, kw, response) |
| 594 |
| 595 @structured_objects('DestinationAddress', 'Items') |
| 596 @requires(['SellerFulfillmentOrderId', 'DisplayableOrderId', |
| 597 'ShippingSpeedCategory', 'DisplayableOrderDateTime', |
| 598 'DestinationAddress', 'DisplayableOrderComment', |
| 599 'Items']) |
| 600 @api_action('Outbound', 30, 0.5) |
| 601 def create_fulfillment_order(self, path, response, **kw): |
| 602 """Requests that Amazon ship items from the seller's inventory |
| 603 to a destination address. |
| 604 """ |
| 605 return self.post_request(path, kw, response) |
| 606 |
| 607 @requires(['SellerFulfillmentOrderId']) |
| 608 @api_action('Outbound', 30, 0.5) |
| 609 def get_fulfillment_order(self, path, response, **kw): |
| 610 """Returns a fulfillment order based on a specified |
| 611 SellerFulfillmentOrderId. |
| 612 """ |
| 613 return self.post_request(path, kw, response) |
| 614 |
| 615 @api_action('Outbound', 30, 0.5) |
| 616 def list_all_fulfillment_orders(self, path, response, **kw): |
| 617 """Returns a list of fulfillment orders fulfilled after (or |
| 618 at) a specified date or by fulfillment method. |
| 619 """ |
| 620 return self.post_request(path, kw, response) |
| 621 |
| 622 @requires(['NextToken']) |
| 623 @api_action('Outbound', 30, 0.5) |
| 624 def list_all_fulfillment_orders_by_next_token(self, path, response, **kw): |
| 625 """Returns the next page of inbound shipment items using the |
| 626 NextToken parameter. |
| 627 """ |
| 628 return self.post_request(path, kw, response) |
| 629 |
| 630 @requires(['SellerFulfillmentOrderId']) |
| 631 @api_action('Outbound', 30, 0.5) |
| 632 def cancel_fulfillment_order(self, path, response, **kw): |
| 633 """Requests that Amazon stop attempting to fulfill an existing |
| 634 fulfillment order. |
| 635 """ |
| 636 return self.post_request(path, kw, response) |
| 637 |
| 638 @api_action('Outbound', 2, 300, 'GetServiceStatus') |
| 639 def get_outbound_service_status(self, path, response, **kw): |
| 640 """Returns the operational status of the Fulfillment Outbound |
| 641 API section. |
| 642 """ |
| 643 return self.post_request(path, kw, response) |
| 644 |
| 645 @requires(['CreatedAfter'], ['LastUpdatedAfter']) |
| 646 @exclusive(['CreatedAfter'], ['LastUpdatedAfter']) |
| 647 @dependent('CreatedBefore', ['CreatedAfter']) |
| 648 @exclusive(['LastUpdatedAfter'], ['BuyerEmail'], ['SellerOrderId']) |
| 649 @dependent('LastUpdatedBefore', ['LastUpdatedAfter']) |
| 650 @exclusive(['CreatedAfter'], ['LastUpdatedBefore']) |
| 651 @requires(['MarketplaceId']) |
| 652 @structured_objects('OrderTotal', 'ShippingAddress', |
| 653 'PaymentExecutionDetail') |
| 654 @structured_lists('MarketplaceId.Id', 'OrderStatus.Status', |
| 655 'FulfillmentChannel.Channel', 'PaymentMethod.') |
| 656 @api_action('Orders', 6, 60) |
| 657 def list_orders(self, path, response, **kw): |
| 658 """Returns a list of orders created or updated during a time |
| 659 frame that you specify. |
| 660 """ |
| 661 toggle = set(('FulfillmentChannel.Channel.1', |
| 662 'OrderStatus.Status.1', 'PaymentMethod.1', |
| 663 'LastUpdatedAfter', 'LastUpdatedBefore')) |
| 664 for do, dont in { |
| 665 'BuyerEmail': toggle.union(['SellerOrderId']), |
| 666 'SellerOrderId': toggle.union(['BuyerEmail']), |
| 667 }.items(): |
| 668 if do in kw and filter(kw.has_key, dont): |
| 669 message = "Don't include {0} when specifying " \ |
| 670 "{1}".format(' or '.join(dont), do) |
| 671 raise AssertionError(message) |
| 672 return self.post_request(path, kw, response) |
| 673 |
| 674 @requires(['NextToken']) |
| 675 @api_action('Orders', 6, 60) |
| 676 def list_orders_by_next_token(self, path, response, **kw): |
| 677 """Returns the next page of orders using the NextToken value |
| 678 that was returned by your previous request to either |
| 679 ListOrders or ListOrdersByNextToken. |
| 680 """ |
| 681 return self.post_request(path, kw, response) |
| 682 |
| 683 @requires(['AmazonOrderId']) |
| 684 @structured_lists('AmazonOrderId.Id') |
| 685 @api_action('Orders', 6, 60) |
| 686 def get_order(self, path, response, **kw): |
| 687 """Returns an order for each AmazonOrderId that you specify. |
| 688 """ |
| 689 return self.post_request(path, kw, response) |
| 690 |
| 691 @requires(['AmazonOrderId']) |
| 692 @api_action('Orders', 30, 2) |
| 693 def list_order_items(self, path, response, **kw): |
| 694 """Returns order item information for an AmazonOrderId that |
| 695 you specify. |
| 696 """ |
| 697 return self.post_request(path, kw, response) |
| 698 |
| 699 @requires(['NextToken']) |
| 700 @api_action('Orders', 30, 2) |
| 701 def list_order_items_by_next_token(self, path, response, **kw): |
| 702 """Returns the next page of order items using the NextToken |
| 703 value that was returned by your previous request to either |
| 704 ListOrderItems or ListOrderItemsByNextToken. |
| 705 """ |
| 706 return self.post_request(path, kw, response) |
| 707 |
| 708 @api_action('Orders', 2, 300, 'GetServiceStatus') |
| 709 def get_orders_service_status(self, path, response, **kw): |
| 710 """Returns the operational status of the Orders API section. |
| 711 """ |
| 712 return self.post_request(path, kw, response) |
| 713 |
| 714 @requires(['MarketplaceId', 'Query']) |
| 715 @api_action('Products', 20, 20) |
| 716 def list_matching_products(self, path, response, **kw): |
| 717 """Returns a list of products and their attributes, ordered |
| 718 by relevancy, based on a search query that you specify. |
| 719 """ |
| 720 return self.post_request(path, kw, response) |
| 721 |
| 722 @requires(['MarketplaceId', 'ASINList']) |
| 723 @structured_lists('ASINList.ASIN') |
| 724 @api_action('Products', 20, 20) |
| 725 def get_matching_product(self, path, response, **kw): |
| 726 """Returns a list of products and their attributes, based on |
| 727 a list of ASIN values that you specify. |
| 728 """ |
| 729 return self.post_request(path, kw, response) |
| 730 |
| 731 @requires(['MarketplaceId', 'IdType', 'IdList']) |
| 732 @structured_lists('IdList.Id') |
| 733 @api_action('Products', 20, 20) |
| 734 def get_matching_product_for_id(self, path, response, **kw): |
| 735 """Returns a list of products and their attributes, based on |
| 736 a list of Product IDs that you specify. |
| 737 """ |
| 738 return self.post_request(path, kw, response) |
| 739 |
| 740 @requires(['MarketplaceId', 'SellerSKUList']) |
| 741 @structured_lists('SellerSKUList.SellerSKU') |
| 742 @api_action('Products', 20, 10, 'GetCompetitivePricingForSKU') |
| 743 def get_competitive_pricing_for_sku(self, path, response, **kw): |
| 744 """Returns the current competitive pricing of a product, |
| 745 based on the SellerSKUs and MarketplaceId that you specify. |
| 746 """ |
| 747 return self.post_request(path, kw, response) |
| 748 |
| 749 @requires(['MarketplaceId', 'ASINList']) |
| 750 @structured_lists('ASINList.ASIN') |
| 751 @api_action('Products', 20, 10, 'GetCompetitivePricingForASIN') |
| 752 def get_competitive_pricing_for_asin(self, path, response, **kw): |
| 753 """Returns the current competitive pricing of a product, |
| 754 based on the ASINs and MarketplaceId that you specify. |
| 755 """ |
| 756 return self.post_request(path, kw, response) |
| 757 |
| 758 @requires(['MarketplaceId', 'SellerSKUList']) |
| 759 @structured_lists('SellerSKUList.SellerSKU') |
| 760 @api_action('Products', 20, 5, 'GetLowestOfferListingsForSKU') |
| 761 def get_lowest_offer_listings_for_sku(self, path, response, **kw): |
| 762 """Returns the lowest price offer listings for a specific |
| 763 product by item condition and SellerSKUs. |
| 764 """ |
| 765 return self.post_request(path, kw, response) |
| 766 |
| 767 @requires(['MarketplaceId', 'ASINList']) |
| 768 @structured_lists('ASINList.ASIN') |
| 769 @api_action('Products', 20, 5, 'GetLowestOfferListingsForASIN') |
| 770 def get_lowest_offer_listings_for_asin(self, path, response, **kw): |
| 771 """Returns the lowest price offer listings for a specific |
| 772 product by item condition and ASINs. |
| 773 """ |
| 774 return self.post_request(path, kw, response) |
| 775 |
| 776 @requires(['MarketplaceId', 'SellerSKU']) |
| 777 @api_action('Products', 20, 20, 'GetProductCategoriesForSKU') |
| 778 def get_product_categories_for_sku(self, path, response, **kw): |
| 779 """Returns the product categories that a SellerSKU belongs to. |
| 780 """ |
| 781 return self.post_request(path, kw, response) |
| 782 |
| 783 @requires(['MarketplaceId', 'ASIN']) |
| 784 @api_action('Products', 20, 20, 'GetProductCategoriesForASIN') |
| 785 def get_product_categories_for_asin(self, path, response, **kw): |
| 786 """Returns the product categories that an ASIN belongs to. |
| 787 """ |
| 788 return self.post_request(path, kw, response) |
| 789 |
| 790 @api_action('Products', 2, 300, 'GetServiceStatus') |
| 791 def get_products_service_status(self, path, response, **kw): |
| 792 """Returns the operational status of the Products API section. |
| 793 """ |
| 794 return self.post_request(path, kw, response) |
| 795 |
| 796 @api_action('Sellers', 15, 60) |
| 797 def list_marketplace_participations(self, path, response, **kw): |
| 798 """Returns a list of marketplaces that the seller submitting |
| 799 the request can sell in, and a list of participations that |
| 800 include seller-specific information in that marketplace. |
| 801 """ |
| 802 return self.post_request(path, kw, response) |
| 803 |
| 804 @requires(['NextToken']) |
| 805 @api_action('Sellers', 15, 60) |
| 806 def list_marketplace_participations_by_next_token(self, path, response, |
| 807 **kw): |
| 808 """Returns the next page of marketplaces and participations |
| 809 using the NextToken value that was returned by your |
| 810 previous request to either ListMarketplaceParticipations |
| 811 or ListMarketplaceParticipationsByNextToken. |
| 812 """ |
| 813 return self.post_request(path, kw, response) |
| OLD | NEW |