api: Add schemas for resource_locks
This is a mostly complete example showing request body, request query string and response body schemas in action. The only thing not included yet is descriptions for fields, which is still being worked on. Change-Id: I14db582eec6db25ea5437675f8207dcf94228b25 Signed-off-by: Stephen Finucane <stephenfin@redhat.com> Partially-implements: bp json-schema-validation
This commit is contained in:
parent
05279a6fe7
commit
9be7243e70
|
@ -0,0 +1,240 @@
|
|||
# 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 oslo_config import cfg
|
||||
|
||||
from manila.api.validation import parameter_types
|
||||
from manila.api.validation import response_types
|
||||
from manila.common import constants
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
# TODO(stephenfin): Reject additional properties in a future microversion
|
||||
create_request_body = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'resource_lock': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'resource_id': {
|
||||
'type': 'string',
|
||||
'format': 'uuid',
|
||||
},
|
||||
'lock_reason': {
|
||||
'type': ['string', 'null'],
|
||||
'maxLength': 1023,
|
||||
},
|
||||
'resource_type': {
|
||||
'type': ['string', 'null'],
|
||||
'enum': constants.RESOURCE_LOCK_RESOURCE_TYPES + (None,),
|
||||
'default': constants.SHARE_RESOURCE_TYPE,
|
||||
},
|
||||
'resource_action': {
|
||||
'type': ['string', 'null'],
|
||||
'enum': constants.RESOURCE_LOCK_RESOURCE_ACTIONS + (None,),
|
||||
'default': constants.RESOURCE_ACTION_DELETE,
|
||||
},
|
||||
},
|
||||
'required': ['resource_id'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
'required': ['resource_lock'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
update_request_body = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'resource_lock': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'resource_action': {
|
||||
'type': ['string', 'null'],
|
||||
'enum': constants.RESOURCE_LOCK_RESOURCE_ACTIONS + (None,),
|
||||
},
|
||||
'lock_reason': {
|
||||
'type': ['string', 'null'],
|
||||
'maxLength': 1023,
|
||||
},
|
||||
},
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
'required': ['resource_lock'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
index_request_query = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'limit': parameter_types.multi_params(
|
||||
parameter_types.non_negative_integer
|
||||
),
|
||||
'marker': parameter_types.multi_params({
|
||||
'type': ['string'],
|
||||
}),
|
||||
'offset': parameter_types.multi_params(
|
||||
parameter_types.non_negative_integer
|
||||
),
|
||||
'sort_key': parameter_types.multi_params({
|
||||
'type': 'string',
|
||||
'default': 'created_at',
|
||||
}),
|
||||
# TODO(stephenfin): Make this an enum of ['asc', 'desc']
|
||||
'sort_dir': parameter_types.multi_params({
|
||||
'type': 'string',
|
||||
'default': 'desc',
|
||||
}),
|
||||
'with_count': parameter_types.multi_params(parameter_types.boolean),
|
||||
'created_since': parameter_types.multi_params({
|
||||
'type': 'string',
|
||||
'format': 'date-time',
|
||||
}),
|
||||
'created_before': parameter_types.multi_params({
|
||||
'type': 'string',
|
||||
'format': 'date-time',
|
||||
}),
|
||||
'project_id': parameter_types.multi_params({
|
||||
'type': ['string', 'null'],
|
||||
'format': 'uuid',
|
||||
}),
|
||||
'user_id': parameter_types.multi_params({
|
||||
'type': ['string', 'null'],
|
||||
'format': 'uuid',
|
||||
}),
|
||||
'resource_id': parameter_types.multi_params({
|
||||
'type': ['string', 'null'],
|
||||
'format': 'uuid',
|
||||
}),
|
||||
'resource_action': parameter_types.multi_params({
|
||||
'type': ['string', 'null'],
|
||||
'enum': constants.RESOURCE_LOCK_RESOURCE_ACTIONS + (None,),
|
||||
}),
|
||||
'resource_type': parameter_types.multi_params({
|
||||
'type': ['string', 'null'],
|
||||
'enum': constants.RESOURCE_LOCK_RESOURCE_TYPES + (None,),
|
||||
}),
|
||||
'all_projects': parameter_types.multi_params(parameter_types.boolean),
|
||||
'lock_context': parameter_types.multi_params({
|
||||
'type': ['string', 'null'],
|
||||
'maxLength': 10,
|
||||
}),
|
||||
'lock_reason': parameter_types.multi_params({
|
||||
'type': ['string', 'null'],
|
||||
'maxLength': 1023,
|
||||
}),
|
||||
},
|
||||
# TODO(stephenfin): Exclude additional query string parameters in a future
|
||||
# microversion
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
show_request_query = {
|
||||
'type': 'object',
|
||||
'properties': {},
|
||||
# TODO(stephenfin): Exclude additional query string parameters in a future
|
||||
# microversion
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
_resource_lock_response = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {
|
||||
'type': 'string',
|
||||
'format': 'uuid',
|
||||
},
|
||||
'user_id': {
|
||||
'type': 'string',
|
||||
'format': 'uuid',
|
||||
},
|
||||
'project_id': {
|
||||
'type': 'string',
|
||||
'format': 'uuid',
|
||||
},
|
||||
'lock_context': {
|
||||
'type': 'string',
|
||||
},
|
||||
'resource_type': {
|
||||
'type': 'string',
|
||||
'enum': constants.RESOURCE_LOCK_RESOURCE_TYPES,
|
||||
},
|
||||
'resource_id': {
|
||||
'type': 'string',
|
||||
'format': 'uuid',
|
||||
},
|
||||
'resource_action': {
|
||||
'type': 'string',
|
||||
'enum': constants.RESOURCE_LOCK_RESOURCE_ACTIONS,
|
||||
},
|
||||
'lock_reason': {
|
||||
'type': ['string', 'null'],
|
||||
},
|
||||
'created_at': {
|
||||
'type': 'string',
|
||||
'format': 'date-time',
|
||||
},
|
||||
'updated_at': {
|
||||
'type': ['string', 'null'],
|
||||
'format': 'date-time',
|
||||
},
|
||||
'links': response_types.links,
|
||||
},
|
||||
}
|
||||
|
||||
create_response_body = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'resource_lock': _resource_lock_response,
|
||||
},
|
||||
'required': ['resource_lock'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
index_response_body = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'resource_locks': {
|
||||
'type': 'array',
|
||||
'items': _resource_lock_response,
|
||||
},
|
||||
'count': {
|
||||
'type': 'integer',
|
||||
},
|
||||
'resource_locks_links': response_types.collection_links,
|
||||
},
|
||||
'required': ['resource_locks'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
show_response_body = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'resource_lock': _resource_lock_response,
|
||||
},
|
||||
'required': ['resource_lock'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
update_response_body = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'resource_lock': _resource_lock_response,
|
||||
},
|
||||
'required': ['resource_lock'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
delete_response_body = {
|
||||
'type': 'null',
|
||||
}
|
|
@ -29,6 +29,8 @@ from webob import exc
|
|||
|
||||
from manila.api import common
|
||||
from manila.api.openstack import wsgi
|
||||
from manila.api.schemas import resource_locks as schema
|
||||
from manila.api import validation
|
||||
from manila.api.views import resource_locks as resource_locks_view
|
||||
from manila.common import constants
|
||||
from manila import exception
|
||||
|
@ -39,6 +41,7 @@ from manila import utils
|
|||
RESOURCE_LOCKS_MIN_API_VERSION = '2.81'
|
||||
|
||||
|
||||
@validation.validated
|
||||
class ResourceLocksController(wsgi.Controller):
|
||||
"""The Resource Locks API controller for the OpenStack API."""
|
||||
|
||||
|
@ -92,6 +95,8 @@ class ResourceLocksController(wsgi.Controller):
|
|||
|
||||
@wsgi.Controller.api_version(RESOURCE_LOCKS_MIN_API_VERSION)
|
||||
@wsgi.Controller.authorize('get_all')
|
||||
@validation.request_query_schema(schema.index_request_query)
|
||||
@validation.response_body_schema(schema.index_response_body)
|
||||
def index(self, req):
|
||||
"""Returns a list of locks, transformed through view builder."""
|
||||
context = req.environ['manila.context']
|
||||
|
@ -131,6 +136,8 @@ class ResourceLocksController(wsgi.Controller):
|
|||
|
||||
@wsgi.Controller.api_version(RESOURCE_LOCKS_MIN_API_VERSION)
|
||||
@wsgi.Controller.authorize('get')
|
||||
@validation.request_query_schema(schema.show_request_query)
|
||||
@validation.response_body_schema(schema.show_response_body)
|
||||
def show(self, req, id):
|
||||
"""Return an existing resource lock by ID."""
|
||||
context = req.environ['manila.context']
|
||||
|
@ -143,6 +150,7 @@ class ResourceLocksController(wsgi.Controller):
|
|||
@wsgi.Controller.api_version(RESOURCE_LOCKS_MIN_API_VERSION)
|
||||
@wsgi.Controller.authorize
|
||||
@wsgi.action("delete")
|
||||
@validation.response_body_schema(schema.delete_response_body)
|
||||
def delete(self, req, id):
|
||||
"""Delete an existing resource lock."""
|
||||
context = req.environ['manila.context']
|
||||
|
@ -154,6 +162,8 @@ class ResourceLocksController(wsgi.Controller):
|
|||
|
||||
@wsgi.Controller.api_version(RESOURCE_LOCKS_MIN_API_VERSION)
|
||||
@wsgi.Controller.authorize
|
||||
@validation.request_body_schema(schema.create_request_body)
|
||||
@validation.response_body_schema(schema.create_response_body)
|
||||
def create(self, req, body):
|
||||
"""Create a resource lock."""
|
||||
context = req.environ['manila.context']
|
||||
|
@ -180,6 +190,8 @@ class ResourceLocksController(wsgi.Controller):
|
|||
|
||||
@wsgi.Controller.api_version(RESOURCE_LOCKS_MIN_API_VERSION)
|
||||
@wsgi.Controller.authorize
|
||||
@validation.request_body_schema(schema.update_request_body)
|
||||
@validation.response_body_schema(schema.update_response_body)
|
||||
def update(self, req, id, body):
|
||||
"""Update an existing resource lock."""
|
||||
context = req.environ['manila.context']
|
||||
|
|
|
@ -10,7 +10,22 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Common parameter types for validating API requests/responses."""
|
||||
"""Common parameter types for validating API requests."""
|
||||
|
||||
from manila.common import constants
|
||||
|
||||
|
||||
def single_param(schema):
|
||||
"""Macro function to support query params that allow only one value."""
|
||||
ret = multi_params(schema)
|
||||
ret['maxItems'] = 1
|
||||
return ret
|
||||
|
||||
|
||||
def multi_params(schema):
|
||||
"""Macro function to support query params that allow multiple values."""
|
||||
return {'type': 'array', 'items': schema}
|
||||
|
||||
|
||||
boolean = {
|
||||
'type': ['boolean', 'string'],
|
||||
|
@ -43,3 +58,19 @@ boolean = {
|
|||
'f',
|
||||
],
|
||||
}
|
||||
|
||||
positive_integer = {
|
||||
'type': ['integer', 'string'],
|
||||
'pattern': '^[0-9]*$',
|
||||
'minimum': 1,
|
||||
'maximum': constants.DB_MAX_INT,
|
||||
'minLength': 1,
|
||||
}
|
||||
|
||||
non_negative_integer = {
|
||||
'type': ['integer', 'string'],
|
||||
'pattern': '^[0-9]*$',
|
||||
'minimum': 0,
|
||||
'maximum': constants.DB_MAX_INT,
|
||||
'minLength': 1,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
# 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.
|
||||
|
||||
"""Common field types for validating API responses."""
|
||||
|
||||
links = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'rel': {
|
||||
'type': 'string',
|
||||
'enum': ['self', 'bookmark'],
|
||||
},
|
||||
'href': {
|
||||
'type': 'string',
|
||||
'format': 'uri',
|
||||
},
|
||||
},
|
||||
'required': ['rel', 'href'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
}
|
||||
|
||||
collection_links = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'rel': {
|
||||
'const': 'next',
|
||||
},
|
||||
'href': {
|
||||
'type': 'string',
|
||||
'format': 'uri',
|
||||
},
|
||||
},
|
||||
'required': ['rel', 'href'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
# there should be one and only one link object
|
||||
'minItems': 1,
|
||||
'maxItems': 1,
|
||||
}
|
|
@ -272,8 +272,8 @@ RESOURCE_LOCK_RESOURCE_ACTIONS = (
|
|||
)
|
||||
|
||||
RESOURCE_LOCK_ACTIONS_MAPPING = {
|
||||
"share": [RESOURCE_ACTION_DELETE],
|
||||
"access_rule": [RESOURCE_ACTION_DELETE, RESOURCE_ACTION_SHOW],
|
||||
SHARE_RESOURCE_TYPE: [RESOURCE_ACTION_DELETE],
|
||||
SHARE_ACCESS_RESOURCE_TYPE: [RESOURCE_ACTION_DELETE, RESOURCE_ACTION_SHOW],
|
||||
}
|
||||
|
||||
DISALLOWED_STATUS_WHEN_LOCKING_SHARES = (
|
||||
|
|
|
@ -209,7 +209,7 @@ class ResourceLockApiTest(test.TestCase):
|
|||
url, version=resource_locks.RESOURCE_LOCKS_MIN_API_VERSION)
|
||||
req.environ['manila.context'] = self.ctxt
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.assertRaises(exception.ValidationError,
|
||||
self.controller.index,
|
||||
req)
|
||||
|
||||
|
@ -226,7 +226,7 @@ class ResourceLockApiTest(test.TestCase):
|
|||
url, version=resource_locks.RESOURCE_LOCKS_MIN_API_VERSION)
|
||||
req.environ['manila.context'] = self.ctxt
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.assertRaises(exception.ValidationError,
|
||||
self.controller.index,
|
||||
req)
|
||||
|
||||
|
@ -343,7 +343,7 @@ class ResourceLockApiTest(test.TestCase):
|
|||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create,
|
||||
self.req,
|
||||
body)
|
||||
body=body)
|
||||
|
||||
def test_create_visibility_already_locked(self):
|
||||
self.mock_object(self.controller, '_check_body')
|
||||
|
@ -364,7 +364,7 @@ class ResourceLockApiTest(test.TestCase):
|
|||
self.assertRaises(webob.exc.HTTPConflict,
|
||||
self.controller.create,
|
||||
self.req,
|
||||
body)
|
||||
body=body)
|
||||
|
||||
def test_create(self):
|
||||
self.mock_object(self.controller, '_check_body')
|
||||
|
@ -382,7 +382,9 @@ class ResourceLockApiTest(test.TestCase):
|
|||
'create',
|
||||
mock.Mock(return_value=expected_lock))
|
||||
|
||||
actual_lock = self.controller.create(self.req, body)['resource_lock']
|
||||
actual_lock = self.controller.create(
|
||||
self.req, body=body
|
||||
)['resource_lock']
|
||||
|
||||
self.controller.resource_locks_api.create.assert_called_once_with(
|
||||
utils.IsAMatcher(context.RequestContext),
|
||||
|
@ -415,7 +417,7 @@ class ResourceLockApiTest(test.TestCase):
|
|||
actual_lock = self.controller.update(
|
||||
self.req,
|
||||
'04512dae-18c2-45b5-bbab-50b775ba6f1d',
|
||||
body
|
||||
body=body
|
||||
)['resource_lock']
|
||||
|
||||
self.controller.resource_locks_api.update.assert_called_once_with(
|
||||
|
|
Loading…
Reference in New Issue