From 89364ed864610b13fe9cc4a8adfc445ff6531190 Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Sat, 23 Feb 2013 22:26:46 -0500 Subject: [PATCH] Adding sqs queue creation --- moto/__init__.py | 1 + moto/core/utils.py | 43 +++++++++++++ moto/ec2/responses/__init__.py | 4 +- moto/ec2/responses/instances.py | 3 +- moto/ec2/utils.py | 21 ------ moto/s3/responses.py | 3 +- moto/s3/utils.py | 10 --- moto/sqs/__init__.py | 2 + moto/sqs/models.py | 42 ++++++++++++ moto/sqs/responses.py | 110 ++++++++++++++++++++++++++++++++ moto/sqs/urls.py | 10 +++ tests/test_sqs/test_sqs.py | 30 +++++++++ 12 files changed, 244 insertions(+), 35 deletions(-) create mode 100644 moto/core/utils.py create mode 100644 moto/sqs/__init__.py create mode 100644 moto/sqs/models.py create mode 100644 moto/sqs/responses.py create mode 100644 moto/sqs/urls.py create mode 100644 tests/test_sqs/test_sqs.py diff --git a/moto/__init__.py b/moto/__init__.py index 24dc1456c..e9a5815ed 100644 --- a/moto/__init__.py +++ b/moto/__init__.py @@ -4,3 +4,4 @@ logging.getLogger('boto').setLevel(logging.CRITICAL) from .dynamodb import mock_dynamodb from .ec2 import mock_ec2 from .s3 import mock_s3 +from .sqs import mock_sqs diff --git a/moto/core/utils.py b/moto/core/utils.py new file mode 100644 index 000000000..e49670573 --- /dev/null +++ b/moto/core/utils.py @@ -0,0 +1,43 @@ +import inspect +from urlparse import parse_qs + + +def headers_to_dict(headers): + result = {} + for index, header in enumerate(headers.split("\r\n")): + if not header: + continue + if index: + # Parsing headers + key, value = header.split(":", 1) + result[key.strip()] = value.strip() + else: + # Parsing method and path + path_and_querystring = header.split(" /")[1] + if '?' in path_and_querystring: + querystring = path_and_querystring.split("?")[1] + else: + querystring = path_and_querystring + queryset_dict = parse_qs(querystring) + result.update(queryset_dict) + return result + + +def camelcase_to_underscores(argument): + ''' Converts a camelcase param like theNewAttribute to the equivalent + python underscore variable like the_new_attribute''' + result = '' + prev_char_title = True + for char in argument: + if char.istitle() and not prev_char_title: + # Only add underscore if char is capital, not first letter, and prev + # char wasn't capital + result += "_" + prev_char_title = char.istitle() + if not char.isspace(): # Only add non-whitespace + result += char.lower() + return result + + +def method_names_from_class(clazz): + return [x[0] for x in inspect.getmembers(clazz, predicate=inspect.ismethod)] diff --git a/moto/ec2/responses/__init__.py b/moto/ec2/responses/__init__.py index 8203a1c36..ce27693c6 100644 --- a/moto/ec2/responses/__init__.py +++ b/moto/ec2/responses/__init__.py @@ -1,6 +1,6 @@ from urlparse import parse_qs -from moto.ec2.utils import camelcase_to_underscores, method_namess_from_class +from moto.core.utils import camelcase_to_underscores, method_names_from_class from .amazon_dev_pay import AmazonDevPay from .amis import AmisResponse @@ -76,7 +76,7 @@ class EC2Response(object): action = camelcase_to_underscores(action) for sub_response in self.sub_responses: - method_names = method_namess_from_class(sub_response) + method_names = method_names_from_class(sub_response) if action in method_names: response = sub_response(querystring) method = getattr(response, action) diff --git a/moto/ec2/responses/instances.py b/moto/ec2/responses/instances.py index 758b89a38..798b84137 100644 --- a/moto/ec2/responses/instances.py +++ b/moto/ec2/responses/instances.py @@ -1,7 +1,8 @@ from jinja2 import Template +from moto.core.utils import camelcase_to_underscores from moto.ec2.models import ec2_backend -from moto.ec2.utils import instance_ids_from_querystring, camelcase_to_underscores +from moto.ec2.utils import instance_ids_from_querystring class InstanceResponse(object): diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py index 08b07b58c..d95ad3bbe 100644 --- a/moto/ec2/utils.py +++ b/moto/ec2/utils.py @@ -1,4 +1,3 @@ -import inspect import random @@ -53,23 +52,3 @@ def resource_ids_from_querystring(querystring_dict): response_values[value[0]] = (tag_key, tag_value) return response_values - - -def camelcase_to_underscores(argument): - ''' Converts a camelcase param like theNewAttribute to the equivalent - python underscore variable like the_new_attribute''' - result = '' - prev_char_title = True - for char in argument: - if char.istitle() and not prev_char_title: - # Only add underscore if char is capital, not first letter, and prev - # char wasn't capital - result += "_" - prev_char_title = char.istitle() - if not char.isspace(): # Only add non-whitespace - result += char.lower() - return result - - -def method_namess_from_class(clazz): - return [x[0] for x in inspect.getmembers(clazz, predicate=inspect.ismethod)] diff --git a/moto/s3/responses.py b/moto/s3/responses.py index 74f80ec81..f830701a5 100644 --- a/moto/s3/responses.py +++ b/moto/s3/responses.py @@ -1,7 +1,8 @@ from jinja2 import Template from .models import s3_backend -from .utils import bucket_name_from_hostname, headers_to_dict +from moto.core.utils import headers_to_dict +from .utils import bucket_name_from_hostname def all_buckets(uri, body, method): diff --git a/moto/s3/utils.py b/moto/s3/utils.py index 40cbf99f0..4f1325aae 100644 --- a/moto/s3/utils.py +++ b/moto/s3/utils.py @@ -6,13 +6,3 @@ bucket_name_regex = re.compile("(.+).s3.amazonaws.com") def bucket_name_from_hostname(hostname): bucket_result = bucket_name_regex.search(hostname) return bucket_result.groups()[0] - - -def headers_to_dict(headers): - result = {} - for header in headers.split("\r\n"): - if ':' in header: - key, value = header.split(":", 1) - result[key.strip()] = value.strip() - - return result diff --git a/moto/sqs/__init__.py b/moto/sqs/__init__.py new file mode 100644 index 000000000..e04391a39 --- /dev/null +++ b/moto/sqs/__init__.py @@ -0,0 +1,2 @@ +from .models import sqs_backend +mock_sqs = sqs_backend.decorator diff --git a/moto/sqs/models.py b/moto/sqs/models.py new file mode 100644 index 000000000..f90aee579 --- /dev/null +++ b/moto/sqs/models.py @@ -0,0 +1,42 @@ +from moto.core import BaseBackend +from moto.core.utils import camelcase_to_underscores + + +class Queue(object): + camelcase_attributes = ['VisibilityTimeout'] + + def __init__(self, name, visibility_timeout): + self.name = name + self.visibility_timeout = visibility_timeout + + @property + def attributes(self): + result = {} + for attribute in self.camelcase_attributes: + result[attribute] = getattr(self, camelcase_to_underscores(attribute)) + return result + +class SQSBackend(BaseBackend): + + def __init__(self): + self.queues = {} + super(SQSBackend, self).__init__() + + def create_queue(self, name, visibility_timeout): + queue = Queue(name, visibility_timeout) + self.queues[name] = queue + return queue + + def list_queues(self): + return self.queues.values() + + def get_queue(self, queue_name): + return self.queues[queue_name] + + def delete_queue(self, queue_name): + if queue_name in self.queues: + return self.queues.pop(queue_name) + return False + + +sqs_backend = SQSBackend() diff --git a/moto/sqs/responses.py b/moto/sqs/responses.py new file mode 100644 index 000000000..a4f4c735b --- /dev/null +++ b/moto/sqs/responses.py @@ -0,0 +1,110 @@ +from urlparse import parse_qs + +from jinja2 import Template + +from moto.core.utils import headers_to_dict, camelcase_to_underscores, method_names_from_class +from .models import sqs_backend + + +class BaseResponse(object): + def dispatch(self, uri, body, headers): + if body: + querystring = parse_qs(body) + else: + querystring = headers_to_dict(headers) + + self.path = uri.path + self.querystring = querystring + + action = querystring['Action'][0] + action = camelcase_to_underscores(action) + + method_names = method_names_from_class(self.__class__) + if action in method_names: + method = getattr(self, action) + return method() + raise NotImplementedError("The {} action has not been implemented".format(action)) + + +class QueuesResponse(BaseResponse): + + def create_queue(self): + visibility_timeout = None + if 'Attribute.1.Name' in self.querystring and self.querystring.get('Attribute.1.Name')[0] == 'VisibilityTimeout': + visibility_timeout = self.querystring.get("Attribute.1.Value")[0] + + queue_name = self.querystring.get("QueueName")[0] + queue = sqs_backend.create_queue(queue_name, visibility_timeout=visibility_timeout) + template = Template(CREATE_QUEUE_RESPONSE) + return template.render(queue=queue) + + def list_queues(self): + queues = sqs_backend.list_queues() + template = Template(LIST_QUEUES_RESPONSE) + return template.render(queues=queues) + + +class QueueResponse(BaseResponse): + def get_queue_attributes(self): + queue_name = self.path.split("/")[-1] + queue = sqs_backend.get_queue(queue_name) + template = Template(GET_QUEUE_ATTRIBUTES_RESPONSE) + return template.render(queue=queue) + + def delete_queue(self): + queue_name = self.path.split("/")[-1] + queue = sqs_backend.delete_queue(queue_name) + if not queue: + return "A queue with name {} does not exist".format(queue_name), dict(status=404) + template = Template(DELETE_QUEUE_RESPONSE) + return template.render(queue=queue) + + + +CREATE_QUEUE_RESPONSE = """ + + http://sqs.us-east-1.amazonaws.com/123456789012/{{ queue.name }} + {{ queue.visibility_timeout }} + + + + 7a62c49f-347e-4fc4-9331-6e8e7a96aa73 + + +""" + +LIST_QUEUES_RESPONSE = """ + + {% for queue in queues %} + http://sqs.us-east-1.amazonaws.com/123456789012/{{ queue.name }} + {{ queue.visibility_timeout }} + {% endfor %} + + + + 725275ae-0b9b-4762-b238-436d7c65a1ac + + +""" + +DELETE_QUEUE_RESPONSE = """ + + + 6fde8d1e-52cd-4581-8cd9-c512f4c64223 + + +""" + +GET_QUEUE_ATTRIBUTES_RESPONSE = """ + + {% for key, value in queue.attributes.items() %} + + {{ key }} + {{ value }} + + {% endfor %} + + + 1ea71be5-b5a2-4f9d-b85a-945d8d08cd0b + +""" \ No newline at end of file diff --git a/moto/sqs/urls.py b/moto/sqs/urls.py new file mode 100644 index 000000000..acf8c4a32 --- /dev/null +++ b/moto/sqs/urls.py @@ -0,0 +1,10 @@ +from .responses import QueueResponse, QueuesResponse + +base_url = "https://(.*).amazonaws.com" +#base_url2 = "https://sqs.us-east-1.amazonaws.com" + + +urls = { + '{0}/$'.format(base_url): QueuesResponse().dispatch, + '{0}/(\d+)/(.*)$'.format(base_url): QueueResponse().dispatch, +} diff --git a/tests/test_sqs/test_sqs.py b/tests/test_sqs/test_sqs.py new file mode 100644 index 000000000..c3dce18f5 --- /dev/null +++ b/tests/test_sqs/test_sqs.py @@ -0,0 +1,30 @@ +import boto +from boto.exception import SQSError + +from sure import expect + +from moto import mock_sqs + + +@mock_sqs +def test_create_queue(): + conn = boto.connect_sqs('the_key', 'the_secret') + conn.create_queue("test-queue", visibility_timeout=60) + + all_queues = conn.get_all_queues() + all_queues[0].name.should.equal("test-queue") + + all_queues[0].get_timeout().should.equal(60) + + +@mock_sqs +def test_delete_queue(): + conn = boto.connect_sqs('the_key', 'the_secret') + queue = conn.create_queue("test-queue", visibility_timeout=60) + + conn.get_all_queues().should.have.length_of(1) + + queue.delete() + conn.get_all_queues().should.have.length_of(0) + + queue.delete.when.called_with().should.throw(SQSError)