text: # -*- coding: utf-8 -*-
  from __future__ import absolute_import
  
  import base64
  import hashlib
  import hmac
  import warnings
  
  from requests import request
  from requests.exceptions import HTTPError
  
  from . import utils
  
  try:
      from urllib.parse import quote
  except ImportError:
      from urllib import quote
  try:
      from xml.etree.ElementTree import ParseError as XMLError
  except ImportError:
      from xml.parsers.expat import ExpatError as XMLError
  
  
  # Future utilities, coming to the rescue through time and space!
  from mws.future_utils.params import clean_params_dict
  from mws.future_utils.params import remove_empty_param_keys
  from mws.future_utils.timezone import mws_utc_now
  from mws.future_utils.xml import remove_xml_namespaces
  
  
  __all__ = [
      "Feeds",
      "Inventory",
      "InboundShipments",
      "MWSError",
      "Reports",
      "Orders",
      "Products",
      "Recommendations",
      "Sellers",
      "Finances",
  ]
  
  # See https://images-na.ssl-images-amazon.com/images/G/01/mwsportal/doc/en_US/bde/MWSDeveloperGuide._V357736853_.pdf
  # page 8
  # for a list of the end points and marketplace IDs
  
  # spell-checker: disable
  MARKETPLACES = {
      "AE": "https://mws.amazonservices.ae",  # A2VIGQ35RCS4UG
      "AU": "https://mws.amazonservices.com.au",  # A39IBJ37TRP1C6
      "BR": "https://mws.amazonservices.com",  # A2Q3Y263D00KWC
      "CA": "https://mws.amazonservices.ca",  # A2EUQ1WTGCTBG2
      "DE": "https://mws-eu.amazonservices.com",  # A1PA6795UKMFR9
      "EG": "https://mws-eu.amazonservices.com",  # ARBP9OOSHTCHU
      "ES": "https://mws-eu.amazonservices.com",  # A1RKKUPIHCS9HS
      "FR": "https://mws-eu.amazonservices.com",  # A13V1IB3VIYZZH
      "GB": "https://mws-eu.amazonservices.com",  # A1F83G8C2ARO7P
      "IN": "https://mws.amazonservices.in",  # A21TJRUUN4KGV
      "IT": "https://mws-eu.amazonservices.com",  # APJ6JRA9NG5V4
      "JP": "https://mws.amazonservices.jp",  # A1VC38T7YXB528
      "MX": "https://mws.amazonservices.com.mx",  # A1AM78C64UM0Y8
      "NL": "https://mws-eu.amazonservices.com",  # A1805IZSGTT6HS
      "SA": "https://mws-eu.amazonservices.com",  # A17E79C6D8DWNP
      "SE": "https://mws-eu.amazonservices.com",  # A2NODRKZP88ZB9
      "SG": "https://mws-fe.amazonservices.com",  # A19VAU5U5O7RUS
      "TR": "https://mws-eu.amazonservices.com",  # A33AVAJ2PDY3EV
      "UK": "https://mws-eu.amazonservices.com",  # A1F83G8C2ARO7P - GB alias
      "US": "https://mws.amazonservices.com",  # ATVPDKIKX0DER
      "PL": "https://mws-eu.amazonservices.com",  # A1C3SOZRARQ6R3
  }
  # spell-checker: enable
  
  
  class MWSError(Exception):
      """
      Main MWS Exception class
      """
  
      # Allows quick access to the response object.
      # Do not rely on this attribute, always check if its not None.
      response = None
  
  
  def calc_md5(string):
      """
      Calculates the MD5 encryption for the given string
      """
      md5_hash = hashlib.md5()
      md5_hash.update(string)
      return base64.b64encode(md5_hash.digest()).strip(b"\n")
  
  
  def calc_request_description(params):
      request_description = ""
      for key in sorted(params):
          encoded_value = params[key]
          request_description += "&{}={}".format(key, encoded_value)
      return request_description[1:]  # don't include leading ampersand
  
  
  class DictWrapper(object):
      def __init__(self, xml, rootkey=None):
          self.original = xml
          self.response = None
          self._rootkey = rootkey
          self._mydict = utils.XML2Dict().fromstring(remove_xml_namespaces(xml))
          self._response_dict = self._mydict.get(
              list(self._mydict.keys())[0], self._mydict
          )
  
      @property
      def parsed(self):
          if self._rootkey:
              return self._response_dict.get(self._rootkey)
          return self._response_dict
  
  
  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
          if "content-md5" in header:
              hash_ = calc_md5(self.original)
              if header["content-md5"].encode() != hash_:
                  raise MWSError("Wrong Contentlength, maybe amazon error...")
  
      @property
      def parsed(self):
          return self.original
  
  
  class MWS(object):
      """
      Base Amazon API class
      """
  
      # This is used to post/get to the different uris used by amazon per api
      # ie. /Orders/2011-01-01
      # All subclasses must define their own URI only if needed
      URI = "/"
  
      # The API version varies in most amazon APIs
      VERSION = "2009-01-01"
  
      # There seem to be some xml namespace issues. therefore every api subclass
      # is recommended to define its namespace, so that it can be referenced
      # like so AmazonAPISubclass.NAMESPACE.
      # For more information see http://stackoverflow.com/a/8719461/389453
      NAMESPACE = ""
  
      # In here we name each of the operations available to the subclass
      # that have 'ByNextToken' operations associated with them.
      # If the Operation is not listed here, self.action_by_next_token
      # will raise an error.
      NEXT_TOKEN_OPERATIONS = []
  
      # Some APIs are available only to either a "Merchant" or "Seller"
      # the type of account needs to be sent in every call to the amazon MWS.
      # This constant defines the exact name of the parameter Amazon expects
      # for the specific API being used.
      # All subclasses need to define this if they require another account type
      # like "Merchant" in which case you define it like so.
      # ACCOUNT_TYPE = "Merchant"
      # Which is the name of the parameter for that specific account type.
      ACCOUNT_TYPE = "SellerId"
  
      def __init__(
          self,
          access_key,
          secret_key,
          account_id,
          region="US",
          domain="",
          uri="",
          version="",
          auth_token="",
      ):
          self.access_key = access_key
          self.secret_key = secret_key
          self.account_id = account_id
          self.auth_token = auth_token
          self.version = version or self.VERSION
          self.uri = uri or self.URI
  
          if domain:
              self.domain = domain
          elif region in MARKETPLACES:
              self.domain = MARKETPLACES[region]
          else:
              error_msg = "Incorrect region supplied ('%(region)s'). Must be one of the following: %(marketplaces)s" % {
                  "marketplaces": ", ".join(MARKETPLACES.keys()),
                  "region": region,
              }
              raise MWSError(error_msg)
  
      def get_params(self):
          """
          Get the parameters required in all MWS requests
          """
          params = {
              "AWSAccessKeyId": self.access_key,
              self.ACCOUNT_TYPE: self.account_id,
              "SignatureVersion": "2",
              "Timestamp": mws_utc_now(),
              "Version": self.version,
              "SignatureMethod": "HmacSHA256",
          }
          if self.auth_token:
              params["MWSAuthToken"] = self.auth_token
          return params
  
      def make_request(self, extra_data, method="GET", **kwargs):
          """
          Make request to Amazon MWS API with these parameters
          """
          from mws import __version__
  
          params = self.get_params()
          params.update(extra_data)
  
          # Clean parameters using backported utilities
          params = remove_empty_param_keys(params)
          params = clean_params_dict(params)
  
          request_description = calc_request_description(params)
          signature = self.calc_signature(method, request_description)
          url = "{domain}{uri}?{description}&Signature={signature}".format(
              domain=self.domain,
              uri=self.uri,
              description=request_description,
              signature=quote(signature),
          )
          headers = {
              "User-Agent": "python-amazon-mws/{} (Language=Python)".format(__version__)
          }
          headers.update(kwargs.get("extra_headers", {}))
  
          try:
              # Some might wonder as to why i don't pass the params dict as the params argument to request.
              # 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
              )
              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
              # response.content and converts it to unicode.
  
              data = response.content
              # I do not check the headers to decide which content structure to server simply because sometimes
              # Amazon's MWS API returns XML error responses with "text/plain" as the Content-Type.
              rootkey = kwargs.get("rootkey", extra_data.get("Action") + "Result")
              try:
                  try:
                      parsed_response = DictWrapper(data, rootkey)
                  except TypeError:
                      # raised when using Python 3 and trying to remove XML namespaces
                      # When we got CSV as result, we will got error on this
                      response.encoding = 'utf-8'
                      parsed_response = DictWrapper(response.text, rootkey)
              except XMLError:
                  parsed_response = DataWrapper(data, response.headers)
  
          except HTTPError as e:
              error = MWSError(str(e.response.text))
              error.response = e.response
              raise error
  
          # Store the response object in the parsed_response for quick access
          parsed_response.response = response
          return parsed_response
  
      def get_service_status(self):
          """
          Returns a GREEN, GREEN_I, YELLOW or RED status.
          Depending on the status/availability of the API its being called from.
          """
  
          return self.make_request(extra_data=dict(Action="GetServiceStatus"))
  
      def action_by_next_token(self, action, next_token):
          """
          Run a '...ByNextToken' action for the given action.
          If the action is not listed in self.NEXT_TOKEN_OPERATIONS, MWSError is raised.
          Action is expected NOT to include 'ByNextToken'
          at the end of its name for this call: function will add that by itself.
          """
          if action not in self.NEXT_TOKEN_OPERATIONS:
              raise MWSError(
                  (
                      "{} action not listed in this API's NEXT_TOKEN_OPERATIONS. "
                      "Please refer to documentation."
                  ).format(action)
              )
  
          action = "{}ByNextToken".format(action)
  
          data = dict(Action=action, NextToken=next_token)
          return self.make_request(data, method="POST")
  
      def calc_signature(self, method, request_description):
          """
          Calculate MWS signature to interface with Amazon
  
          Args:
              method (str)
              request_description (str)
          """
          sig_data = "\n".join(
              [
                  method,
                  self.domain.replace("https://", "").lower(),
                  self.uri,
                  request_description,
              ]
          )
          return base64.b64encode(
              hmac.new(
                  self.secret_key.encode(), sig_data.encode(), hashlib.sha256
              ).digest()
          )
  
      def get_timestamp(self):
          """
          Returns the current timestamp in proper format.
          """
          warnings.warn(
              (
                  "`get_timestamp` is deprecated. Please use "
                  "backported utility `mws.future_utils.timezone.mws_utc_now` "
                  "(in 1.0+ versions, `mws.utils.timezone.mws_utc_now` instead)."
              ),
              DeprecationWarning,
          )
          return mws_utc_now().isoformat()
  
      def enumerate_param(self, param, values):
          """
          DEPRECATED.
          Please use `utils.enumerate_param` for one param, or
          `utils.enumerate_params` for multiple params.
          """
          warnings.warn(
              (
                  "Please use `utils.enumerate_param` for one param, or "
                  "`utils.enumerate_params` for multiple params."
              ),
              DeprecationWarning,
          )
          return utils.enumerate_param(param, values)
  
  
  def feed_options_str(feed_options):
      """Convert a FeedOptions dict of values into an appropriate string value.
  
      Amazon docs for VAT upload with details:
      https://m.media-amazon.com/images/G/01/B2B/DeveloperGuide/vat_calculation_service__dev_guide_H383rf73k4hsu1TYRH139kk134yzs.pdf
      (section 6.4)
  
      Example:
        feed_options = {
          "shippingid": "283845474",
          "totalAmount": 3.25,
          "totalvatamount": 1.23,
          "invoicenumber": "INT-3431-XJE3",
          "documenttype": "CreditNote",
          "transactionid": "amzn:crow:42949119239s",
        }
        print(feed_options_str(feed_options))
        >>> "metadata:shippingid=283845474;metadata:totalAmount=3.25;metadata:totalvatamount=1.23;
        metadata:invoicenumber=INT-3431-XJE3;metadata:documenttype=CreditNote;
        metadata:transactionid=amzn:crow:42949119239s"
      """
      if not feed_options:
          return None
      if not isinstance(feed_options, dict):
          raise ValueError("`feed_options` should be a dict or None")
      output = []
      for key, val in feed_options.items():
          outval = val
          if outval is True or outval is False:
              # Convert literal `True` or `False` to strings `"true"` and `"false"`
              outval = str(outval).lower()
          output.append("metadata:{}={}".format(key, outval))
      return ";".join(output)
  
  
  class Feeds(MWS):
      """
      Amazon MWS Feeds API
      """
  
      ACCOUNT_TYPE = "Merchant"
  
      NEXT_TOKEN_OPERATIONS = [
          "GetFeedSubmissionList",
      ]
  
      def submit_feed(
          self,
          feed,
          feed_type,
          feed_options=None,
          marketplaceids=None,
          content_type="text/xml",
          purge="false",
      ):
          """
          Uploads a feed ( xml or .tsv ) to the seller's inventory.
          Can be used for creating/updating products on Amazon.
          """
          if isinstance(feed_options, dict):
              # Convert dict to appropriate string value
              feed_options = feed_options_str(feed_options)
          data = dict(
              Action="SubmitFeed",
              FeedType=feed_type,
              FeedOptions=feed_options,
              PurgeAndReplace=purge,
          )
          data.update(utils.enumerate_param("MarketplaceIdList.Id.", marketplaceids))
          md = calc_md5(feed)
          return self.make_request(
              data,
              method="POST",
              body=feed,
              extra_headers={"Content-MD5": md, "Content-Type": content_type},
          )
  
      @utils.next_token_action("GetFeedSubmissionList")
      def get_feed_submission_list(
          self,
          feedids=None,
          max_count=None,
          feedtypes=None,
          processingstatuses=None,
          fromdate=None,
          todate=None,
          next_token=None,
      ):
          """
          Returns a list of all feed submissions submitted in the previous 90 days.
          That match the query parameters.
          """
  
          data = dict(
              Action="GetFeedSubmissionList",
              MaxCount=max_count,
              SubmittedFromDate=fromdate,
              SubmittedToDate=todate,
          )
          data.update(utils.enumerate_param("FeedSubmissionIdList.Id", feedids))
          data.update(utils.enumerate_param("FeedTypeList.Type.", feedtypes))
          data.update(
              utils.enumerate_param(
                  "FeedProcessingStatusList.Status.", processingstatuses
              )
          )
          return self.make_request(data)
  
      def get_submission_list_by_next_token(self, token):
          """
          Deprecated.
          Use `get_feed_submission_list(next_token=token)` instead.
          """
          # data = dict(Action='GetFeedSubmissionListByNextToken', NextToken=token)
          # return self.make_request(data)
          warnings.warn(
              "Use `get_feed_submission_list(next_token=token)` instead.",
              DeprecationWarning,
          )
          return self.get_feed_submission_list(next_token=token)
  
      def get_feed_submission_count(
          self, feedtypes=None, processingstatuses=None, fromdate=None, todate=None
      ):
          data = dict(
              Action="GetFeedSubmissionCount",
              SubmittedFromDate=fromdate,
              SubmittedToDate=todate,
          )
          data.update(utils.enumerate_param("FeedTypeList.Type.", feedtypes))
          data.update(
              utils.enumerate_param(
                  "FeedProcessingStatusList.Status.", processingstatuses
              )
          )
          return self.make_request(data)
  
      def cancel_feed_submissions(
          self, feedids=None, feedtypes=None, fromdate=None, todate=None
      ):
          data = dict(
              Action="CancelFeedSubmissions",
              SubmittedFromDate=fromdate,
              SubmittedToDate=todate,
          )
          data.update(utils.enumerate_param("FeedSubmissionIdList.Id.", feedids))
          data.update(utils.enumerate_param("FeedTypeList.Type.", feedtypes))
          return self.make_request(data)
  
      def get_feed_submission_result(self, feedid):
          data = dict(Action="GetFeedSubmissionResult", FeedSubmissionId=feedid)
          return self.make_request(data, rootkey="Message")
  
  
  class Reports(MWS):
      """
      Amazon MWS Reports API
      """
  
      ACCOUNT_TYPE = "Merchant"
      NEXT_TOKEN_OPERATIONS = [
          "GetReportRequestList",
          "GetReportList",
          "GetReportScheduleList",
      ]
  
      # * REPORTS * #
  
      def get_report(self, report_id):
          data = dict(Action="GetReport", ReportId=report_id)
          return self.make_request(data)
  
      def get_report_count(
          self, report_types=(), acknowledged=None, fromdate=None, todate=None
      ):
          data = dict(
              Action="GetReportCount",
              Acknowledged=acknowledged,
              AvailableFromDate=fromdate,
              AvailableToDate=todate,
          )
          data.update(utils.enumerate_param("ReportTypeList.Type.", report_types))
          return self.make_request(data)
  
      @utils.next_token_action("GetReportList")
      def get_report_list(
          self,
          requestids=(),
          max_count=None,
          types=(),
          acknowledged=None,
          fromdate=None,
          todate=None,
          next_token=None,
      ):
          data = dict(
              Action="GetReportList",
              Acknowledged=acknowledged,
              AvailableFromDate=fromdate,
              AvailableToDate=todate,
              MaxCount=max_count,
          )
          data.update(utils.enumerate_param("ReportRequestIdList.Id.", requestids))
          data.update(utils.enumerate_param("ReportTypeList.Type.", types))
          return self.make_request(data)
  
      def get_report_list_by_next_token(self, token):
          """
          Deprecated.
          Use `get_report_list(next_token=token)` instead.
          """
          # data = dict(Action='GetReportListByNextToken', NextToken=token)
          # return self.make_request(data)
          warnings.warn(
              "Use `get_report_list(next_token=token)` instead.",
              DeprecationWarning,
          )
          return self.get_report_list(next_token=token)
  
      def get_report_request_count(
          self, report_types=(), processingstatuses=(), fromdate=None, todate=None
      ):
          data = dict(
              Action="GetReportRequestCount",
              RequestedFromDate=fromdate,
              RequestedToDate=todate,
          )
          data.update(utils.enumerate_param("ReportTypeList.Type.", report_types))
          data.update(
              utils.enumerate_param(
                  "ReportProcessingStatusList.Status.", processingstatuses
              )
          )
          return self.make_request(data)
  
      @utils.next_token_action("GetReportRequestList")
      def get_report_request_list(
          self,
          requestids=(),
          types=(),
          processingstatuses=(),
          max_count=None,
          fromdate=None,
          todate=None,
          next_token=None,
      ):
          data = dict(
              Action="GetReportRequestList",
              MaxCount=max_count,
              RequestedFromDate=fromdate,
              RequestedToDate=todate,
          )
          data.update(utils.enumerate_param("ReportRequestIdList.Id.", requestids))
          data.update(utils.enumerate_param("ReportTypeList.Type.", types))
          data.update(
              utils.enumerate_param(
                  "ReportProcessingStatusList.Status.", processingstatuses
              )
          )
          return self.make_request(data)
  
      def get_report_request_list_by_next_token(self, token):
          """
          Deprecated.
          Use `get_report_request_list(next_token=token)` instead.
          """
          # data = dict(Action='GetReportRequestListByNextToken', NextToken=token)
          # return self.make_request(data)
          warnings.warn(
              "Use `get_report_request_list(next_token=token)` instead.",
              DeprecationWarning,
          )
          return self.get_report_request_list(next_token=token)
  
      def request_report(
          self, report_type, start_date=None, end_date=None, marketplaceids=()
      ):
          data = dict(
              Action="RequestReport",
              ReportType=report_type,
              StartDate=start_date,
              EndDate=end_date,
          )
          data.update(utils.enumerate_param("MarketplaceIdList.Id.", marketplaceids))
          return self.make_request(data)
  
      # * ReportSchedule * #
  
      def get_report_schedule_list(self, types=()):
          data = dict(Action="GetReportScheduleList")
          data.update(utils.enumerate_param("ReportTypeList.Type.", types))
          return self.make_request(data)
  
      def get_report_schedule_count(self, types=()):
          data = dict(Action="GetReportScheduleCount")
          data.update(utils.enumerate_param("ReportTypeList.Type.", types))
          return self.make_request(data)
  
  
  class Orders(MWS):
      """
      Amazon Orders API
      """
  
      URI = "/Orders/2013-09-01"
      VERSION = "2013-09-01"
      NAMESPACE = "{https://mws.amazonservices.com/Orders/2013-09-01}"
      NEXT_TOKEN_OPERATIONS = [
          "ListOrders",
          "ListOrderItems",
      ]
  
      @utils.next_token_action("ListOrders")
      def list_orders(
          self,
          marketplaceids=None,
          created_after=None,
          created_before=None,
          lastupdatedafter=None,
          lastupdatedbefore=None,
          orderstatus=(),
          fulfillment_channels=(),
          payment_methods=(),
          buyer_email=None,
          seller_orderid=None,
          max_results="100",
          next_token=None,
      ):
  
          data = dict(
              Action="ListOrders",
              CreatedAfter=created_after,
              CreatedBefore=created_before,
              LastUpdatedAfter=lastupdatedafter,
              LastUpdatedBefore=lastupdatedbefore,
              BuyerEmail=buyer_email,
              SellerOrderId=seller_orderid,
              MaxResultsPerPage=max_results,
          )
          data.update(utils.enumerate_param("OrderStatus.Status.", orderstatus))
          data.update(utils.enumerate_param("MarketplaceId.Id.", marketplaceids))
          data.update(
              utils.enumerate_param("FulfillmentChannel.Channel.", fulfillment_channels)
          )
          data.update(utils.enumerate_param("PaymentMethod.Method.", payment_methods))
          return self.make_request(data)
  
      def list_orders_by_next_token(self, token):
          """
          Deprecated.
          Use `list_orders(next_token=token)` instead.
          """
          # data = dict(Action='ListOrdersByNextToken', NextToken=token)
          # return self.make_request(data)
          warnings.warn(
              "Use `list_orders(next_token=token)` instead.",
              DeprecationWarning,
          )
          return self.list_orders(next_token=token)
  
      def get_order(self, amazon_order_ids):
          data = dict(Action="GetOrder")
          data.update(utils.enumerate_param("AmazonOrderId.Id.", amazon_order_ids))
          return self.make_request(data)
  
      @utils.next_token_action("ListOrderItems")
      def list_order_items(self, amazon_order_id=None, next_token=None):
          data = dict(Action="ListOrderItems", AmazonOrderId=amazon_order_id)
          return self.make_request(data)
  
      def list_order_items_by_next_token(self, token):
          """
          Deprecated.
          Use `list_order_items(next_token=token)` instead.
          """
          # data = dict(Action='ListOrderItemsByNextToken', NextToken=token)
          # return self.make_request(data)
          warnings.warn(
              "Use `list_order_items(next_token=token)` instead.",
              DeprecationWarning,
          )
          return self.list_order_items(next_token=token)
  
  
  class Products(MWS):
      """
      Amazon MWS Products API
      """
  
      URI = "/Products/2011-10-01"
      VERSION = "2011-10-01"
      NAMESPACE = "{http://mws.amazonservices.com/schema/Products/2011-10-01}"
      # NEXT_TOKEN_OPERATIONS = []
  
      def list_matching_products(self, marketplaceid, query, contextid=None):
          """
          Returns a list of products and their attributes, ordered by
          relevancy, based on a search query that you specify.
          Your search query can be a phrase that describes the product
          or it can be a product identifier such as a UPC, EAN, ISBN, or JAN.
          """
          data = dict(
              Action="ListMatchingProducts",
              MarketplaceId=marketplaceid,
              Query=query,
              QueryContextId=contextid,
          )
          return self.make_request(data)
  
      def get_matching_product(self, marketplaceid, asins):
          """
          Returns a list of products and their attributes, based on a list of
          ASIN values that you specify.
          """
          data = dict(Action="GetMatchingProduct", MarketplaceId=marketplaceid)
          data.update(utils.enumerate_param("ASINList.ASIN.", asins))
          return self.make_request(data)
  
      def get_matching_product_for_id(self, marketplaceid, type_, ids):
          """
          Returns a list of products and their attributes, based on a list of
          product identifier values (ASIN, SellerSKU, UPC, EAN, ISBN, GCID  and JAN)
          The identifier type is case sensitive.
          Added in Fourth Release, API version 2011-10-01
          """
          data = dict(
              Action="GetMatchingProductForId", MarketplaceId=marketplaceid, IdType=type_
          )
  
          data.update(utils.enumerate_param("IdList.Id.", ids))
          return self.make_request(data)
  
      def get_competitive_pricing_for_sku(self, marketplaceid, skus):
          """
          Returns the current competitive pricing of a product,
          based on the SellerSKU and MarketplaceId that you specify.
          """
          data = dict(Action="GetCompetitivePricingForSKU", MarketplaceId=marketplaceid)
          data.update(utils.enumerate_param("SellerSKUList.SellerSKU.", skus))
          return self.make_request(data)
  
      def get_competitive_pricing_for_asin(self, marketplaceid, asins):
          """
          Returns the current competitive pricing of a product,
          based on the ASIN and MarketplaceId that you specify.
          """
          data = dict(Action="GetCompetitivePricingForASIN", MarketplaceId=marketplaceid)
          data.update(utils.enumerate_param("ASINList.ASIN.", asins))
          return self.make_request(data)
  
      def get_lowest_offer_listings_for_sku(
          self, marketplaceid, skus, condition="Any", excludeme="False"
      ):
          data = dict(
              Action="GetLowestOfferListingsForSKU",
              MarketplaceId=marketplaceid,
              ItemCondition=condition,
              ExcludeMe=excludeme,
          )
          data.update(utils.enumerate_param("SellerSKUList.SellerSKU.", skus))
          return self.make_request(data)
  
      def get_lowest_offer_listings_for_asin(
          self, marketplaceid, asins, condition="Any", excludeme="False"
      ):
          data = dict(
              Action="GetLowestOfferListingsForASIN",
              MarketplaceId=marketplaceid,
              ItemCondition=condition,
              ExcludeMe=excludeme,
          )
          data.update(utils.enumerate_param("ASINList.ASIN.", asins))
          return self.make_request(data)
  
      def get_lowest_priced_offers_for_sku(
          self, marketplaceid, sku, condition="New", excludeme="False"
      ):
          data = dict(
              Action="GetLowestPricedOffersForSKU",
              MarketplaceId=marketplaceid,
              SellerSKU=sku,
              ItemCondition=condition,
              ExcludeMe=excludeme,
          )
          return self.make_request(data)
  
      def get_lowest_priced_offers_for_asin(
          self, marketplaceid, asin, condition="New", excludeme="False"
      ):
          data = dict(
              Action="GetLowestPricedOffersForASIN",
              MarketplaceId=marketplaceid,
              ASIN=asin,
              ItemCondition=condition,
              ExcludeMe=excludeme,
          )
          return self.make_request(data)
  
      def get_product_categories_for_sku(self, marketplaceid, sku):
          data = dict(
              Action="GetProductCategoriesForSKU",
              MarketplaceId=marketplaceid,
              SellerSKU=sku,
          )
          return self.make_request(data)
  
      def get_product_categories_for_asin(self, marketplaceid, asin):
          data = dict(
              Action="GetProductCategoriesForASIN", MarketplaceId=marketplaceid, ASIN=asin
          )
          return self.make_request(data)
  
      def get_my_price_for_sku(self, marketplaceid, skus, condition=None):
          data = dict(
              Action="GetMyPriceForSKU",
              MarketplaceId=marketplaceid,
              ItemCondition=condition,
          )
          data.update(utils.enumerate_param("SellerSKUList.SellerSKU.", skus))
          return self.make_request(data)
  
      def get_my_price_for_asin(self, marketplaceid, asins, condition=None):
          data = dict(
              Action="GetMyPriceForASIN",
              MarketplaceId=marketplaceid,
              ItemCondition=condition,
          )
          data.update(utils.enumerate_param("ASINList.ASIN.", asins))
          return self.make_request(data)
  
  
  class Sellers(MWS):
      """
      Amazon MWS Sellers API
      """
  
      URI = "/Sellers/2011-07-01"
      VERSION = "2011-07-01"
      NAMESPACE = "{http://mws.amazonservices.com/schema/Sellers/2011-07-01}"
      NEXT_TOKEN_OPERATIONS = [
          "ListMarketplaceParticipations",
      ]
  
      @utils.next_token_action("ListMarketplaceParticipations")
      def list_marketplace_participations(self, next_token=None):
          """
          Returns a list of marketplaces a seller can participate in and
          a list of participations that include seller-specific information in that marketplace.
          The operation returns only those marketplaces where the seller's account is
          in an active state.
  
          Run with `next_token` kwarg to call related "ByNextToken" action.
          """
          data = dict(Action="ListMarketplaceParticipations")
          return self.make_request(data)
  
      def list_marketplace_participations_by_next_token(self, token):
          """
          Deprecated.
          Use `list_marketplace_participations(next_token=token)` instead.
          """
          # data = dict(Action='ListMarketplaceParticipations', NextToken=token)
          # return self.make_request(data)
          warnings.warn(
              "Use `list_marketplace_participations(next_token=token)` instead.",
              DeprecationWarning,
          )
          return self.list_marketplace_participations(next_token=token)
  
  
  class Finances(MWS):
      """
      Amazon MWS Finances API
      """
  
      URI = "/Finances/2015-05-01"
      VERSION = "2015-05-01"
      NS = "{https://mws.amazonservices.com/Finances/2015-05-01}"
      NEXT_TOKEN_OPERATIONS = [
          "ListFinancialEventGroups",
          "ListFinancialEvents",
      ]
  
      @utils.next_token_action("ListFinancialEventGroups")
      def list_financial_event_groups(
          self, created_after=None, created_before=None, max_results=None, next_token=None
      ):
          """
          Returns a list of financial event groups
          """
          data = dict(
              Action="ListFinancialEventGroups",
              FinancialEventGroupStartedAfter=created_after,
              FinancialEventGroupStartedBefore=created_before,
              MaxResultsPerPage=max_results,
          )
          return self.make_request(data)
  
      def list_financial_event_groups_by_next_token(self, token):
          """
          Deprecated.
          Use `list_financial_event_groups(next_token=token)` instead.
          """
          warnings.warn(
              "Use `list_financial_event_groups(next_token=token)` instead.",
              DeprecationWarning,
          )
          return self.list_financial_event_groups(next_token=token)
  
      @utils.next_token_action("ListFinancialEvents")
      def list_financial_events(
          self,
          financial_event_group_id=None,
          amazon_order_id=None,
          posted_after=None,
          posted_before=None,
          max_results=None,
          next_token=None,
      ):
          """
          Returns financial events for a user-provided FinancialEventGroupId or AmazonOrderId
          """
          data = dict(
              Action="ListFinancialEvents",
              FinancialEventGroupId=financial_event_group_id,
              AmazonOrderId=amazon_order_id,
              PostedAfter=posted_after,
              PostedBefore=posted_before,
              MaxResultsPerPage=max_results,
          )
          return self.make_request(data)
  
      def list_financial_events_by_next_token(self, token):
          """
          Deprecated.
          Use `list_financial_events(next_token=token)` instead.
          """
          warnings.warn(
              "Use `list_financial_events(next_token=token)` instead.",
              DeprecationWarning,
          )
          return self.list_financial_events(next_token=token)
  
  
  # * Fulfillment APIs * #
  
  
  class InboundShipments(MWS):
      """
      Amazon MWS FulfillmentInboundShipment API
      """
  
      URI = "/FulfillmentInboundShipment/2010-10-01"
      VERSION = "2010-10-01"
      NAMESPACE = "{http://mws.amazonaws.com/FulfillmentInboundShipment/2010-10-01/}"
      NEXT_TOKEN_OPERATIONS = [
          "ListInboundShipments",
          "ListInboundShipmentItems",
      ]
      SHIPMENT_STATUSES = ["WORKING", "SHIPPED", "CANCELLED"]
      DEFAULT_SHIP_STATUS = "WORKING"
      LABEL_PREFERENCES = ["SELLER_LABEL", "AMAZON_LABEL_ONLY", "AMAZON_LABEL_PREFERRED"]
  
      def __init__(self, *args, **kwargs):
          """
          Allow the addition of a from_address dict during object initialization.
          kwarg "from_address" is caught and popped here,
          then calls set_ship_from_address.
          If empty or left out, empty dict is set by default.
          """
          self.from_address = {}
          addr = kwargs.pop("from_address", None)
          if addr is not None:
              self.set_ship_from_address(addr)
          super(InboundShipments, self).__init__(*args, **kwargs)
  
      def set_ship_from_address(self, address):
          """
          Verifies the structure of an address dictionary.
          Once verified against the KEY_CONFIG, saves a parsed version
          of that dictionary, ready to send to requests.
          """
          # Clear existing
          self.from_address = None
  
          if not address:
              raise MWSError("Missing required `address` dict.")
          if not isinstance(address, dict):
              raise MWSError("`address` must be a dict")
  
          key_config = [
              # Tuples composed of:
              # (input_key, output_key, is_required, default_value)
              ("name", "Name", True, None),
              ("address_1", "AddressLine1", True, None),
              ("address_2", "AddressLine2", False, None),
              ("city", "City", True, None),
              ("district_or_county", "DistrictOrCounty", False, None),
              ("state_or_province", "StateOrProvinceCode", False, None),
              ("postal_code", "PostalCode", False, None),
              ("country", "CountryCode", False, "US"),
          ]
  
          # Check if all REQUIRED keys in address exist:
          if not all(k in address for k in [c[0] for c in key_config if c[2]]):
              # Required parts of address missing
              raise MWSError(
                  (
                      "`address` dict missing required keys: {required}."
                      "\n- Optional keys: {optional}."
                  ).format(
                      required=", ".join([c[0] for c in key_config if c[2]]),
                      optional=", ".join([c[0] for c in key_config if not c[2]]),
                  )
              )
  
          # Passed tests. Assign values
          addr = {
              "ShipFromAddress.{}".format(c[1]): address.get(c[0], c[3])
              for c in key_config
          }
          self.from_address = addr
  
      def _parse_item_args(self, item_args, operation):
          """
          Parses item arguments sent to create_inbound_shipment_plan, create_inbound_shipment,
          and update_inbound_shipment methods.
  
          `item_args` is expected as an iterable containing dicts.
          Each dict should have the following keys:
            For `create_inbound_shipment_plan`:
              REQUIRED: 'sku', 'quantity'
              OPTIONAL: 'quantity_in_case', 'asin', 'condition'
            Other operations:
              REQUIRED: 'sku', 'quantity'
              OPTIONAL: 'quantity_in_case'
          If a required key is missing, throws MWSError.
          All extra keys are ignored.
  
          Keys (above) are converted to the appropriate MWS key according to `key_config` (below)
          based on the particular operation required.
          """
          if not item_args:
              raise MWSError("One or more `item` dict arguments required.")
  
          if operation == "CreateInboundShipmentPlan":
              # `key_config` composed of list of tuples, each tuple compose of:
              # (input_key, output_key, is_required, default_value)
              key_config = [
                  ("sku", "SellerSKU", True, None),
                  ("quantity", "Quantity", True, None),
                  ("quantity_in_case", "QuantityInCase", False, None),
                  ("asin", "ASIN", False, None),
                  ("condition", "Condition", False, None),
              ]
              # The expected MWS key for quantity is different for this operation.
              # This ensures we use the right key later on.
              quantity_key = "Quantity"
          else:
              key_config = [
                  ("sku", "SellerSKU", True, None),
                  ("quantity", "QuantityShipped", True, None),
                  ("quantity_in_case", "QuantityInCase", False, None),
              ]
              quantity_key = "QuantityShipped"
  
          items = []
          for item in item_args:
              if not isinstance(item, dict):
                  raise MWSError("`item` argument must be a dict.")
              if not all(k in item for k in [c[0] for c in key_config if c[2]]):
                  # Required keys of an item line missing
                  raise MWSError(
                      (
                          "`item` dict missing required keys: {required}."
                          "\n- Optional keys: {optional}."
                      ).format(
                          required=", ".join([c[0] for c in key_config if c[2]]),
                          optional=", ".join([c[0] for c in key_config if not c[2]]),
                      )
                  )
  
              # Get data from the item.
              # Convert to str if present, or leave as None if missing
              quantity = item.get("quantity")
              if quantity is not None:
                  quantity = str(quantity)
  
              quantity_in_case = item.get("quantity_in_case")
              if quantity_in_case is not None:
                  quantity_in_case = str(quantity_in_case)
  
              item_dict = {
                  "SellerSKU": item.get("sku"),
                  quantity_key: quantity,
                  "QuantityInCase": quantity_in_case,
              }
              item_dict.update(
                  {
                      c[1]: item.get(c[0], c[3])
                      for c in key_config
                      if c[0] not in ["sku", "quantity", "quantity_in_case"]
                  }
              )
              items.append(item_dict)
  
          return items
  
      def create_inbound_shipment_plan(
          self, items, country_code="US", subdivision_code="", label_preference=""
      ):
          """
          Returns one or more inbound shipment plans, which provide the
          information you need to create inbound shipments.
  
          At least one dictionary must be passed as `args`. Each dictionary
          should contain the following keys:
            REQUIRED: 'sku', 'quantity'
            OPTIONAL: 'asin', 'condition', 'quantity_in_case'
  
          'from_address' is required. Call 'set_ship_from_address' first before
          using this operation.
          """
          if not items:
              raise MWSError("One or more `item` dict arguments required.")
          subdivision_code = subdivision_code or None
          label_preference = label_preference or None
  
          items = self._parse_item_args(items, "CreateInboundShipmentPlan")
          if not self.from_address:
              raise MWSError(
                  (
                      "ShipFromAddress has not been set. "
                      "Please use `.set_ship_from_address()` first."
                  )
              )
  
          data = dict(
              Action="CreateInboundShipmentPlan",
              ShipToCountryCode=country_code,
              ShipToCountrySubdivisionCode=subdivision_code,
              LabelPrepPreference=label_preference,
          )
          data.update(self.from_address)
          data.update(
              utils.enumerate_keyed_param(
                  "InboundShipmentPlanRequestItems.member",
                  items,
              )
          )
          return self.make_request(data, method="POST")
  
      def create_inbound_shipment(
          self,
          shipment_id,
          shipment_name,
          destination,
          items,
          shipment_status="",
          label_preference="",
          case_required=False,
          box_contents_source=None,
      ):
          """
          Creates an inbound shipment to Amazon's fulfillment network.
  
          At least one dictionary must be passed as `items`. Each dictionary
          should contain the following keys:
            REQUIRED: 'sku', 'quantity'
            OPTIONAL: 'quantity_in_case'
  
          'from_address' is required. Call 'set_ship_from_address' first before
          using this operation.
          """
          assert isinstance(shipment_id, str), "`shipment_id` must be a string."
          assert isinstance(shipment_name, str), "`shipment_name` must be a string."
          assert isinstance(destination, str), "`destination` must be a string."
  
          if not items:
              raise MWSError("One or more `item` dict arguments required.")
  
          items = self._parse_item_args(items, "CreateInboundShipment")
  
          if not self.from_address:
              raise MWSError(
                  (
                      "ShipFromAddress has not been set. "
                      "Please use `.set_ship_from_address()` first."
                  )
              )
          from_address = self.from_address
          from_address = {
              "InboundShipmentHeader.{}".format(k): v for k, v in from_address.items()
          }
  
          if shipment_status not in self.SHIPMENT_STATUSES:
              # Status is required for `create` request.
              # Set it to default.
              shipment_status = self.DEFAULT_SHIP_STATUS
  
          if label_preference not in self.LABEL_PREFERENCES:
              # Label preference not required. Set to None
              label_preference = None
  
          # Explicit True/False for case_required,
          # written as the strings MWS expects.
          case_required = "true" if case_required else "false"
  
          data = {
              "Action": "CreateInboundShipment",
              "ShipmentId": shipment_id,
              "InboundShipmentHeader.ShipmentName": shipment_name,
              "InboundShipmentHeader.DestinationFulfillmentCenterId": destination,
              "InboundShipmentHeader.LabelPrepPreference": label_preference,
              "InboundShipmentHeader.AreCasesRequired": case_required,
              "InboundShipmentHeader.ShipmentStatus": shipment_status,
              "InboundShipmentHeader.IntendedBoxContentsSource": box_contents_source,
          }
          data.update(from_address)
          data.update(
              utils.enumerate_keyed_param(
                  "InboundShipmentItems.member",
                  items,
              )
          )
          return self.make_request(data, method="POST")
  
      def update_inbound_shipment(
          self,
          shipment_id,
          shipment_name,
          destination,
          items=None,
          shipment_status="",
          label_preference="",
          case_required=False,
          box_contents_source=None,
      ):
          """
          Updates an existing inbound shipment in Amazon FBA.
          'from_address' is required. Call 'set_ship_from_address' first before
          using this operation.
          """
          # Assert these are strings, error out if not.
          assert isinstance(shipment_id, str), "`shipment_id` must be a string."
          assert isinstance(shipment_name, str), "`shipment_name` must be a string."
          assert isinstance(destination, str), "`destination` must be a string."
  
          # Parse item args
          if items:
              items = self._parse_item_args(items, "UpdateInboundShipment")
          else:
              items = None
  
          # Raise exception if no from_address has been set prior to calling
          if not self.from_address:
              raise MWSError(
                  (
                      "ShipFromAddress has not been set. "
                      "Please use `.set_ship_from_address()` first."
                  )
              )
          # Assemble the from_address using operation-specific header
          from_address = self.from_address
          from_address = {
              "InboundShipmentHeader.{}".format(k): v for k, v in from_address.items()
          }
  
          if shipment_status not in self.SHIPMENT_STATUSES:
              # Passed shipment status is an invalid choice.
              # Remove it from this request by setting it to None.
              shipment_status = None
  
          if label_preference not in self.LABEL_PREFERENCES:
              # Passed label preference is an invalid choice.
              # Remove it from this request by setting it to None.
              label_preference = None
  
          case_required = "true" if case_required else "false"
  
          data = {
              "Action": "UpdateInboundShipment",
              "ShipmentId": shipment_id,
              "InboundShipmentHeader.ShipmentName": shipment_name,
              "InboundShipmentHeader.DestinationFulfillmentCenterId": destination,
              "InboundShipmentHeader.LabelPrepPreference": label_preference,
              "InboundShipmentHeader.AreCasesRequired": case_required,
              "InboundShipmentHeader.ShipmentStatus": shipment_status,
              "InboundShipmentHeader.IntendedBoxContentsSource": box_contents_source,
          }
          data.update(from_address)
          if items:
              # Update with an items paramater only if they exist.
              data.update(
                  utils.enumerate_keyed_param(
                      "InboundShipmentItems.member",
                      items,
                  )
              )
          return self.make_request(data, method="POST")
  
      def get_prep_instructions_for_sku(self, skus=None, country_code=None):
          """
          Returns labeling requirements and item preparation instructions
          to help you prepare items for an inbound shipment.
          """
          country_code = country_code or "US"
          skus = skus or []
  
          # 'skus' should be a unique list, or there may be an error returned.
          skus = utils.unique_list_order_preserved(skus)
  
          data = dict(
              Action="GetPrepInstructionsForSKU",
              ShipToCountryCode=country_code,
          )
          data.update(
              utils.enumerate_params(
                  {
                      "SellerSKUList.ID.": skus,
                  }
              )
          )
          return self.make_request(data, method="POST")
  
      def get_prep_instructions_for_asin(self, asins=None, country_code=None):
          """
          Returns item preparation instructions to help with
          item sourcing decisions.
          """
          country_code = country_code or "US"
          asins = asins or []
  
          # 'asins' should be a unique list, or there may be an error returned.
          asins = utils.unique_list_order_preserved(asins)
  
          data = dict(
              Action="GetPrepInstructionsForASIN",
              ShipToCountryCode=country_code,
          )
          data.update(
              utils.enumerate_params(
                  {
                      "ASINList.ID.": asins,
                  }
              )
          )
          return self.make_request(data, method="POST")
  
      def get_package_labels(self, shipment_id, num_packages, page_type=None):
          """
          Returns PDF document data for printing package labels for
          an inbound shipment.
          """
          data = dict(
              Action="GetPackageLabels",
              ShipmentId=shipment_id,
              PageType=page_type,
              NumberOfPackages=str(num_packages),
          )
          return self.make_request(data, method="POST")
  
      def get_transport_content(self, shipment_id):
          """
          Returns current transportation information about an
          inbound shipment.
          """
          data = dict(Action="GetTransportContent", ShipmentId=shipment_id)
          return self.make_request(data, method="POST")
  
      def estimate_transport_request(self, shipment_id):
          """
          Requests an estimate of the shipping cost for an inbound shipment.
          """
          data = dict(
              Action="EstimateTransportRequest",
              ShipmentId=shipment_id,
          )
          return self.make_request(data, method="POST")
  
      def void_transport_request(self, shipment_id):
          """
          Voids a previously-confirmed request to ship your inbound shipment
          using an Amazon-partnered carrier.
          """
          data = dict(Action="VoidTransportRequest", ShipmentId=shipment_id)
          return self.make_request(data, method="POST")
  
      def get_bill_of_lading(self, shipment_id):
          """
          Returns PDF document data for printing a bill of lading
          for an inbound shipment.
          """
          data = dict(
              Action="GetBillOfLading",
              ShipmentId=shipment_id,
          )
          return self.make_request(data, "POST")
  
      @utils.next_token_action("ListInboundShipments")
      def list_inbound_shipments(
          self,
          shipment_ids=None,
          shipment_statuses=None,
          last_updated_after=None,
          last_updated_before=None,
      ):
          """
          Returns list of shipments based on statuses, IDs, and/or
          before/after datetimes.
          """
          last_updated_after = utils.dt_iso_or_none(last_updated_after)
          last_updated_before = utils.dt_iso_or_none(last_updated_before)
  
          data = dict(
              Action="ListInboundShipments",
              LastUpdatedAfter=last_updated_after,
              LastUpdatedBefore=last_updated_before,
          )
          data.update(
              utils.enumerate_params(
                  {
                      "ShipmentStatusList.member.": shipment_statuses,
                      "ShipmentIdList.member.": shipment_ids,
                  }
              )
          )
          return self.make_request(data, method="POST")
  
      @utils.next_token_action("ListInboundShipmentItems")
      def list_inbound_shipment_items(
          self,
          shipment_id=None,
          last_updated_after=None,
          last_updated_before=None,
      ):
          """
          Returns list of items within inbound shipments and/or
          before/after datetimes.
          """
          last_updated_after = utils.dt_iso_or_none(last_updated_after)
          last_updated_before = utils.dt_iso_or_none(last_updated_before)
  
          data = dict(
              Action="ListInboundShipmentItems",
              ShipmentId=shipment_id,
              LastUpdatedAfter=last_updated_after,
              LastUpdatedBefore=last_updated_before,
          )
          return self.make_request(data, method="POST")
  
  
  class Inventory(MWS):
      """Amazon MWS Inventory Fulfillment API
  
      `MWS Docs: Inventory API
      <https://docs.developer.amazonservices.com/en_US/fba_inventory/FBAInventory_Overview.html>`_
      """
  
      URI = "/FulfillmentInventory/2010-10-01"
      VERSION = "2010-10-01"
      NAMESPACE = "{http://mws.amazonaws.com/FulfillmentInventory/2010-10-01}"
      NEXT_TOKEN_OPERATIONS = [
          "ListInventorySupply",
      ]
  
      @utils.next_token_action("ListInventorySupply")
      def list_inventory_supply(
          self,
          skus=(),
          datetime_=None,
          response_group="Basic",
          marketplace_id=None,
          next_token=None,
      ):
          """Returns information on available inventory
  
          Pass ``next_token`` to call "ListInventorySupplyByNextToken" instead.
  
          `MWS Docs: ListInventorySupply
          <https://docs.developer.amazonservices.com/en_US/fba_inventory/FBAInventory_ListInventorySupply.html>`_
          """
  
          data = dict(
              Action="ListInventorySupply",
              QueryStartDateTime=datetime_,
              ResponseGroup=response_group,
              MarketplaceId=marketplace_id,
          )
          data.update(utils.enumerate_param("SellerSkus.member.", skus))
          return self.make_request(data, "POST")
  
      def list_inventory_supply_by_next_token(self, token):
          """Alias for ``list_inventory_supply(next_token=token)``
  
          `MWS Docs: ListInventorySupplyByNextToken
          <https://docs.developer.amazonservices.com/en_US/fba_inventory/FBAInventory_ListInventorySupplyByNextToken.html>`_
          """
          # data = dict(Action='ListInventorySupplyByNextToken', NextToken=token)
          # return self.make_request(data, "POST")
          warnings.warn(
              "Use `list_inventory_supply(next_token=token)` instead.",
              DeprecationWarning,
          )
          return self.list_inventory_supply(next_token=token)
  
  
  class OutboundShipments(MWS):
      """
      Amazon MWS Fulfillment Outbound Shipments API
      """
  
      URI = "/FulfillmentOutboundShipment/2010-10-01"
      VERSION = "2010-10-01"
      NEXT_TOKEN_OPERATIONS = [
          "ListAllFulfillmentOrders",
      ]
      # TODO: Complete this class section
  
  
  class Recommendations(MWS):
      """
      Amazon MWS Recommendations API
      """
  
      URI = "/Recommendations/2013-04-01"
      VERSION = "2013-04-01"
      NAMESPACE = "{https://mws.amazonservices.com/Recommendations/2013-04-01}"
      NEXT_TOKEN_OPERATIONS = [
          "ListRecommendations",
      ]
  
      def get_last_updated_time_for_recommendations(self, marketplaceid):
          """
          Checks whether there are active recommendations for each category for the given marketplace, and if there are,
          returns the time when recommendations were last updated for each category.
          """
          data = dict(
              Action="GetLastUpdatedTimeForRecommendations", MarketplaceId=marketplaceid
          )
          return self.make_request(data, "POST")
  
      @utils.next_token_action("ListRecommendations")
      def list_recommendations(
          self, marketplaceid=None, recommendationcategory=None, next_token=None
      ):
          """
          Returns your active recommendations for a specific category or for all categories for a specific marketplace.
          """
          data = dict(
              Action="ListRecommendations",
              MarketplaceId=marketplaceid,
              RecommendationCategory=recommendationcategory,
          )
          return self.make_request(data, "POST")
  
      def list_recommendations_by_next_token(self, token):
          """
          Deprecated.
          Use `list_recommendations(next_token=token)` instead.
          """
          # data = dict(Action="ListRecommendationsByNextToken",
          #             NextToken=token)
          # return self.make_request(data, "POST")
          warnings.warn(
              "Use `list_recommendations(next_token=token)` instead.",
              DeprecationWarning,
          )
          return self.list_recommendations(next_token=token)
  