From 761d23dc65b20078a41b94a2234c07cc2caafa09 Mon Sep 17 00:00:00 2001 From: Victoria Martinez de la Cruz Date: Fri, 28 Aug 2015 16:25:12 -0300 Subject: [PATCH] Refactoring to make Websocket driver available in v2 Considering that most of this feature landed during the Liberty cycle, it made sense to make Websocket a v2 feature. APIImpact Implements-blueprint: persistent-transport Change-Id: I52ef38f32afd688239d4aee9ac379cd8dea03d6c --- doc/source/api/zaqar.api.v1_1.endpoints.rst | 7 - doc/source/api/zaqar.api.v2.endpoints.rst | 7 + doc/source/api/zaqar.api.v2.request.rst | 7 + doc/source/api/zaqar.api.v2.response.rst | 7 + zaqar/api/handler.py | 14 +- zaqar/api/v1/request.py | 523 ++++++++++++------ zaqar/api/v1_1/request.py | 373 +------------ .../websocket/v1_1 => api/v2}/__init__.py | 0 zaqar/api/{v1_1 => v2}/endpoints.py | 2 +- zaqar/api/v2/request.py | 22 + zaqar/api/v2/response.py | 410 ++++++++++++++ .../unit/transport/websocket/v2/__init__.py | 0 .../websocket/{v1_1 => v2}/test_auth.py | 2 +- .../websocket/{v1_1 => v2}/test_claims.py | 0 .../websocket/{v1_1 => v2}/test_messages.py | 4 +- .../{v1_1 => v2}/test_queue_lifecycle.py | 2 +- 16 files changed, 827 insertions(+), 553 deletions(-) delete mode 100644 doc/source/api/zaqar.api.v1_1.endpoints.rst create mode 100644 doc/source/api/zaqar.api.v2.endpoints.rst create mode 100644 doc/source/api/zaqar.api.v2.request.rst create mode 100644 doc/source/api/zaqar.api.v2.response.rst rename zaqar/{tests/unit/transport/websocket/v1_1 => api/v2}/__init__.py (100%) rename zaqar/api/{v1_1 => v2}/endpoints.py (99%) create mode 100644 zaqar/api/v2/request.py create mode 100644 zaqar/api/v2/response.py create mode 100644 zaqar/tests/unit/transport/websocket/v2/__init__.py rename zaqar/tests/unit/transport/websocket/{v1_1 => v2}/test_auth.py (99%) rename zaqar/tests/unit/transport/websocket/{v1_1 => v2}/test_claims.py (100%) rename zaqar/tests/unit/transport/websocket/{v1_1 => v2}/test_messages.py (99%) rename zaqar/tests/unit/transport/websocket/{v1_1 => v2}/test_queue_lifecycle.py (99%) diff --git a/doc/source/api/zaqar.api.v1_1.endpoints.rst b/doc/source/api/zaqar.api.v1_1.endpoints.rst deleted file mode 100644 index 0b997bf38..000000000 --- a/doc/source/api/zaqar.api.v1_1.endpoints.rst +++ /dev/null @@ -1,7 +0,0 @@ -The :mod:`zaqar.api.v1_1.endpoints` module -=========================================== - -.. automodule:: zaqar.api.v1_1.endpoints - :members: - :undoc-members: - :show-inheritance: diff --git a/doc/source/api/zaqar.api.v2.endpoints.rst b/doc/source/api/zaqar.api.v2.endpoints.rst new file mode 100644 index 000000000..14289007d --- /dev/null +++ b/doc/source/api/zaqar.api.v2.endpoints.rst @@ -0,0 +1,7 @@ +The :mod:`zaqar.api.v2.endpoints` module +========================================= + +.. automodule:: zaqar.api.v2.endpoints + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/zaqar.api.v2.request.rst b/doc/source/api/zaqar.api.v2.request.rst new file mode 100644 index 000000000..cda464819 --- /dev/null +++ b/doc/source/api/zaqar.api.v2.request.rst @@ -0,0 +1,7 @@ +The :mod:`zaqar.api.v2.request` module +======================================= + +.. automodule:: zaqar.api.v2.request + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/zaqar.api.v2.response.rst b/doc/source/api/zaqar.api.v2.response.rst new file mode 100644 index 000000000..00749f5dc --- /dev/null +++ b/doc/source/api/zaqar.api.v2.response.rst @@ -0,0 +1,7 @@ +The :mod:`zaqar.api.v2.response` module +======================================== + +.. automodule:: zaqar.api.v2.response + :members: + :undoc-members: + :show-inheritance: diff --git a/zaqar/api/handler.py b/zaqar/api/handler.py index 619de3704..63b7ef2f2 100644 --- a/zaqar/api/handler.py +++ b/zaqar/api/handler.py @@ -12,8 +12,8 @@ # License for the specific language governing permissions and limitations under # the License. -from zaqar.api.v1_1 import endpoints -from zaqar.api.v1_1 import request as schema_validator +from zaqar.api.v2 import endpoints +from zaqar.api.v2 import request as schema_validator from zaqar.common.api import request from zaqar.common.api import response @@ -37,12 +37,12 @@ class Handler(object): } def __init__(self, storage, control, validate, defaults): - self.v1_1_endpoints = endpoints.Endpoints(storage, control, - validate, defaults) + self.v2_endpoints = endpoints.Endpoints(storage, control, + validate, defaults) def process_request(self, req): # FIXME(vkmc): Control API version - return getattr(self.v1_1_endpoints, req._action)(req) + return getattr(self.v2_endpoints, req._action)(req) @staticmethod def validate_request(payload, req): @@ -79,10 +79,10 @@ class Handler(object): headers = payload.get('headers') return request.Request(action=action, body=body, - headers=headers, api="v1.1") + headers=headers, api="v2") def get_defaults(self): - return self.v1_1_endpoints._defaults + return self.v2_endpoints._defaults def verify_signature(self, key, payload): action = payload.get('action') diff --git a/zaqar/api/v1/request.py b/zaqar/api/v1/request.py index c988e26f2..61242531e 100644 --- a/zaqar/api/v1/request.py +++ b/zaqar/api/v1/request.py @@ -18,180 +18,369 @@ from zaqar.common.api import api class RequestSchema(api.Api): + headers = { + 'User-Agent': {'type': 'string'}, + 'Date': {'type': 'string'}, + 'Accept': {'type': 'string'}, + 'Client-ID': {'type': 'string'}, + 'X-Project-ID': {'type': 'string'}, + 'X-Auth-Token': {'type': 'string'} + } + schema = { - 'queue_list': { - 'ref': 'queues', - 'method': 'GET', - 'properties': { - 'marker': {'type': 'string'}, - 'limit': {'type': 'integer'}, - 'detailed': {'type': 'boolean'} - } - }, - 'queue_create': { - 'ref': 'queues/{queue_name}', - 'method': 'PUT', - 'required': ['queue_name'], + # Base + 'get_home_doc': { 'properties': { - 'queue_name': {'type': 'string'} + 'action': {'enum': ['get_home_doc']}, + 'headers': { + 'type': 'object', + 'properties': headers, + } }, - }, - - 'queue_exists': { - 'ref': 'queues/{queue_name}', - 'method': 'HEAD', - 'required': ['queue_name'], - 'properties': { - 'queue_name': {'type': 'string'} - } - }, - - 'queue_delete': { - 'ref': 'queues/{queue_name}', - 'method': 'DELETE', - 'required': ['queue_name'], - 'properties': { - 'queue_name': {'type': 'string'} - } - }, - - 'queue_set_metadata': { - 'ref': 'queues/{queue_name}/metadata', - 'method': 'PUT', - 'required': ['queue_name'], - 'properties': { - # NOTE(flaper87): Metadata is part - # of the request content. No need to - # add it here. - 'queue_name': {'type': 'string'} - } - }, - - 'queue_get_metadata': { - 'ref': 'queues/{queue_name}/metadata', - 'method': 'GET', - 'required': ['queue_name'], - 'properties': { - 'queue_name': {'type': 'string'} - } - }, - - 'queue_get_stats': { - 'ref': 'queues/{queue_name}/stats', - 'method': 'GET', - 'required': ['queue_name'], + 'required': ['action', 'headers'], 'admin': True, - 'properties': { - 'queue_name': {'type': 'string'}, - } - }, - - 'message_list': { - 'ref': 'queues/{queue_name}/messages', - 'method': 'GET', - 'required': ['queue_name'], - 'properties': { - 'queue_name': {'type': 'string'}, - 'marker': {'type': 'string'}, - 'limit': {'type': 'integer'}, - 'echo': {'type': 'boolean'}, - 'include_claimed': {'type': 'boolean'} - } - }, - - 'message_post': { - 'ref': 'queues/{queue_name}/messages', - 'method': 'POST', - 'required': ['queue_name'], - 'properties': { - 'queue_name': {'type': 'string'} - } - }, - - 'message_get': { - 'ref': 'queues/{queue_name}/messages/{message_id}', - 'method': 'GET', - 'required': ['queue_name', 'message_id'], - 'properties': { - 'queue_name': {'type': 'string'}, - 'message_id': {'type': 'string'} - } - }, - - 'message_get_many': { - 'ref': 'queues/{queue_name}/messages', - 'method': 'GET', - 'required': ['queue_name', 'ids'], - 'properties': { - 'queue_name': {'type': 'string'}, - 'ids': {'type': 'array'} - } - }, - - 'message_delete': { - 'ref': 'queues/{queue_name}/messages/{message_id}', - 'method': 'DELETE', - 'required': ['queue_name', 'message_id'], - 'properties': { - 'queue_name': {'type': 'string'}, - 'message_id': {'type': 'string'}, - 'claim_id': {'type': 'string'} - } - }, - - 'message_delete_many': { - 'ref': 'queues/{queue_name}/messages', - 'method': 'DELETE', - 'required': ['queue_name', 'ids'], - 'properties': { - 'queue_name': {'type': 'string'}, - 'ids': {'type': 'array'} - } - }, - - 'claim_create': { - 'ref': 'queues/{queue_name}/claims', - 'method': 'POST', - 'required': ['queue_name'], - 'properties': { - 'queue_name': {'type': 'string'}, - 'limit': {'type': 'integer'} - } - }, - - 'claim_get': { - 'ref': 'queues/{queue_name}/claims/{claim_id}', - 'method': 'GET', - 'required': ['queue_name', 'claim_id'], - 'properties': { - 'queue_name': {'type': 'string'}, - 'claim_id': {'type': 'string'} - } - }, - - 'claim_update': { - 'ref': 'queues/{queue_name}/claims/{claim_id}', - 'method': 'PATCH', - 'required': ['queue_name', 'claim_id'], - 'properties': { - 'queue_name': {'type': 'string'}, - 'claim_id': {'type': 'string'} - } - }, - - 'claim_delete': { - 'ref': 'queues/{queue_name}/claims/{claim_id}', - 'method': 'DELETE', - 'required': ['queue_name', 'claim_id'], - 'properties': { - 'queue_name': {'type': 'string'}, - 'claim_id': {'type': 'string'} - } }, 'check_node_health': { - 'ref': '/v1/health', - 'method': 'GET', + 'properties': { + 'action': {'enum': ['check_node_health']}, + 'headers': { + 'type': 'object', + 'properties': headers, + } + }, + 'required': ['action', 'headers'], + 'admin': True, + }, + + 'ping_node': { + 'properties': { + 'action': {'enum': ['ping_node']}, + 'headers': { + 'type': 'object', + 'properties': headers, + } + }, + 'required': ['action', 'headers'], + 'admin': True, + }, + 'authenticate': { + 'properties': { + 'action': {'enum': ['authenticate']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['X-Project-ID', 'X-Auth-Token'] + } + }, + 'required': ['action', 'headers'], + }, + + # Queues + 'queue_list': { + 'properties': { + 'action': {'enum': ['queue_list']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'marker': {'type': 'string'}, + 'limit': {'type': 'integer'}, + 'detailed': {'type': 'boolean'} + } + } + }, + 'required': ['action', 'headers'] + }, + + 'queue_create': { + 'properties': { + 'action': {'enum': ['queue_create']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID']}, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + }, + 'required': ['queue_name'], + } + }, + 'required': ['action', 'headers', 'body'] + }, + + 'queue_delete': { + 'properties': { + 'action': {'enum': ['queue_delete']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + }, + 'required': ['queue_name'] + } + }, + 'required': ['action', 'headers', 'body'] + }, + + 'queue_get': { + 'properties': { + 'action': {'enum': ['queue_get']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + }, + 'required': ['queue_name'], + } + }, + 'required': ['action', 'headers', 'body'] + }, + + 'queue_get_stats': { + 'properties': { + 'action': {'enum': ['queue_get_stats']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + }, + 'required': ['queue_name'], + } + }, + 'required': ['action', 'headers', 'body'], + 'admin': True + }, + + # Messages + 'message_list': { + 'properties': { + 'action': {'enum': ['message_list']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + 'marker': {'type': 'string'}, + 'limit': {'type': 'integer'}, + 'echo': {'type': 'boolean'}, + 'include_claimed': {'type': 'boolean'}, + }, + 'required': ['queue_name'], + } + }, + 'required': ['action', 'headers', 'body'] + }, + + 'message_get': { + 'properties': { + 'action': {'enum': ['message_get']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + 'message_id': {'type': 'string'}, + }, + 'required': ['queue_name', 'message_id'], + } + }, + 'required': ['action', 'headers', 'body'] + }, + + 'message_get_many': { + 'properties': { + 'action': {'enum': ['message_get_many']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + 'message_ids': {'type': 'array'}, + }, + 'required': ['queue_name', 'message_ids'], + } + }, + 'required': ['action', 'headers', 'body'] + }, + + 'message_post': { + 'properties': { + 'action': {'enum': ['message_post']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + 'messages': {'type': 'array'}, + }, + 'required': ['queue_name', 'messages'], + } + }, + 'required': ['action', 'headers', 'body'] + }, + + 'message_delete': { + 'properties': { + 'action': {'enum': ['message_delete']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + 'message_id': {'type': 'string'}, + 'claim_id': {'type': 'string'} + }, + 'required': ['queue_name', 'message_id'], + } + }, + 'required': ['action', 'headers', 'body'] + }, + + 'message_delete_many': { + 'properties': { + 'action': {'enum': ['message_delete_many']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + 'message_ids': {'type': 'array'}, + 'pop': {'type': 'integer'} + }, + 'required': ['queue_name', 'message_ids'], + } + }, + 'required': ['action', 'headers', 'body'] + }, + + # Claims + 'claim_create': { + 'properties': { + 'action': {'enum': ['claim_create']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + 'limit': {'type': 'integer'}, + 'ttl': {'type': 'integer'}, + 'grace': {'type': 'integer'} + }, + 'required': ['queue_name'], + } + }, + 'required': ['action', 'headers', 'body'] + }, + + 'claim_get': { + 'properties': { + 'action': {'enum': ['claim_get']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + 'claim_id': {'type': 'string'} + }, + 'required': ['queue_name', 'claim_id'], + } + }, + 'required': ['action', 'headers', 'body'] + }, + + 'claim_update': { + 'properties': { + 'action': {'enum': ['claim_update']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + 'claim_id': {'type': 'string'}, + 'ttl': {'type': 'integer'} + }, + 'required': ['queue_name', 'claim_id'], + } + }, + 'required': ['action', 'headers', 'body'] + }, + + 'claim_delete': { + 'properties': { + 'action': {'enum': ['claim_delete']}, + 'headers': { + 'type': 'object', + 'properties': headers, + 'required': ['Client-ID', 'X-Project-ID'] + }, + 'body': { + 'type': 'object', + 'properties': { + 'queue_name': {'type': 'string'}, + 'claim_id': {'type': 'string'} + }, + 'required': ['queue_name', 'claim_id'], + } + }, + 'required': ['action', 'headers', 'body'] }, } diff --git a/zaqar/api/v1_1/request.py b/zaqar/api/v1_1/request.py index 929fa7117..238e2a0aa 100644 --- a/zaqar/api/v1_1/request.py +++ b/zaqar/api/v1_1/request.py @@ -13,376 +13,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -from zaqar.common.api import api +from zaqar.api.v1 import request as v1 -class RequestSchema(api.Api): +class RequestSchema(v1.RequestSchema): - headers = { - 'User-Agent': {'type': 'string'}, - 'Date': {'type': 'string'}, - 'Accept': {'type': 'string'}, - 'Client-ID': {'type': 'string'}, - 'X-Project-ID': {'type': 'string'}, - 'X-Auth-Token': {'type': 'string'} - } + headers = v1.RequestSchema.headers + schema = v1.RequestSchema.schema - schema = { - - # Base - 'get_home_doc': { - 'properties': { - 'action': {'enum': ['get_home_doc']}, - 'headers': { - 'type': 'object', - 'properties': headers, - } - }, - 'required': ['action', 'headers'], - 'admin': True, - }, - - 'check_node_health': { - 'properties': { - 'action': {'enum': ['check_node_health']}, - 'headers': { - 'type': 'object', - 'properties': headers, - } - }, - 'required': ['action', 'headers'], - 'admin': True, - }, - - 'ping_node': { - 'properties': { - 'action': {'enum': ['ping_node']}, - 'headers': { - 'type': 'object', - 'properties': headers, - } - }, - 'required': ['action', 'headers'], - 'admin': True, - }, - 'authenticate': { - 'properties': { - 'action': {'enum': ['authenticate']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['X-Project-ID', 'X-Auth-Token'] - } - }, - 'required': ['action', 'headers'], - }, - - # Queues - 'queue_list': { - 'properties': { - 'action': {'enum': ['queue_list']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'marker': {'type': 'string'}, - 'limit': {'type': 'integer'}, - 'detailed': {'type': 'boolean'} - } - } - }, - 'required': ['action', 'headers'] - }, - - 'queue_create': { - 'properties': { - 'action': {'enum': ['queue_create']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID']}, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - }, - 'required': ['queue_name'], - } - }, - 'required': ['action', 'headers', 'body'] - }, - - 'queue_delete': { - 'properties': { - 'action': {'enum': ['queue_delete']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - }, - 'required': ['queue_name'] - } - }, - 'required': ['action', 'headers', 'body'] - }, - - 'queue_get': { - 'properties': { - 'action': {'enum': ['queue_get']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - }, - 'required': ['queue_name'], - } - }, - 'required': ['action', 'headers', 'body'] - }, - - 'queue_get_stats': { - 'properties': { - 'action': {'enum': ['queue_get_stats']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - }, - 'required': ['queue_name'], - } - }, - 'required': ['action', 'headers', 'body'], - 'admin': True - }, - - # Messages - 'message_list': { - 'properties': { - 'action': {'enum': ['message_list']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - 'marker': {'type': 'string'}, - 'limit': {'type': 'integer'}, - 'echo': {'type': 'boolean'}, - 'include_claimed': {'type': 'boolean'}, - }, - 'required': ['queue_name'], - } - }, - 'required': ['action', 'headers', 'body'] - }, - - 'message_get': { - 'properties': { - 'action': {'enum': ['message_get']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - 'message_id': {'type': 'string'}, - }, - 'required': ['queue_name', 'message_id'], - } - }, - 'required': ['action', 'headers', 'body'] - }, - - 'message_get_many': { - 'properties': { - 'action': {'enum': ['message_get_many']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - 'message_ids': {'type': 'array'}, - }, - 'required': ['queue_name', 'message_ids'], - } - }, - 'required': ['action', 'headers', 'body'] - }, - - 'message_post': { - 'properties': { - 'action': {'enum': ['message_post']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - 'messages': {'type': 'array'}, - }, - 'required': ['queue_name', 'messages'], - } - }, - 'required': ['action', 'headers', 'body'] - }, - - 'message_delete': { - 'properties': { - 'action': {'enum': ['message_delete']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - 'message_id': {'type': 'string'}, - 'claim_id': {'type': 'string'} - }, - 'required': ['queue_name', 'message_id'], - } - }, - 'required': ['action', 'headers', 'body'] - }, - - 'message_delete_many': { - 'properties': { - 'action': {'enum': ['message_delete_many']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - 'message_ids': {'type': 'array'}, - 'pop': {'type': 'integer'} - }, - 'required': ['queue_name', 'message_ids'], - } - }, - 'required': ['action', 'headers', 'body'] - }, - - # Claims - 'claim_create': { - 'properties': { - 'action': {'enum': ['claim_create']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - 'limit': {'type': 'integer'}, - 'ttl': {'type': 'integer'}, - 'grace': {'type': 'integer'} - }, - 'required': ['queue_name'], - } - }, - 'required': ['action', 'headers', 'body'] - }, - - 'claim_get': { - 'properties': { - 'action': {'enum': ['claim_get']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - 'claim_id': {'type': 'string'} - }, - 'required': ['queue_name', 'claim_id'], - } - }, - 'required': ['action', 'headers', 'body'] - }, - - 'claim_update': { - 'properties': { - 'action': {'enum': ['claim_update']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - 'claim_id': {'type': 'string'}, - 'ttl': {'type': 'integer'} - }, - 'required': ['queue_name', 'claim_id'], - } - }, - 'required': ['action', 'headers', 'body'] - }, - - 'claim_delete': { - 'properties': { - 'action': {'enum': ['claim_delete']}, - 'headers': { - 'type': 'object', - 'properties': headers, - 'required': ['Client-ID', 'X-Project-ID'] - }, - 'body': { - 'type': 'object', - 'properties': { - 'queue_name': {'type': 'string'}, - 'claim_id': {'type': 'string'} - }, - 'required': ['queue_name', 'claim_id'], - } - }, - 'required': ['action', 'headers', 'body'] - }, + schema.update({ # Pools 'pool_list': { @@ -601,4 +240,4 @@ class RequestSchema(api.Api): 'required': ['action', 'headers', 'body'], 'admin': True, }, - } + }) diff --git a/zaqar/tests/unit/transport/websocket/v1_1/__init__.py b/zaqar/api/v2/__init__.py similarity index 100% rename from zaqar/tests/unit/transport/websocket/v1_1/__init__.py rename to zaqar/api/v2/__init__.py diff --git a/zaqar/api/v1_1/endpoints.py b/zaqar/api/v2/endpoints.py similarity index 99% rename from zaqar/api/v1_1/endpoints.py rename to zaqar/api/v2/endpoints.py index 43de3a52e..eadc4dd6a 100644 --- a/zaqar/api/v1_1/endpoints.py +++ b/zaqar/api/v2/endpoints.py @@ -25,7 +25,7 @@ LOG = logging.getLogger(__name__) class Endpoints(object): - """v1.1 API Endpoints.""" + """v2 API Endpoints.""" def __init__(self, storage, control, validate, defaults): self._queue_controller = storage.queue_controller diff --git a/zaqar/api/v2/request.py b/zaqar/api/v2/request.py new file mode 100644 index 000000000..c7385ff6a --- /dev/null +++ b/zaqar/api/v2/request.py @@ -0,0 +1,22 @@ +# 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 zaqar.api.v1_1 import request as v1_1 + + +class RequestSchema(v1_1.RequestSchema): + + headers = v1_1.RequestSchema.headers + schema = v1_1.RequestSchema.schema diff --git a/zaqar/api/v2/response.py b/zaqar/api/v2/response.py new file mode 100644 index 000000000..39a356557 --- /dev/null +++ b/zaqar/api/v2/response.py @@ -0,0 +1,410 @@ +# 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. + +from zaqar.common.api import api + + +class ResponseSchema(api.Api): + + """Define validation schema for json response.""" + + def __init__(self, limits): + self.limits = limits + + age = { + "type": "number", + "minimum": 0 + } + + message = { + "type": "object", + "properties": { + "id": { + "type": "string", + }, + "href": { + "type": "string", + "pattern": "^(/v1/queues/[a-zA-Z0-9_-]{1,64}" + "/messages/[a-zA-Z0-9_-]+)(\?claim_id=[a-zA-Z0-9_-]+)?$" + }, + "age": age, + "ttl": { + "type": "number", + "minimum": 1, + "maximum": self.limits.max_message_ttl + }, + + "body": { + "type": "object" + } + }, + "required": ["href", "ttl", "age", "body", "id"], + "additionalProperties": False, + } + + claim_href = { + "type": "string", + "pattern": "^(/v2/queues/[a-zA-Z0-9_-]{1,64}" + "/messages/[a-zA-Z0-9_-]+)" + "\?claim_id=[a-zA-Z0-9_-]+$" + } + + flavor = { + 'type': 'object', + 'properties': { + 'href': { + 'type': 'string', + 'pattern': '^/v2/flavors/[a-zA-Z0-9_-]{1,64}$' + }, + 'pool': { + 'type': 'string', + }, + 'project': { + 'type': 'string' + }, + 'capabilities': { + 'type': 'object', + 'additionalProperties': True + } + }, + 'required': ['href', 'pool', 'project'], + 'additionalProperties': False, + } + + self.schema = { + 'message_get_many': { + 'type': 'object', + 'properties': { + 'messages': { + "type": "array", + "items": message, + "minItems": 1, + "maxItems": self.limits.max_messages_per_page + } + }, + 'required': ['messages'], + 'additionalProperties': False, + }, + + 'queue_list': { + 'type': 'object', + 'properties': { + 'links': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'rel': { + 'type': 'string', + 'enum': ['next'], + }, + 'href': { + 'type': 'string', + "pattern": "^/v2/queues\?", + } + }, + 'required': ['rel', 'href'], + 'additionalProperties': False, + }, + 'minItems': 1, + 'maxItems': 1, + }, + + 'queues': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'name': { + 'type': 'string', + 'pattern': '^[a-zA-Z0-9_-]{1,64}$' + }, + 'href': { + 'type': 'string', + 'pattern': '^/v2/queues/' + '[a-zA-Z0-9_-]{1,64}$', + }, + 'metadata': { + 'type': 'object', + } + }, + 'required': ['name', 'href'], + 'additionalProperties': False, + }, + 'minItems': 1, + 'maxItems': self.limits.max_queues_per_page, + } + }, + 'required': ['links', 'queues'], + 'additionalProperties': False, + }, + + 'queue_stats': { + 'type': 'object', + 'properties': { + 'messages': { + 'type': 'object', + 'properties': { + 'free': { + 'type': 'number', + 'minimum': 0 + }, + 'claimed': { + 'type': 'number', + 'minimum': 0 + }, + 'total': { + 'type': 'number', + 'minimum': 0 + }, + 'oldest': { + 'type': 'object' + }, + 'newest': { + 'type': 'object' + } + + }, + 'required': ['free', 'claimed', 'total'], + 'additionalProperties': False + } + }, + 'required': ['messages'], + 'additionalProperties': False + }, + + 'pool_list': { + 'type': 'object', + 'properties': { + 'links': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'rel': { + 'type': 'string' + }, + 'href': { + 'type': 'string', + 'pattern': '^/v2/pools\?' + } + }, + 'required': ['rel', 'href'], + 'additionalProperties': False + } + }, + 'pools': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'href': { + 'type': 'string', + 'pattern': '^/v2/' + 'pools/[a-zA-Z0-9_-]{1,64}$' + }, + 'weight': { + 'type': 'number', + 'minimum': -1 + }, + 'name': { + 'type': 'string' + }, + 'uri': { + 'type': 'string' + }, + 'group': { + 'type': ['string', 'null'] + }, + 'options': { + 'type': 'object', + 'additionalProperties': True + } + }, + 'required': ['href', 'weight', 'uri', 'group'], + 'additionalProperties': False, + }, + } + }, + 'required': ['links', 'pools'], + 'additionalProperties': False + }, + + 'message_list': { + 'type': 'object', + 'properties': { + 'links': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'rel': { + 'type': 'string' + }, + 'href': { + 'type': 'string', + 'pattern': '^/v2/queues/[a-zA-Z0-9_-]+' + '/messages\?(.)*$' + } + }, + 'required': ['rel', 'href'], + 'additionalProperties': False + } + }, + 'messages': { + "type": "array", + "items": message, + "minItems": 0, + "maxItems": self.limits.max_messages_per_claim_or_pop + } + } + }, + 'pool_detail': { + 'type': 'object', + 'properties': { + 'name': { + 'type': 'string' + }, + 'uri': { + 'type': 'string' + }, + 'group': { + 'type': ['string', 'null'] + }, + 'weight': { + 'type': 'number', + 'minimum': -1 + }, + 'href': { + 'type': 'string', + 'pattern': '^/v2/pools/' + '[a-zA-Z0-9_\-]+$' + }, + 'options': { + 'type': 'object', + 'additionalProperties': True + } + }, + 'required': ['uri', 'weight', 'href'], + 'additionalProperties': False + }, + + 'claim_create': { + 'type': 'object', + 'properties': { + 'messages': { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + }, + "href": claim_href, + "ttl": { + "type": "number", + "minimum": 1, + "maximum": self.limits.max_message_ttl + }, + "age": age, + "body": { + "type": "object" + } + }, + "required": ["href", "ttl", "age", "body", "id"], + "additionalProperties": False, + }, + "minItems": 1, + "maxItems": self.limits.max_messages_per_page + } + }, + 'required': ['messages'], + 'additionalProperties': False + }, + + 'claim_get': { + 'type': 'object', + 'properties': { + 'age': age, + 'ttl': { + 'type': 'number', + 'minimum': 0, + 'maximum': self.limits.max_claim_ttl + }, + 'href': { + 'type': 'string', + 'pattern': '^/v2/queues/[a-zA-Z0-9_-]+' + '/claims/[a-zA-Z0-9_-]+$' + }, + 'messages': { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + }, + "href": claim_href, + "ttl": { + "type": "number", + "minimum": 1, + "maximum": self.limits.max_message_ttl + }, + "age": age, + "body": { + "type": "object" + } + }, + "required": ["href", "ttl", "age", "body", "id"], + "additionalProperties": False, + }, + "minItems": 1, + "maxItems": self.limits.max_messages_per_page + } + }, + 'required': ['age', 'ttl', 'messages', 'href'], + 'additionalProperties': False + }, + + 'flavor_list': { + 'type': 'object', + 'properties': { + 'links': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'rel': { + 'type': 'string' + }, + 'href': { + 'type': 'string', + 'pattern': '^/v2/flavors\?' + } + }, + 'required': ['rel', 'href'], + 'additionalProperties': False + } + }, + 'flavors': { + 'type': 'array', + 'items': flavor, + } + }, + 'required': ['links', 'flavors'], + 'additionalProperties': False + } + + } diff --git a/zaqar/tests/unit/transport/websocket/v2/__init__.py b/zaqar/tests/unit/transport/websocket/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/zaqar/tests/unit/transport/websocket/v1_1/test_auth.py b/zaqar/tests/unit/transport/websocket/v2/test_auth.py similarity index 99% rename from zaqar/tests/unit/transport/websocket/v1_1/test_auth.py rename to zaqar/tests/unit/transport/websocket/v2/test_auth.py index e625af79b..8b766fc12 100644 --- a/zaqar/tests/unit/transport/websocket/v1_1/test_auth.py +++ b/zaqar/tests/unit/transport/websocket/v2/test_auth.py @@ -24,7 +24,7 @@ from zaqar.tests.unit.transport.websocket import base from zaqar.tests.unit.transport.websocket import utils as test_utils -class AuthTest(base.V1_1Base): +class AuthTest(base.V2Base): config_file = "websocket_mongodb_keystone_auth.conf" diff --git a/zaqar/tests/unit/transport/websocket/v1_1/test_claims.py b/zaqar/tests/unit/transport/websocket/v2/test_claims.py similarity index 100% rename from zaqar/tests/unit/transport/websocket/v1_1/test_claims.py rename to zaqar/tests/unit/transport/websocket/v2/test_claims.py diff --git a/zaqar/tests/unit/transport/websocket/v1_1/test_messages.py b/zaqar/tests/unit/transport/websocket/v2/test_messages.py similarity index 99% rename from zaqar/tests/unit/transport/websocket/v1_1/test_messages.py rename to zaqar/tests/unit/transport/websocket/v2/test_messages.py index a3777ef79..f13e80ede 100644 --- a/zaqar/tests/unit/transport/websocket/v1_1/test_messages.py +++ b/zaqar/tests/unit/transport/websocket/v2/test_messages.py @@ -29,7 +29,7 @@ from zaqar.transport import validation @ddt.ddt -class MessagesBaseTest(base.V1_1Base): +class MessagesBaseTest(base.V2Base): config_file = "websocket_mongodb.conf" @@ -583,5 +583,5 @@ class MessagesBaseTest(base.V1_1Base): self.assertIn('error', response['body']) self.assertEqual({'status': 400}, response['headers']) self.assertEqual( - {'action': None, 'api': 'v1.1', 'body': {}, 'headers': {}}, + {'action': None, 'api': 'v2', 'body': {}, 'headers': {}}, response['request']) diff --git a/zaqar/tests/unit/transport/websocket/v1_1/test_queue_lifecycle.py b/zaqar/tests/unit/transport/websocket/v2/test_queue_lifecycle.py similarity index 99% rename from zaqar/tests/unit/transport/websocket/v1_1/test_queue_lifecycle.py rename to zaqar/tests/unit/transport/websocket/v2/test_queue_lifecycle.py index ee113e9bb..8510c4c0e 100644 --- a/zaqar/tests/unit/transport/websocket/v1_1/test_queue_lifecycle.py +++ b/zaqar/tests/unit/transport/websocket/v2/test_queue_lifecycle.py @@ -24,7 +24,7 @@ from zaqar.tests.unit.transport.websocket import utils as test_utils @ddt.ddt -class QueueLifecycleBaseTest(base.V1_1Base): +class QueueLifecycleBaseTest(base.V2Base): config_file = "websocket_mongodb.conf"