Repository: boto/boto

Issue:
get_spot_price_history is missing params
The underlying `DescribeSpotPriceHistory` supports a `filters` param and a `nextToken` param.  The `filters` are mainly an alternative to the explicit params of older API versions but may be useful or preferable to some users.  The `next_token` parameter is required to retrieve more than the first page of results.



Discussion:

Resolving patch:
diff --git a/boto/ec2/connection.py b/boto/ec2/connection.py
--- a/boto/ec2/connection.py
+++ b/boto/ec2/connection.py
@@ -1301,7 +1301,8 @@ def get_all_spot_instance_requests(self, request_ids=None,
     def get_spot_price_history(self, start_time=None, end_time=None,
                                instance_type=None, product_description=None,
                                availability_zone=None, dry_run=False,
-                               max_results=None):
+                               max_results=None, next_token=None,
+                               filters=None):
         """
         Retrieve the recent history of spot instances pricing.
 
@@ -1339,6 +1340,19 @@ def get_spot_price_history(self, start_time=None, end_time=None,
         :param max_results: The maximum number of paginated items
             per response.
 
+        :type next_token: str
+        :param next_token: The next set of rows to return.  This should
+            be the value of the ``next_token`` attribute from a previous
+            call to ``get_spot_price_history``.
+
+        :type filters: dict
+        :param filters: Optional filters that can be used to limit the
+            results returned.  Filters are provided in the form of a
+            dictionary consisting of filter names as the key and
+            filter values as the value.  The set of allowable filter
+            names/values is dependent on the request being performed.
+            Check the EC2 API guide for details.
+
         :rtype: list
         :return: A list tuples containing price and timestamp.
         """
@@ -1357,6 +1371,10 @@ def get_spot_price_history(self, start_time=None, end_time=None,
             params['DryRun'] = 'true'
         if max_results is not None:
             params['MaxResults'] = max_results
+        if next_token:
+            params['NextToken'] = next_token
+        if filters:
+            self.build_filter_params(params, filters)
         return self.get_list('DescribeSpotPriceHistory', params,
                              [('item', SpotPriceHistory)], verb='POST')
 
diff --git a/boto/resultset.py b/boto/resultset.py
--- a/boto/resultset.py
+++ b/boto/resultset.py
@@ -117,6 +117,8 @@ def endElement(self, name, value, connection):
             self.append(value)
         elif name == 'NextToken':
             self.next_token = value
+        elif name == 'nextToken':
+            self.next_token = value
         elif name == 'BoxUsage':
             try:
                 connection.box_usage += float(value)


Test patch:
diff --git a/tests/unit/ec2/test_spotinstance.py b/tests/unit/ec2/test_spotinstance.py
--- a/tests/unit/ec2/test_spotinstance.py
+++ b/tests/unit/ec2/test_spotinstance.py
@@ -40,3 +40,70 @@ def test_cancel_spot_instance_requests(self):
         self.assertEqual(response[0].state, 'cancelled')
         self.assertEqual(response[1].id, 'sir-9f8e7d6c')
         self.assertEqual(response[1].state, 'cancelled')
+
+
+class TestGetSpotPriceHistory(AWSMockServiceTestCase):
+
+    connection_class = EC2Connection
+
+    def default_body(self):
+        return """
+              <DescribeSpotPriceHistoryResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
+              <requestId>b6c6978c-bd13-4ad7-9bc8-6f0ac9d32bcc</requestId>
+              <spotPriceHistorySet>
+                <item>
+                  <instanceType>c3.large</instanceType>
+                  <productDescription>Linux/UNIX</productDescription>
+                  <spotPrice>0.032000</spotPrice>
+                  <timestamp>2013-12-28T12:17:43.000Z</timestamp>
+                  <availabilityZone>us-west-2c</availabilityZone>
+                </item>
+                <item>
+                  <instanceType>c3.large</instanceType>
+                  <productDescription>Windows (Amazon VPC)</productDescription>
+                  <spotPrice>0.104000</spotPrice>
+                  <timestamp>2013-12-28T07:49:40.000Z</timestamp>
+                  <availabilityZone>us-west-2b</availabilityZone>
+                </item>
+              </spotPriceHistorySet>
+              <nextToken>q5GwEl5bMGjKq6YmhpDLJ7hEwyWU54jJC2GQ93n61vZV4s1+fzZ674xzvUlTihrl</nextToken>
+            </DescribeSpotPriceHistoryResponse>
+        """
+
+    def test_get_spot_price_history(self):
+        self.set_http_response(status_code=200)
+        response = self.service_connection.get_spot_price_history(
+            instance_type='c3.large')
+        self.assert_request_parameters({
+            'Action': 'DescribeSpotPriceHistory',
+            'InstanceType': 'c3.large'},
+            ignore_params_values=['AWSAccessKeyId', 'SignatureMethod',
+                                  'SignatureVersion', 'Timestamp',
+                                  'Version'])
+        self.assertEqual(len(response), 2)
+        self.assertEqual(response.next_token,
+                         'q5GwEl5bMGjKq6YmhpDLJ7hEwyWU54jJC2GQ93n61vZV4s1+fzZ674xzvUlTihrl')
+        self.assertEqual(response[0].instance_type, 'c3.large')
+        self.assertEqual(response[0].availability_zone, 'us-west-2c')
+        self.assertEqual(response[1].instance_type, 'c3.large')
+        self.assertEqual(response[1].availability_zone, 'us-west-2b')
+        
+        response = self.service_connection.get_spot_price_history(
+            filters={'instance-type': 'c3.large'})
+        self.assert_request_parameters({
+            'Action': 'DescribeSpotPriceHistory',
+            'Filter.1.Name': 'instance-type',
+            'Filter.1.Value.1': 'c3.large'},
+            ignore_params_values=['AWSAccessKeyId', 'SignatureMethod',
+                                  'SignatureVersion', 'Timestamp',
+                                  'Version'])
+        
+        response = self.service_connection.get_spot_price_history(
+            next_token='foobar')
+        self.assert_request_parameters({
+            'Action': 'DescribeSpotPriceHistory',
+            'NextToken': 'foobar'},
+            ignore_params_values=['AWSAccessKeyId', 'SignatureMethod',
+                                  'SignatureVersion', 'Timestamp',
+                                  'Version'])
+