diff --git a/setup.cfg b/setup.cfg index 60384163..39ea95b7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -40,9 +40,13 @@ zaqarclient.transport = http.v1.1 = zaqarclient.transport.http:HttpTransport https.v1.1 = zaqarclient.transport.http:HttpTransport + http.v2 = zaqarclient.transport.http:HttpTransport + https.v2 = zaqarclient.transport.http:HttpTransport + zaqarclient.api = queues.v1 = zaqarclient.queues.v1.api:V1 queues.v1.1 = zaqarclient.queues.v1.api:V1_1 + queues.v2 = zaqarclient.queues.v2.api:V2 openstack.messaging.v1 = queue_list = zaqarclient.queues.v1.cli:ListQueues diff --git a/tests/functional/queues/v2/__init__.py b/tests/functional/queues/v2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/functional/queues/v2/test_queues.py b/tests/functional/queues/v2/test_queues.py new file mode 100644 index 00000000..5ef40ca6 --- /dev/null +++ b/tests/functional/queues/v2/test_queues.py @@ -0,0 +1,26 @@ +# Copyright (c) 2013 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from zaqarclient.tests.queues import queues +from zaqarclient.transport import http + + +class QueuesV2QueueHttpFunctionalTest(queues.QueuesV2QueueFunctionalTest): + + is_functional = True + transport_cls = http.HttpTransport + url = 'http://127.0.0.1:8888' + version = 2 diff --git a/tests/unit/queues/v2/__init__.py b/tests/unit/queues/v2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/queues/v2/test_client.py b/tests/unit/queues/v2/test_client.py new file mode 100644 index 00000000..4433c87a --- /dev/null +++ b/tests/unit/queues/v2/test_client.py @@ -0,0 +1,55 @@ +# Copyright 2014 IBM Corp. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock + +import ddt + +from zaqarclient.queues import client +from zaqarclient.queues.v1 import core +from zaqarclient.tests import base +from zaqarclient.transport import errors + +VERSIONS = [2] + + +@ddt.ddt +class TestClient(base.TestBase): + + @ddt.data(*VERSIONS) + def test_transport(self, version): + cli = client.Client('http://example.com', + version, {}) + self.assertIsNotNone(cli.transport()) + + @ddt.data(*VERSIONS) + def test_health_ok(self, version): + cli = client.Client('http://example.com', + version, {}) + with mock.patch.object(core, 'health', autospec=True) as core_health: + core_health.return_value = None + self.assertTrue(cli.health()) + + @ddt.data(*VERSIONS) + def test_health_bad(self, version): + cli = client.Client('http://example.com', + version, {}) + + def raise_error(*args, **kwargs): + raise errors.ServiceUnavailableError() + + with mock.patch.object(core, 'health', autospec=True) as core_health: + core_health.side_effect = raise_error + self.assertFalse(cli.health()) diff --git a/tests/unit/queues/v2/test_core.py b/tests/unit/queues/v2/test_core.py new file mode 100644 index 00000000..7df54596 --- /dev/null +++ b/tests/unit/queues/v2/test_core.py @@ -0,0 +1,20 @@ +# Copyright (c) 2013 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from tests.unit.queues.v1 import test_core + + +class TestV2Core(test_core.TestV1Core): + pass diff --git a/tests/unit/queues/v2/test_message.py b/tests/unit/queues/v2/test_message.py new file mode 100644 index 00000000..6cef88f0 --- /dev/null +++ b/tests/unit/queues/v2/test_message.py @@ -0,0 +1,109 @@ +# Copyright (c) 2013 Rackspace, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import mock + +from tests.unit.queues.v1 import test_message as msg +from zaqarclient.queues.v1 import iterator as iterate +from zaqarclient.queues.v1 import message +from zaqarclient.transport import http +from zaqarclient.transport import response + + +class TestMessageIterator(msg.TestMessageIterator): + def test_no_next_iteration(self): + messages = {'links': [], + 'messages': [{ + 'href': '/v2/queues/mine/messages/123123423', + 'ttl': 800, + 'age': 790, + 'body': {'event': 'ActivateAccount', + 'mode': 'active'} + }] + } + + iterator = iterate._Iterator(self.queue.client, + messages, + 'messages', + message.create_object(self.queue)) + iterated = [msg for msg in iterator] + self.assertEqual(len(iterated), 1) + + def test_stream(self): + messages = {'links': [], + 'messages': [{ + 'href': '/v2/queues/mine/messages/123123423', + 'ttl': 800, + 'age': 790, + 'body': {'event': 'ActivateAccount', + 'mode': 'active'} + }] + } + + with mock.patch.object(self.transport, 'send', + autospec=True) as send_method: + + resp = response.Response(None, json.dumps(messages)) + send_method.return_value = resp + + # NOTE(flaper87): The first iteration will return 1 message + # and then call `_next_page` which will use the rel-next link + # to get a new set of messages. + link = {'rel': 'next', + 'href': "/v2/queues/mine/messages?marker=6244-244224-783"} + messages['links'].append(link) + + iterator = iterate._Iterator(self.queue.client, + messages, + 'messages', + message.create_object(self.queue)) + iterated = [msg for msg in iterator.stream()] + self.assertEqual(len(iterated), 2) + + def test_iterator_respect_paging(self): + messages = {'links': [], + 'messages': [{ + 'href': '/v2/queues/mine/messages/123123423', + 'ttl': 800, + 'age': 790, + 'body': {'event': 'ActivateAccount', + 'mode': 'active'} + }] + } + + with mock.patch.object(self.transport, 'send', + autospec=True) as send_method: + + resp = response.Response(None, json.dumps(messages)) + send_method.return_value = resp + + link = {'rel': 'next', + 'href': "/v2/queues/mine/messages?marker=6244-244224-783"} + messages['links'].append(link) + + iterator = iterate._Iterator(self.queue.client, + messages, + 'messages', + message.create_object(self.queue)) + iterated = [msg for msg in iterator] + self.assertEqual(len(iterated), 1) + + +class QueuesV2MessageHttpUnitTest(msg.QueuesV1MessageHttpUnitTest): + + transport_cls = http.HttpTransport + url = 'http://127.0.0.1:8888/v2' + version = 2 diff --git a/tests/unit/queues/v2/test_queues.py b/tests/unit/queues/v2/test_queues.py new file mode 100644 index 00000000..88103b86 --- /dev/null +++ b/tests/unit/queues/v2/test_queues.py @@ -0,0 +1,27 @@ +# Copyright (c) 2013 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from tests.unit.queues.v1 import test_queues +from zaqarclient.transport import http + + +class QueuesV2QueueHttpUnitTest(test_queues.QueuesV1QueueHttpUnitTest): + + transport_cls = http.HttpTransport + url = 'http://127.0.0.1:8888/v2' + version = 2 + + def test_queue_exists(self): + pass diff --git a/zaqarclient/queues/client.py b/zaqarclient/queues/client.py index e898c8b8..9c035cf4 100644 --- a/zaqarclient/queues/client.py +++ b/zaqarclient/queues/client.py @@ -70,9 +70,11 @@ the underlying API, although not recommended. """ from zaqarclient import errors from zaqarclient.queues.v1 import client as cv1 +from zaqarclient.queues.v2 import client as cv2 _CLIENTS = {1: cv1.Client, - 1.1: cv1.Client} + 1.1: cv1.Client, + 2: cv2.Client} def Client(url=None, version=None, conf=None): diff --git a/zaqarclient/queues/v2/__init__.py b/zaqarclient/queues/v2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/zaqarclient/queues/v2/api.py b/zaqarclient/queues/v2/api.py new file mode 100644 index 00000000..cabeef30 --- /dev/null +++ b/zaqarclient/queues/v2/api.py @@ -0,0 +1,6 @@ +from zaqarclient.queues.v1 import api + + +class V2(api.V1_1): + label = 'v2' + schema = api.V1_1.schema.copy() diff --git a/zaqarclient/queues/v2/client.py b/zaqarclient/queues/v2/client.py new file mode 100644 index 00000000..18d2e812 --- /dev/null +++ b/zaqarclient/queues/v2/client.py @@ -0,0 +1,44 @@ +# Copyright (c) 2013 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import uuid + +from zaqarclient.queues.v1 import client + + +class Client(client.Client): + """Client base class + + :param url: Zaqar's instance base url. + :type url: `six.text_type` + :param version: API Version pointing to. + :type version: `int` + :param options: Extra options: + - client_uuid: Custom client uuid. A new one + will be generated, if not passed. + - auth_opts: Authentication options: + - backend + - options + :type options: `dict` + """ + + def __init__(self, url=None, version=2, conf=None): + self.conf = conf or {} + + self.api_url = url + self.api_version = version + self.auth_opts = self.conf.get('auth_opts', {}) + self.client_uuid = self.conf.get('client_uuid', + uuid.uuid4().hex) diff --git a/zaqarclient/queues/v2/core.py b/zaqarclient/queues/v2/core.py new file mode 100644 index 00000000..0ee7c1e5 --- /dev/null +++ b/zaqarclient/queues/v2/core.py @@ -0,0 +1,43 @@ +# Copyright (c) 2013 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This module defines a lower level API for queues' v2. This level of the +API is responsible for packing up the final request, sending it to the server +and handling asynchronous requests. + +Functions present in this module assume that: + + 1. The transport instance is ready to `send` the + request to the server. + + 2. Transport instance holds the conf instance to use for this + request. +""" + +from zaqarclient.queues.v1 import core + +queue_create = core.queue_create +queue_exists = core.queue_exists +queue_get = core.queue_get +queue_get_metadata = core.queue_get_metadata +queue_set_metadata = core.queue_set_metadata +queue_get_stats = core.queue_get_stats +queue_delete = core.queue_delete +queue_list = core.queue_list +message_list = core.message_list +message_post = core.message_post +message_delete = core.message_delete +message_delete_many = core.message_delete_many diff --git a/zaqarclient/queues/v2/message.py b/zaqarclient/queues/v2/message.py new file mode 100644 index 00000000..41a8a687 --- /dev/null +++ b/zaqarclient/queues/v2/message.py @@ -0,0 +1,5 @@ +from zaqarclient.queues.v1 import message + + +class Message(message.Message): + pass diff --git a/zaqarclient/queues/v2/queues.py b/zaqarclient/queues/v2/queues.py new file mode 100644 index 00000000..3cf0fbf4 --- /dev/null +++ b/zaqarclient/queues/v2/queues.py @@ -0,0 +1,39 @@ +# Copyright (c) 2013 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from zaqarclient import errors +from zaqarclient.queues.v1 import queues +from zaqarclient.queues.v2 import core +from zaqarclient.queues.v2 import message + + +class Queue(queues.Queue): + + def message(self, message_id): + """Gets a message by id + + :param message_id: Message's reference + :type message_id: `six.text_type` + + :returns: A message + :rtype: `dict` + """ + req, trans = self.client._request_and_transport() + if self.client.api_version >= 2: + raise errors.InvalidOperation("Unavailable on versions >= 2") + else: + msg = core.message_get(trans, req, self._name, + message_id) + return message.Message(self, **msg) diff --git a/zaqarclient/tests/queues/queues.py b/zaqarclient/tests/queues/queues.py index a0431bbb..e23e93a5 100644 --- a/zaqarclient/tests/queues/queues.py +++ b/zaqarclient/tests/queues/queues.py @@ -461,3 +461,7 @@ class QueuesV1_1QueueFunctionalTest(QueuesV1QueueFunctionalTest): remaining = queue.messages(echo=True) self.assertEqual(1, len(list(remaining))) + + +class QueuesV2QueueFunctionalTest(QueuesV1_1QueueFunctionalTest): + pass diff --git a/zaqarclient/tests/queues/v2/__init__.py b/zaqarclient/tests/queues/v2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/zaqarclient/tests/queues/v2/messages.py b/zaqarclient/tests/queues/v2/messages.py new file mode 100644 index 00000000..26d30a76 --- /dev/null +++ b/zaqarclient/tests/queues/v2/messages.py @@ -0,0 +1,61 @@ +# Copyright (c) 2013 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import mock + +from zaqarclient.tests.queues.v1 import messages +from zaqarclient.transport import response + + +class QueuesV2MessageUnitTest(messages.QueuesV1MessageUnitTest): + + def test_message_delete(self): + returned = { + 'href': '/v2/queues/fizbit/messages/50b68a50d6f5b8c8a7c62b01', + 'ttl': 800, + 'age': 790, + 'body': {'event': 'ActivateAccount', 'mode': 'active'} + } + + with mock.patch.object(self.transport, 'send', + autospec=True) as send_method: + + resp = response.Response(None, json.dumps(returned)) + send_method.return_value = resp + + msg = self.queue.message('50b68a50d6f5b8c8a7c62b01') + + send_method.return_value = None + self.assertIsNone(msg.delete()) + + def test_message_delete_with_claim(self): + returned = { + 'href': '/v2/queues/fizbit/messages/50b68a50d6?claim_id=5388b5dd0', + 'ttl': 800, + 'age': 790, + 'body': {'event': 'ActivateAccount', 'mode': 'active'} + } + + with mock.patch.object(self.transport, 'send', + autospec=True) as send_method: + + resp = response.Response(None, json.dumps(returned)) + send_method.return_value = resp + + msg = self.queue.message('50b68a50d6') + + send_method.return_value = None + self.assertIsNone(msg.delete()) diff --git a/zaqarclient/tests/queues/v2/queues.py b/zaqarclient/tests/queues/v2/queues.py new file mode 100644 index 00000000..45f9592e --- /dev/null +++ b/zaqarclient/tests/queues/v2/queues.py @@ -0,0 +1,24 @@ +# Copyright (c) 2013 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from zaqarclient.tests.queues.v1 import queues + + +class QueuesV2QueueUnitTest(queues.QueuesV1_1QueueUnitTest): + pass + + +class QueuesV2QueueFunctionalTest(queues.QueuesV1_1QueueFunctionalTest): + pass