Skip to content
This repository was archived by the owner on Jan 4, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ develop-eggs
pip-log.txt

# Unit test / coverage reports
.pytest_cache/
.coverage
.tox

Expand Down
6 changes: 0 additions & 6 deletions mws/apis/inbound_shipments.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,9 +570,6 @@ def list_inbound_shipments(self, shipment_ids=None, shipment_statuses=None,
Docs:
http://docs.developer.amazonservices.com/en_US/fba_inbound/FBAInbound_ListInboundShipments.html
"""
last_updated_after = utils.dt_iso_or_none(last_updated_after)
last_updated_before = utils.dt_iso_or_none(last_updated_before)

data = {
'Action': 'ListInboundShipments',
'LastUpdatedAfter': last_updated_after,
Expand Down Expand Up @@ -605,9 +602,6 @@ def list_inbound_shipment_items(self, shipment_id=None, last_updated_after=None,
Docs:
http://docs.developer.amazonservices.com/en_US/fba_inbound/FBAInbound_ListInboundShipmentItems.html
"""
last_updated_after = utils.dt_iso_or_none(last_updated_after)
last_updated_before = utils.dt_iso_or_none(last_updated_before)

data = {
'Action': 'ListInboundShipmentItems',
'ShipmentId': shipment_id,
Expand Down
4 changes: 2 additions & 2 deletions mws/apis/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
"""
from __future__ import absolute_import

import mws
from ..mws import MWS
from .. import utils
from ..decorators import next_token_action

# TODO Add ReportType enumerations as constants
# TODO Add Schedule enumerations as constants


class Reports(mws.MWS):
class Reports(MWS):
"""
Amazon MWS Reports API

Expand Down
43 changes: 27 additions & 16 deletions mws/mws.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,31 @@ def calc_request_description(params):
"""
description_items = []
for item in sorted(params.keys()):
encoded_val = quote(str(params[item]), safe='-_.~')
encoded_val = params[item]
description_items.append('{}={}'.format(item, encoded_val))
return '&'.join(description_items)


def remove_empty(dict_):
"""
Returns dict_ with all empty values removed.
"""
return {k: v for k, v in dict_.items() if v}
def clean_params(params):
"""Input cleanup and prevent a lot of common input mistakes."""
# silently remove parameter where values are empty
params = {k: v for k, v in params.items() if v}

params_enc = dict()
for key, value in params.items():
if isinstance(value, (dict, list, set, tuple)):
message = 'expected string or datetime datatype, got {},'\
'for key {} and value {}'.format(
type(value), key, str(value))
raise MWSError(message)
if isinstance(value, (datetime.datetime, datetime.date)):
value = value.isoformat()
if isinstance(value, bool):
value = str(value).lower()
value = str(value)

params_enc[key] = quote(value, safe='-_.~')
return params_enc


def remove_namespace(xml):
Expand Down Expand Up @@ -121,6 +136,7 @@ class DictWrapper(object):
# TODO create a base class for DictWrapper and DataWrapper with all the keys we expect in responses.
# This will make it easier to use either class in place of each other.
# Either this, or pile everything into DataWrapper and make it able to handle all cases.

def __init__(self, xml, rootkey=None):
self.original = xml
self.response = None
Expand All @@ -142,6 +158,7 @@ class DataWrapper(object):
"""
Text wrapper in charge of validating the hash sent by Amazon.
"""

def __init__(self, data, header):
self.original = data
self.response = None
Expand Down Expand Up @@ -246,18 +263,11 @@ def make_request(self, extra_data, method="GET", **kwargs):
"""
Make request to Amazon MWS API with these parameters
"""
# Remove all keys with an empty value because
# Amazon's MWS does not allow such a thing.
extra_data = remove_empty(extra_data)

# convert all Python date/time objects to isoformat
for key, value in extra_data.items():
if isinstance(value, (datetime.datetime, datetime.date)):
extra_data[key] = value.isoformat()

params = self.get_default_params()
proxies = self.get_proxies()
params.update(extra_data)
params = clean_params(params)

if self._test_request_params:
# Testing method: return the params from this request before the request is made.
return params
Expand All @@ -279,7 +289,8 @@ def make_request(self, extra_data, method="GET", **kwargs):
# My answer is, here i have to get the url parsed string of params in order to sign it, so
# if i pass the params dict as params to request, request will repeat that step because it will need
# to convert the dict to a url parsed string, so why do it twice if i can just pass the full url :).
response = request(method, url, data=kwargs.get('body', ''), headers=headers, proxies=proxies)
response = request(method, url, data=kwargs.get(
'body', ''), headers=headers, proxies=proxies)
response.raise_for_status()
# When retrieving data from the response object,
# be aware that response.content returns the content in bytes while response.text calls
Expand Down
17 changes: 1 addition & 16 deletions mws/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class ObjectDict(dict):
>>> a.water
'water'
"""

def __init__(self, initd=None):
if initd is None:
initd = {}
Expand Down Expand Up @@ -271,22 +272,6 @@ def unique_list_order_preserved(seq):
return [x for x in seq if not (x in seen or seen_add(x))]


def dt_iso_or_none(dt_obj):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this method is a fair change. I added it myself from a version of the API I was using in production years ago, but under current circumstances it doesn't serve much of a purpose.

While this method did provide some enforcement requiring a user to pass a datetime to a request method (and get an early exception if they didn't), I think that's an acceptable risk for the time being. We could also offer type hints ala PEP 484, but that is a topic for another day.

"""
If dt_obj is a datetime, return isoformat()
TODO: if dt_obj is a string in iso8601 already, return it back
Otherwise, return None
"""
# If d is a datetime object, format it to iso and return
if isinstance(dt_obj, datetime.datetime):
return dt_obj.isoformat()

# TODO: if dt_obj is a string in iso8601 already, return it

# none of the above: return None
return None


def get_utc_timestamp():
"""
Returns the current UTC timestamp in ISO-8601 format.
Expand Down
17 changes: 9 additions & 8 deletions tests/request_methods/test_feeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
import unittest
import datetime
import mws
from .utils import CommonRequestTestTools
from .utils import CommonRequestTestTools, transform_date


class FeedsTestCase(unittest.TestCase, CommonRequestTestTools):
"""
Test cases for Feeds.
"""
# TODO: Add remaining methods for Feeds

def setUp(self):
self.api = mws.Feeds(
self.CREDENTIAL_ACCESS,
Expand All @@ -33,9 +34,9 @@ def test_get_feed_submission_list(self):
GetFeedSubmissionList operation
"""
from_date = datetime.datetime.utcnow()
from_date_stamp = from_date.isoformat()
from_date_stamp = transform_date(from_date)
to_date = datetime.datetime.utcnow()
to_date_stamp = to_date.isoformat()
to_date_stamp = transform_date(to_date)
feed_ids = [
'1058369303',
'1228369302',
Expand All @@ -61,7 +62,7 @@ def test_get_feed_submission_list(self):
self.assertEqual(params['Action'], 'GetFeedSubmissionList')
self.assertEqual(params['SubmittedFromDate'], from_date_stamp)
self.assertEqual(params['SubmittedToDate'], to_date_stamp)
self.assertEqual(params['MaxCount'], max_count)
self.assertEqual(params['MaxCount'], str(max_count))
self.assertEqual(params['FeedSubmissionIdList.Id.1'], feed_ids[0])
self.assertEqual(params['FeedSubmissionIdList.Id.2'], feed_ids[1])
self.assertEqual(params['FeedTypeList.Type.1'], feed_types[0])
Expand Down Expand Up @@ -94,9 +95,9 @@ def test_get_feed_submission_count(self):
GetFeedSubmissionCount operation
"""
from_date = datetime.datetime.utcnow()
from_date_stamp = from_date.isoformat()
from_date_stamp = transform_date(from_date)
to_date = datetime.datetime.utcnow()
to_date_stamp = to_date.isoformat()
to_date_stamp = transform_date(to_date)
feed_types = [
'_POST_PRODUCT_OVERRIDES_DATA_',
'_POST_FLAT_FILE_FULFILLMENT_ORDER_CANCELLATION_REQUEST_DATA_',
Expand Down Expand Up @@ -125,9 +126,9 @@ def test_cancel_feed_submissions(self):
CancelFeedSubmissions operation
"""
from_date = datetime.datetime.utcnow()
from_date_stamp = from_date.isoformat()
from_date_stamp = transform_date(from_date)
to_date = datetime.datetime.utcnow()
to_date_stamp = to_date.isoformat()
to_date_stamp = transform_date(to_date)
feed_ids = [
'SUB63kvutS',
'l8dM04jxGD',
Expand Down
21 changes: 10 additions & 11 deletions tests/request_methods/test_finances.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
import unittest
import datetime
import mws
from .utils import CommonRequestTestTools
from .utils import CommonRequestTestTools, transform_date


class FinancesTestCase(unittest.TestCase, CommonRequestTestTools):
"""
Test cases for Finances.
"""
# TODO: Add remaining methods for Finances

def setUp(self):
self.api = mws.Finances(
self.CREDENTIAL_ACCESS,
Expand All @@ -26,9 +27,7 @@ def test_list_financial_event_groups(self):
ListFinancialEventGroups operation.
"""
created_after = datetime.datetime.utcnow()
created_after_stamp = created_after.isoformat()
created_before = datetime.datetime.utcnow()
created_before_stamp = created_before.isoformat()
max_results = 659
params = self.api.list_financial_event_groups(
created_after=created_after,
Expand All @@ -37,9 +36,11 @@ def test_list_financial_event_groups(self):
)
self.assert_common_params(params)
self.assertEqual(params['Action'], 'ListFinancialEventGroups')
self.assertEqual(params['FinancialEventGroupStartedAfter'], created_after_stamp)
self.assertEqual(params['FinancialEventGroupStartedBefore'], created_before_stamp)
self.assertEqual(params['MaxResultsPerPage'], max_results)
self.assertEqual(params['FinancialEventGroupStartedAfter'],
transform_date(created_after))
self.assertEqual(params['FinancialEventGroupStartedBefore'],
transform_date(created_before))
self.assertEqual(params['MaxResultsPerPage'], str(max_results))

def test_list_financial_event_groups_by_next_token(self):
"""
Expand All @@ -66,9 +67,7 @@ def test_list_financial_events(self):
ListFinancialEvents operation.
"""
posted_after = datetime.datetime.utcnow()
posted_after_stamp = posted_after.isoformat()
posted_before = datetime.datetime.utcnow()
posted_before_stamp = posted_before.isoformat()
amazon_order_id = '123-4567890-1234567'
financial_event_group_id = '22YgYW55IGNhcm5hbCBwbGVhEXAMPLE'
max_results = 156
Expand All @@ -83,9 +82,9 @@ def test_list_financial_events(self):
self.assertEqual(params['Action'], 'ListFinancialEvents')
self.assertEqual(params['FinancialEventGroupId'], financial_event_group_id)
self.assertEqual(params['AmazonOrderId'], amazon_order_id)
self.assertEqual(params['PostedAfter'], posted_after_stamp)
self.assertEqual(params['PostedBefore'], posted_before_stamp)
self.assertEqual(params['MaxResultsPerPage'], max_results)
self.assertEqual(params['PostedAfter'], transform_date(posted_after))
self.assertEqual(params['PostedBefore'], transform_date(posted_before))
self.assertEqual(params['MaxResultsPerPage'], str(max_results))

def test_list_financial_events_by_next_token(self):
"""
Expand Down
Loading