Convert db exceptions to api exceptions
All handles of db exceptions were convert to api exceptions. Now clients don't receive error messages from database. Change-Id: Iab8fb03697964c876907fa2fab9e833247bc9525
This commit is contained in:
parent
2315f0ba9c
commit
3e58e2c1e8
@ -24,6 +24,7 @@ import wsmeext.pecan as wsme_pecan
|
||||
import storyboard.api.auth.authorization_checks as checks
|
||||
from storyboard.api.v1 import validations
|
||||
from storyboard.api.v1 import wmodels
|
||||
from storyboard.common import decorators
|
||||
import storyboard.common.exception as exc
|
||||
from storyboard.db.api import project_groups
|
||||
from storyboard.db.api import projects
|
||||
@ -38,6 +39,7 @@ class ProjectsSubcontroller(rest.RestController):
|
||||
Project Group.
|
||||
"""
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.Project], int)
|
||||
def get(self, project_group_id):
|
||||
@ -55,6 +57,7 @@ class ProjectsSubcontroller(rest.RestController):
|
||||
return [wmodels.Project.from_db_model(project)
|
||||
for project in project_group.projects]
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(wmodels.Project, int, int)
|
||||
def put(self, project_group_id, project_id):
|
||||
@ -65,6 +68,7 @@ class ProjectsSubcontroller(rest.RestController):
|
||||
|
||||
return wmodels.Project.from_db_model(projects.project_get(project_id))
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(None, int, int)
|
||||
def delete(self, project_group_id, project_id):
|
||||
@ -87,6 +91,7 @@ class ProjectGroupsController(rest.RestController):
|
||||
validation_post_schema = validations.PROJECT_GROUPS_POST_SCHEMA
|
||||
validation_put_schema = validations.PROJECT_GROUPS_PUT_SCHEMA
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose(wmodels.ProjectGroup, int)
|
||||
def get_one(self, project_group_id):
|
||||
@ -103,6 +108,7 @@ class ProjectGroupsController(rest.RestController):
|
||||
|
||||
return wmodels.ProjectGroup.from_db_model(group)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.ProjectGroup], int, int, unicode, unicode,
|
||||
unicode, unicode)
|
||||
@ -135,6 +141,7 @@ class ProjectGroupsController(rest.RestController):
|
||||
|
||||
return [wmodels.ProjectGroup.from_db_model(group) for group in groups]
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(wmodels.ProjectGroup, body=wmodels.ProjectGroup)
|
||||
def post(self, project_group):
|
||||
@ -151,6 +158,7 @@ class ProjectGroupsController(rest.RestController):
|
||||
|
||||
return wmodels.ProjectGroup.from_db_model(created_group)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(wmodels.ProjectGroup, int, body=wmodels.ProjectGroup)
|
||||
def put(self, project_group_id, project_group):
|
||||
@ -170,6 +178,7 @@ class ProjectGroupsController(rest.RestController):
|
||||
|
||||
return wmodels.ProjectGroup.from_db_model(updated_group)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(None, int)
|
||||
def delete(self, project_group_id):
|
||||
|
@ -25,9 +25,11 @@ from storyboard.api.auth import authorization_checks as checks
|
||||
from storyboard.api.v1.search import search_engine
|
||||
from storyboard.api.v1 import validations
|
||||
from storyboard.api.v1 import wmodels
|
||||
from storyboard.common import decorators
|
||||
from storyboard.db.api import projects as projects_api
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
SEARCH_ENGINE = search_engine.get_engine()
|
||||
@ -44,6 +46,7 @@ class ProjectsController(rest.RestController):
|
||||
validation_post_schema = validations.PROJECTS_POST_SCHEMA
|
||||
validation_put_schema = validations.PROJECTS_PUT_SCHEMA
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose(wmodels.Project, int)
|
||||
def get_one_by_id(self, project_id):
|
||||
@ -60,6 +63,7 @@ class ProjectsController(rest.RestController):
|
||||
raise ClientSideError(_("Project %s not found") % project_id,
|
||||
status_code=404)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose(wmodels.Project, unicode)
|
||||
def get_one_by_name(self, project_name):
|
||||
@ -76,6 +80,7 @@ class ProjectsController(rest.RestController):
|
||||
raise ClientSideError(_("Project %s not found") % project_name,
|
||||
status_code=404)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.Project], int, int, unicode, unicode, int,
|
||||
unicode, unicode)
|
||||
@ -121,6 +126,7 @@ class ProjectsController(rest.RestController):
|
||||
|
||||
return [wmodels.Project.from_db_model(p) for p in projects]
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(wmodels.Project, body=wmodels.Project)
|
||||
def post(self, project):
|
||||
@ -128,9 +134,11 @@ class ProjectsController(rest.RestController):
|
||||
|
||||
:param project: a project within the request body.
|
||||
"""
|
||||
|
||||
result = projects_api.project_create(project.as_dict())
|
||||
return wmodels.Project.from_db_model(result)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(wmodels.Project, int, body=wmodels.Project)
|
||||
def put(self, project_id, project):
|
||||
@ -155,6 +163,7 @@ class ProjectsController(rest.RestController):
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.Project], unicode, unicode, int, int)
|
||||
def search(self, q="", marker=None, limit=None):
|
||||
|
@ -28,6 +28,7 @@ from storyboard.api.v1.timeline import CommentsController
|
||||
from storyboard.api.v1.timeline import TimeLineEventsController
|
||||
from storyboard.api.v1 import validations
|
||||
from storyboard.api.v1 import wmodels
|
||||
from storyboard.common import decorators
|
||||
from storyboard.db.api import stories as stories_api
|
||||
from storyboard.db.api import timeline_events as events_api
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
@ -46,6 +47,7 @@ class StoriesController(rest.RestController):
|
||||
validation_post_schema = validations.STORIES_POST_SCHEMA
|
||||
validation_put_schema = validations.STORIES_PUT_SCHEMA
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose(wmodels.Story, int)
|
||||
def get_one(self, story_id):
|
||||
@ -61,6 +63,7 @@ class StoriesController(rest.RestController):
|
||||
raise ClientSideError(_("Story %s not found") % story_id,
|
||||
status_code=404)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.Story], unicode, unicode, [unicode], int,
|
||||
int, int, int, int, unicode, unicode)
|
||||
@ -115,6 +118,7 @@ class StoriesController(rest.RestController):
|
||||
|
||||
return [wmodels.Story.from_db_model(s) for s in stories]
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(wmodels.Story, body=wmodels.Story)
|
||||
def post(self, story):
|
||||
@ -132,6 +136,7 @@ class StoriesController(rest.RestController):
|
||||
|
||||
return wmodels.Story.from_db_model(created_story)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(wmodels.Story, int, body=wmodels.Story)
|
||||
def put(self, story_id, story):
|
||||
@ -154,6 +159,7 @@ class StoriesController(rest.RestController):
|
||||
raise ClientSideError(_("Story %s not found") % story_id,
|
||||
status_code=404)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(wmodels.Story, int)
|
||||
def delete(self, story_id):
|
||||
@ -168,6 +174,7 @@ class StoriesController(rest.RestController):
|
||||
comments = CommentsController()
|
||||
events = TimeLineEventsController()
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.Story], unicode, unicode, int, int)
|
||||
def search(self, q="", marker=None, limit=None):
|
||||
|
@ -24,6 +24,7 @@ import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from storyboard.api.auth import authorization_checks as checks
|
||||
from storyboard.api.v1 import base
|
||||
from storyboard.common import decorators
|
||||
from storyboard.db.api import subscription_events as subscription_events_api
|
||||
from storyboard.db.api import users as user_api
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
@ -69,6 +70,7 @@ class SubscriptionEventsController(rest.RestController):
|
||||
subscriptionEvents.
|
||||
"""
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(SubscriptionEvent, int)
|
||||
def get_one(self, subscription_event_id):
|
||||
@ -86,6 +88,7 @@ class SubscriptionEventsController(rest.RestController):
|
||||
|
||||
return SubscriptionEvent.from_db_model(subscription_event)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose([SubscriptionEvent], int, int, unicode,
|
||||
int, unicode, unicode)
|
||||
@ -136,6 +139,7 @@ class SubscriptionEventsController(rest.RestController):
|
||||
|
||||
return [SubscriptionEvent.from_db_model(s) for s in subscriptions]
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(None, int)
|
||||
def delete(self, subscription_event_id):
|
||||
|
@ -24,6 +24,7 @@ import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from storyboard.api.auth import authorization_checks as checks
|
||||
from storyboard.api.v1 import base
|
||||
from storyboard.common import decorators
|
||||
from storyboard.db.api import subscriptions as subscription_api
|
||||
from storyboard.db.api import users as user_api
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
@ -62,6 +63,7 @@ class SubscriptionsController(rest.RestController):
|
||||
Provides Create, Delete, and search methods for resource subscriptions.
|
||||
"""
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(Subscription, int)
|
||||
def get_one(self, subscription_id):
|
||||
@ -79,6 +81,7 @@ class SubscriptionsController(rest.RestController):
|
||||
|
||||
return Subscription.from_db_model(subscription)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose([Subscription], int, int, [unicode], int, int,
|
||||
unicode, unicode)
|
||||
@ -130,6 +133,7 @@ class SubscriptionsController(rest.RestController):
|
||||
|
||||
return [Subscription.from_db_model(s) for s in subscriptions]
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(Subscription, body=Subscription)
|
||||
def post(self, subscription):
|
||||
@ -171,6 +175,7 @@ class SubscriptionsController(rest.RestController):
|
||||
result = subscription_api.subscription_create(subscription.as_dict())
|
||||
return Subscription.from_db_model(result)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(None, int)
|
||||
def delete(self, subscription_id):
|
||||
|
@ -25,6 +25,7 @@ from storyboard.api.auth import authorization_checks as checks
|
||||
from storyboard.api.v1.search import search_engine
|
||||
from storyboard.api.v1 import validations
|
||||
from storyboard.api.v1 import wmodels
|
||||
from storyboard.common import decorators
|
||||
from storyboard.db.api import tasks as tasks_api
|
||||
from storyboard.db.api import timeline_events as events_api
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
@ -42,6 +43,7 @@ class TasksController(rest.RestController):
|
||||
validation_post_schema = validations.TASKS_POST_SCHEMA
|
||||
validation_put_schema = validations.TASKS_PUT_SCHEMA
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose(wmodels.Task, int)
|
||||
def get_one(self, task_id):
|
||||
@ -57,6 +59,7 @@ class TasksController(rest.RestController):
|
||||
raise ClientSideError(_("Task %s not found") % task_id,
|
||||
status_code=404)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.Task], unicode, int, int, int, int,
|
||||
[unicode], [unicode], int, int, unicode, unicode)
|
||||
@ -116,6 +119,7 @@ class TasksController(rest.RestController):
|
||||
|
||||
return [wmodels.Task.from_db_model(s) for s in tasks]
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(wmodels.Task, body=wmodels.Task)
|
||||
def post(self, task):
|
||||
@ -136,6 +140,7 @@ class TasksController(rest.RestController):
|
||||
|
||||
return wmodels.Task.from_db_model(created_task)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(wmodels.Task, int, body=wmodels.Task)
|
||||
def put(self, task_id, task):
|
||||
@ -200,6 +205,7 @@ class TasksController(rest.RestController):
|
||||
task_title=original_task.title,
|
||||
author_id=author_id)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(wmodels.Task, int)
|
||||
def delete(self, task_id):
|
||||
@ -219,6 +225,7 @@ class TasksController(rest.RestController):
|
||||
|
||||
response.status_code = 204
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.Task], unicode, unicode, int, int)
|
||||
def search(self, q="", marker=None, limit=None):
|
||||
|
@ -25,6 +25,7 @@ import wsmeext.pecan as wsme_pecan
|
||||
from storyboard.api.auth import authorization_checks as checks
|
||||
from storyboard.api.v1 import validations
|
||||
from storyboard.api.v1 import wmodels
|
||||
from storyboard.common import decorators
|
||||
from storyboard.common import exception as exc
|
||||
from storyboard.db.api import teams as teams_api
|
||||
from storyboard.db.api import users as users_api
|
||||
@ -36,6 +37,7 @@ CONF = cfg.CONF
|
||||
class UsersSubcontroller(rest.RestController):
|
||||
"""This controller should be used to list, add or remove users from a Team.
|
||||
"""
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.User], int)
|
||||
def get(self, team_id):
|
||||
@ -51,6 +53,7 @@ class UsersSubcontroller(rest.RestController):
|
||||
|
||||
return [wmodels.User.from_db_model(user) for user in team.users]
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(wmodels.User, int, int)
|
||||
def put(self, team_id, user_id):
|
||||
@ -61,6 +64,7 @@ class UsersSubcontroller(rest.RestController):
|
||||
|
||||
return wmodels.User.from_db_model(user)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(None, int, int)
|
||||
def delete(self, team_id, user_id):
|
||||
@ -76,6 +80,7 @@ class TeamsController(rest.RestController):
|
||||
validation_post_schema = validations.TEAMS_POST_SCHEMA
|
||||
validation_put_schema = validations.TEAMS_PUT_SCHEMA
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose(wmodels.Team, int)
|
||||
def get_one_by_id(self, team_id):
|
||||
@ -92,6 +97,7 @@ class TeamsController(rest.RestController):
|
||||
raise ClientSideError(_("Team %s not found") % team_id,
|
||||
status_code=404)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose(wmodels.Team, unicode)
|
||||
def get_one_by_name(self, team_name):
|
||||
@ -108,6 +114,7 @@ class TeamsController(rest.RestController):
|
||||
raise ClientSideError(_("Team %s not found") % team_name,
|
||||
status_code=404)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.Team], int, int, unicode, unicode, unicode,
|
||||
unicode)
|
||||
@ -148,6 +155,7 @@ class TeamsController(rest.RestController):
|
||||
|
||||
return [wmodels.Team.from_db_model(t) for t in teams]
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(wmodels.Team, body=wmodels.Team)
|
||||
def post(self, team):
|
||||
@ -158,6 +166,7 @@ class TeamsController(rest.RestController):
|
||||
result = teams_api.team_create(team.as_dict())
|
||||
return wmodels.Team.from_db_model(result)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(wmodels.Team, int, body=wmodels.Team)
|
||||
def put(self, team_id, team):
|
||||
@ -203,6 +212,7 @@ class TeamsController(rest.RestController):
|
||||
# Use default routing for all other requests
|
||||
return super(TeamsController, self)._route(args, request)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(None, int)
|
||||
def delete(self, team_id):
|
||||
|
@ -24,6 +24,7 @@ import wsmeext.pecan as wsme_pecan
|
||||
from storyboard.api.auth import authorization_checks as checks
|
||||
from storyboard.api.v1.search import search_engine
|
||||
from storyboard.api.v1 import wmodels
|
||||
from storyboard.common import decorators
|
||||
from storyboard.common import event_types
|
||||
from storyboard.db.api import comments as comments_api
|
||||
from storyboard.db.api import timeline_events as events_api
|
||||
@ -37,6 +38,7 @@ SEARCH_ENGINE = search_engine.get_engine()
|
||||
class TimeLineEventsController(rest.RestController):
|
||||
"""Manages comments."""
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose(wmodels.TimeLineEvent, int, int)
|
||||
def get_one(self, story_id, event_id):
|
||||
@ -57,6 +59,7 @@ class TimeLineEventsController(rest.RestController):
|
||||
raise ClientSideError(_("Comment %s not found") % event_id,
|
||||
status_code=404)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.TimeLineEvent], int, int, int, unicode,
|
||||
unicode)
|
||||
@ -99,6 +102,7 @@ class TimeLineEventsController(rest.RestController):
|
||||
class CommentsController(rest.RestController):
|
||||
"""Manages comments."""
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose(wmodels.Comment, int, int)
|
||||
def get_one(self, story_id, comment_id):
|
||||
@ -117,6 +121,7 @@ class CommentsController(rest.RestController):
|
||||
raise ClientSideError(_("Comment %s not found") % comment_id,
|
||||
status_code=404)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.Comment], int, int, int, unicode, unicode)
|
||||
def get_all(self, story_id=None, marker=None, limit=None, sort_field='id',
|
||||
@ -166,6 +171,7 @@ class CommentsController(rest.RestController):
|
||||
|
||||
return [wmodels.Comment.from_db_model(comment) for comment in comments]
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(wmodels.TimeLineEvent, int, body=wmodels.Comment)
|
||||
def post(self, story_id, comment):
|
||||
@ -188,6 +194,7 @@ class CommentsController(rest.RestController):
|
||||
event = wmodels.TimeLineEvent.resolve_event_values(event)
|
||||
return event
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(wmodels.Comment, int, int, body=wmodels.Comment)
|
||||
def put(self, story_id, comment_id, comment_body):
|
||||
@ -210,6 +217,7 @@ class CommentsController(rest.RestController):
|
||||
|
||||
return wmodels.Comment.from_db_model(updated_comment)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(wmodels.Comment, int, int)
|
||||
def delete(self, story_id, comment_id):
|
||||
@ -230,6 +238,7 @@ class CommentsController(rest.RestController):
|
||||
response.status_code = 204
|
||||
return response
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.Comment], unicode, unicode, int, int)
|
||||
def search(self, q="", marker=None, limit=None):
|
||||
|
@ -23,6 +23,7 @@ import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from storyboard.api.auth import authorization_checks as checks
|
||||
from storyboard.api.v1 import validations
|
||||
from storyboard.common import decorators
|
||||
import storyboard.db.api.users as user_api
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
from storyboard.openstack.common import log
|
||||
@ -35,6 +36,7 @@ LOG = log.getLogger(__name__)
|
||||
class UserPreferencesController(rest.RestController):
|
||||
validation_post_schema = validations.USER_PREFERENCES_POST_SCHEMA
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(types.DictType(unicode, unicode), int)
|
||||
def get_all(self, user_id):
|
||||
@ -46,6 +48,7 @@ class UserPreferencesController(rest.RestController):
|
||||
|
||||
return user_api.user_get_preferences(user_id)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(types.DictType(unicode, unicode), int,
|
||||
body=types.DictType(unicode, unicode))
|
||||
|
@ -26,6 +26,7 @@ import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from storyboard.api.auth import authorization_checks as checks
|
||||
import storyboard.api.v1.wmodels as wmodels
|
||||
from storyboard.common import decorators
|
||||
import storyboard.db.api.access_tokens as token_api
|
||||
import storyboard.db.api.users as user_api
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
@ -37,6 +38,7 @@ LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class UserTokensController(rest.RestController):
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose([wmodels.AccessToken], int, int, int, unicode,
|
||||
unicode)
|
||||
@ -77,6 +79,7 @@ class UserTokensController(rest.RestController):
|
||||
|
||||
return [wmodels.AccessToken.from_db_model(t) for t in tokens]
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(wmodels.AccessToken, int, int)
|
||||
def get(self, user_id, access_token_id):
|
||||
@ -94,6 +97,7 @@ class UserTokensController(rest.RestController):
|
||||
|
||||
return wmodels.AccessToken.from_db_model(access_token)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(wmodels.AccessToken, int, body=wmodels.AccessToken)
|
||||
def post(self, user_id, body):
|
||||
@ -118,6 +122,7 @@ class UserTokensController(rest.RestController):
|
||||
|
||||
return wmodels.AccessToken.from_db_model(token)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(wmodels.AccessToken, int, int,
|
||||
body=wmodels.AccessToken)
|
||||
@ -145,6 +150,7 @@ class UserTokensController(rest.RestController):
|
||||
|
||||
return wmodels.AccessToken.from_db_model(result_token)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(wmodels.AccessToken, int, int)
|
||||
def delete(self, user_id, access_token_id):
|
||||
|
@ -28,6 +28,7 @@ from storyboard.api.v1.user_preferences import UserPreferencesController
|
||||
from storyboard.api.v1.user_tokens import UserTokensController
|
||||
from storyboard.api.v1 import validations
|
||||
from storyboard.api.v1 import wmodels
|
||||
from storyboard.common import decorators
|
||||
from storyboard.db.api import users as users_api
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
|
||||
@ -51,6 +52,7 @@ class UsersController(rest.RestController):
|
||||
validation_post_schema = validations.USERS_POST_SCHEMA
|
||||
validation_put_schema = validations.USERS_PUT_SCHEMA
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.User], int, int, unicode, unicode, unicode,
|
||||
unicode)
|
||||
@ -90,6 +92,7 @@ class UsersController(rest.RestController):
|
||||
|
||||
return [wmodels.User.from_db_model(u) for u in users]
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose(wmodels.User, int)
|
||||
def get_one(self, user_id):
|
||||
@ -108,6 +111,7 @@ class UsersController(rest.RestController):
|
||||
status_code=404)
|
||||
return user
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(wmodels.User, body=wmodels.User)
|
||||
def post(self, user):
|
||||
@ -119,6 +123,7 @@ class UsersController(rest.RestController):
|
||||
created_user = users_api.user_create(user.as_dict())
|
||||
return wmodels.User.from_db_model(created_user)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@wsme_pecan.wsexpose(wmodels.User, int, body=wmodels.User)
|
||||
def put(self, user_id, user):
|
||||
@ -155,6 +160,7 @@ class UsersController(rest.RestController):
|
||||
updated_user = users_api.user_update(user_id, user_dict)
|
||||
return wmodels.User.from_db_model(updated_user)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@wsme_pecan.wsexpose([wmodels.User], unicode, int, int)
|
||||
def search(self, q="", marker=None, limit=None):
|
||||
|
30
storyboard/common/decorators.py
Normal file
30
storyboard/common/decorators.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright (c) 2015 Mirantis 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 functools
|
||||
|
||||
from pecan import abort
|
||||
|
||||
from storyboard.common import exception as exc
|
||||
|
||||
|
||||
def db_exceptions(func):
|
||||
@functools.wraps(func)
|
||||
def decorate(self, *args, **kwargs):
|
||||
try:
|
||||
return func(self, *args, **kwargs)
|
||||
except exc.DBException as db_exc:
|
||||
abort(db_exc.code, db_exc.message)
|
||||
return decorate
|
@ -4,7 +4,7 @@
|
||||
# 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
|
||||
# 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,
|
||||
@ -13,10 +13,12 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from wsme.exc import ClientSideError
|
||||
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
|
||||
|
||||
class StoryboardException(Exception):
|
||||
class StoryboardException(ClientSideError):
|
||||
"""Base Exception for the project
|
||||
|
||||
To correctly use this class, inherit from it and define
|
||||
@ -28,8 +30,9 @@ class StoryboardException(Exception):
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
def __init__(self):
|
||||
super(StoryboardException, self).__init__(self.message)
|
||||
def __init__(self, message=None, status_code=None):
|
||||
super(StoryboardException, self).__init__(msg=message,
|
||||
status_code=status_code)
|
||||
|
||||
|
||||
class NotFound(StoryboardException):
|
||||
@ -40,17 +43,194 @@ class NotFound(StoryboardException):
|
||||
self.message = message
|
||||
|
||||
|
||||
class DuplicateEntry(StoryboardException):
|
||||
message = _("Database object already exists")
|
||||
|
||||
def __init__(self, message=None):
|
||||
if message:
|
||||
self.message = message
|
||||
|
||||
|
||||
class NotEmpty(StoryboardException):
|
||||
message = _("Database object must be empty")
|
||||
|
||||
def __init__(self, message=None):
|
||||
if message:
|
||||
self.message = message
|
||||
|
||||
|
||||
class DBException(StoryboardException):
|
||||
# Base exception for database errors
|
||||
|
||||
message = _("Database Exception")
|
||||
|
||||
def __init__(self, message=None, status_code=None):
|
||||
"""Constructor for base exception class
|
||||
|
||||
:param: message: exception message.
|
||||
:param: status_code: code of exception.
|
||||
"""
|
||||
if not status_code:
|
||||
status_code = 400
|
||||
|
||||
super(DBException, self).__init__(message=message,
|
||||
status_code=status_code)
|
||||
|
||||
|
||||
class DBDuplicateEntry(DBException):
|
||||
"""Duplicate entry exception
|
||||
|
||||
This exception wraps the same exception from database.
|
||||
"""
|
||||
|
||||
message = _("Database object already exists.")
|
||||
|
||||
def __init__(self, message=None, object_name=None, value=None,
|
||||
status_code=None):
|
||||
"""Constructor for duplicate entry exception
|
||||
|
||||
:param : message: This message will be shown after exception raised.
|
||||
:param : object_name: This parameter is name of object, in which
|
||||
exception was raised.
|
||||
:param: value: Invalid value.
|
||||
:param: status_code: code of exception.
|
||||
|
||||
If object_name or value is not 'None', to message will be appended with
|
||||
new message with information about object name or invalid value
|
||||
"""
|
||||
|
||||
super(DBDuplicateEntry, self).__init__(message=message,
|
||||
status_code=status_code)
|
||||
db_message = None
|
||||
|
||||
if object_name or value:
|
||||
db_message_list = [_("Database object")]
|
||||
|
||||
if object_name:
|
||||
db_message_list.append(_("\'%s\'") % object_name)
|
||||
|
||||
if value:
|
||||
db_message_list.append(_("with field value \'%s\'") % value)
|
||||
else:
|
||||
db_message_list.append(_("with some of unique fields"))
|
||||
|
||||
db_message_list.append(_("already exists."))
|
||||
db_message = _(" ").join(db_message_list)
|
||||
|
||||
if db_message:
|
||||
message_list = []
|
||||
|
||||
if message:
|
||||
message_list.append(message)
|
||||
|
||||
message_list.append(db_message)
|
||||
self.msg = " ".join(message_list)
|
||||
|
||||
|
||||
class DBConnectionError(DBException):
|
||||
"""Connection error exception
|
||||
|
||||
This exception wraps the same exception from database.
|
||||
"""
|
||||
|
||||
message = _("Connection to database failed.")
|
||||
|
||||
|
||||
class ColumnError(DBException):
|
||||
"""Column error exception
|
||||
|
||||
This exception wraps the same exception from database.
|
||||
"""
|
||||
|
||||
message = _("Column is invalid or not found")
|
||||
|
||||
|
||||
class DBDeadLock(DBException):
|
||||
"""Deadlock exception
|
||||
|
||||
This exception wraps the same exception from database.
|
||||
"""
|
||||
|
||||
message = _("Database in dead lock")
|
||||
|
||||
|
||||
class DBInvalidUnicodeParameter(DBException):
|
||||
"""Invalid unicode parameter exception
|
||||
|
||||
This exception wraps the same exception from database.
|
||||
"""
|
||||
|
||||
message = _("Unicode parameter is passed to "
|
||||
"a database without encoding directive")
|
||||
|
||||
|
||||
class DBMigrationError(DBException):
|
||||
"""Migration error exception
|
||||
|
||||
This exception wraps the same exception from database.
|
||||
"""
|
||||
|
||||
message = _("migrations could not be completed successfully")
|
||||
|
||||
|
||||
class DBReferenceError(DBException):
|
||||
"""Reference error exception
|
||||
|
||||
This exception wraps the same exception from database.
|
||||
"""
|
||||
|
||||
message = _("Foreign key error.")
|
||||
|
||||
def __init__(self, message=None, object_name=None, value=None,
|
||||
key=None, status_code=None):
|
||||
"""Constructor for duplicate entry exception
|
||||
|
||||
:param : message: This message will be shown after exception raised.
|
||||
:param : object_name: This parameter is name of object, in which
|
||||
exception was raised.
|
||||
:param: value: Invalid value.
|
||||
:param : key: Field with invalid value.
|
||||
:param : status_code: code of exception.
|
||||
|
||||
If object_name or value or key is not 'None', to message will be
|
||||
appended with new message with information about object name or
|
||||
invalid value or field with invalid value.
|
||||
"""
|
||||
|
||||
super(DBReferenceError, self).__init__(message=message,
|
||||
status_code=status_code)
|
||||
db_message = None
|
||||
|
||||
if object_name or value or key:
|
||||
db_message_list = []
|
||||
|
||||
if object_name:
|
||||
db_message_list.append(
|
||||
_("Error in object"))
|
||||
db_message_list.append(_("\'%s\'.") % object_name)
|
||||
|
||||
if value or key:
|
||||
db_message_list.append(_("Field"))
|
||||
|
||||
if key:
|
||||
db_message_list.append(_("\'%s\'") % key)
|
||||
|
||||
if value:
|
||||
db_message_list.append(_("value"))
|
||||
db_message_list.append(_("\'%s\'") % value)
|
||||
|
||||
db_message_list.append(_("is invalid."))
|
||||
|
||||
db_message = " ".join(db_message_list)
|
||||
|
||||
if db_message:
|
||||
message_list = []
|
||||
|
||||
if message:
|
||||
message_list.append(message)
|
||||
else:
|
||||
message_list.append(self.message)
|
||||
|
||||
message_list.append(db_message)
|
||||
self.msg = " ".join(message_list)
|
||||
|
||||
|
||||
class DBInvalidSortKey(DBException):
|
||||
"""Invalid sortkey error exception
|
||||
|
||||
This exception wraps the same exception from database.
|
||||
"""
|
||||
|
||||
message = _("Invalid sort field")
|
||||
|
@ -18,6 +18,7 @@ import datetime
|
||||
from oslo.db.sqlalchemy.utils import InvalidSortKey
|
||||
from wsme.exc import ClientSideError
|
||||
|
||||
from storyboard.common import exception as exc
|
||||
from storyboard.db.api import base as api_base
|
||||
from storyboard.db import models
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
@ -56,8 +57,8 @@ def access_token_get_all(marker=None, limit=None, sort_field=None,
|
||||
marker=marker,
|
||||
sort_dir=sort_dir)
|
||||
except InvalidSortKey:
|
||||
raise ClientSideError(_("Invalid sort_field [%s]") % (sort_field,),
|
||||
status_code=400)
|
||||
raise exc.DBInvalidSortKey(
|
||||
_("Invalid sort_field [%s]") % (sort_field,))
|
||||
except ValueError as ve:
|
||||
raise ClientSideError(_("%s") % (ve,), status_code=400)
|
||||
|
||||
|
@ -40,8 +40,15 @@ def _get_facade_instance():
|
||||
"""Generate an instance of the DB Facade.
|
||||
"""
|
||||
global _FACADE
|
||||
if _FACADE is None:
|
||||
_FACADE = db_session.EngineFacade.from_config(CONF)
|
||||
|
||||
try:
|
||||
if _FACADE is None:
|
||||
_FACADE = db_session.EngineFacade.from_config(CONF)
|
||||
except db_exc.DBConnectionError:
|
||||
raise exc.DBConnectionError()
|
||||
except db_exc.DBDeadlock:
|
||||
raise exc.DBDeadLock()
|
||||
|
||||
return _FACADE
|
||||
|
||||
|
||||
@ -77,21 +84,37 @@ def get_engine():
|
||||
"""Returns the global instance of our database engine.
|
||||
"""
|
||||
facade = _get_facade_instance()
|
||||
return facade.get_engine(use_slave=True)
|
||||
|
||||
try:
|
||||
return facade.get_engine(use_slave=True)
|
||||
except db_exc.DBConnectionError:
|
||||
raise exc.DBConnectionError()
|
||||
except db_exc.DBDeadlock:
|
||||
raise exc.DBDeadLock()
|
||||
|
||||
|
||||
def get_session(autocommit=True, expire_on_commit=False, **kwargs):
|
||||
"""Returns a database session from our facade.
|
||||
"""
|
||||
facade = _get_facade_instance()
|
||||
return facade.get_session(autocommit=autocommit,
|
||||
expire_on_commit=expire_on_commit, **kwargs)
|
||||
try:
|
||||
return facade.get_session(autocommit=autocommit,
|
||||
expire_on_commit=expire_on_commit, **kwargs)
|
||||
except db_exc.DBConnectionError:
|
||||
raise exc.DBConnectionError()
|
||||
except db_exc.DBDeadlock:
|
||||
raise exc.DBDeadLock()
|
||||
|
||||
|
||||
def cleanup():
|
||||
"""Manually clean up our database engine.
|
||||
"""
|
||||
_destroy_facade_instance()
|
||||
try:
|
||||
_destroy_facade_instance()
|
||||
except db_exc.DBConnectionError:
|
||||
raise exc.DBConnectionError()
|
||||
except db_exc.DBDeadlock:
|
||||
raise exc.DBDeadLock()
|
||||
|
||||
|
||||
def model_query(model, session=None):
|
||||
@ -100,13 +123,32 @@ def model_query(model, session=None):
|
||||
:param model: base model to query
|
||||
"""
|
||||
session = session or get_session()
|
||||
query = session.query(model)
|
||||
|
||||
try:
|
||||
query = session.query(model)
|
||||
except db_exc.DBConnectionError:
|
||||
raise exc.DBConnectionError()
|
||||
except db_exc.ColumnError:
|
||||
raise exc.ColumnError()
|
||||
except db_exc.DBDeadlock:
|
||||
raise exc.DBDeadLock()
|
||||
except db_exc.DBInvalidUnicodeParameter:
|
||||
raise exc.DBInvalidUnicodeParameter()
|
||||
return query
|
||||
|
||||
|
||||
def __entity_get(kls, entity_id, session):
|
||||
query = model_query(kls, session)
|
||||
return query.filter_by(id=entity_id).first()
|
||||
try:
|
||||
query = model_query(kls, session)
|
||||
return query.filter_by(id=entity_id).first()
|
||||
except db_exc.DBConnectionError:
|
||||
raise exc.DBConnectionError()
|
||||
except db_exc.ColumnError:
|
||||
raise exc.ColumnError()
|
||||
except db_exc.DBDeadlock:
|
||||
raise exc.DBDeadLock()
|
||||
except db_exc.DBInvalidUnicodeParameter:
|
||||
raise exc.DBInvalidUnicodeParameter()
|
||||
|
||||
|
||||
def entity_get(kls, entity_id, filter_non_public=False, session=None):
|
||||
@ -123,7 +165,6 @@ def entity_get(kls, entity_id, filter_non_public=False, session=None):
|
||||
|
||||
def entity_get_all(kls, filter_non_public=False, marker=None, limit=None,
|
||||
sort_field='id', sort_dir='asc', **kwargs):
|
||||
|
||||
# Sanity checks, in case someone accidentally explicitly passes in 'None'
|
||||
if not sort_field:
|
||||
sort_field = 'id'
|
||||
@ -144,14 +185,21 @@ def entity_get_all(kls, filter_non_public=False, marker=None, limit=None,
|
||||
sort_keys=[sort_field],
|
||||
marker=marker,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
# Execute the query
|
||||
entities = query.all()
|
||||
except InvalidSortKey:
|
||||
raise ClientSideError(_("Invalid sort_field [%s]") % (sort_field,),
|
||||
status_code=400)
|
||||
raise exc.DBInvalidSortKey(_("Invalid sort_field [%s]") %
|
||||
(sort_field,))
|
||||
except db_exc.DBConnectionError:
|
||||
raise exc.DBConnectionError()
|
||||
except db_exc.DBDeadlock:
|
||||
raise exc.DBDeadLock()
|
||||
except db_exc.DBInvalidUnicodeParameter:
|
||||
raise exc.DBInvalidUnicodeParameter()
|
||||
except ValueError as ve:
|
||||
raise ClientSideError(_("%s") % (ve,), status_code=400)
|
||||
|
||||
# Execute the query
|
||||
entities = query.all()
|
||||
if len(entities) > 0 and filter_non_public:
|
||||
sample_entity = entities[0] if len(entities) > 0 else None
|
||||
public_fields = getattr(sample_entity, "_public_fields", [])
|
||||
@ -169,13 +217,21 @@ def entity_get_count(kls, **kwargs):
|
||||
# Sanity check on input parameters
|
||||
query = apply_query_filters(query=query, model=kls, **kwargs)
|
||||
|
||||
count = query.count()
|
||||
try:
|
||||
count = query.count()
|
||||
except db_exc.DBConnectionError:
|
||||
raise exc.DBConnectionError()
|
||||
except db_exc.DBDeadlock:
|
||||
raise exc.DBDeadLock()
|
||||
except db_exc.DBInvalidUnicodeParameter:
|
||||
raise exc.DBInvalidUnicodeParameter()
|
||||
|
||||
return count
|
||||
|
||||
|
||||
def _filter_non_public_fields(entity, public_list=list()):
|
||||
ent_copy = copy.copy(entity)
|
||||
|
||||
for attr_name, val in six.iteritems(entity.__dict__):
|
||||
if attr_name.startswith("_"):
|
||||
continue
|
||||
@ -191,12 +247,25 @@ def entity_create(kls, values):
|
||||
entity.update(values.copy())
|
||||
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
try:
|
||||
|
||||
try:
|
||||
with session.begin():
|
||||
session.add(entity)
|
||||
except db_exc.DBDuplicateEntry:
|
||||
raise exc.DuplicateEntry(_("Duplicate entry for : %s")
|
||||
% kls.__name__)
|
||||
|
||||
except db_exc.DBDuplicateEntry as de:
|
||||
raise exc.DBDuplicateEntry(object_name=kls.__name__,
|
||||
value=de.value)
|
||||
except db_exc.DBReferenceError as re:
|
||||
raise exc.DBReferenceError(object_name=kls.__name__,
|
||||
value=re.constraint, key=re.key)
|
||||
except db_exc.DBConnectionError:
|
||||
raise exc.DBConnectionError()
|
||||
except db_exc.ColumnError:
|
||||
raise exc.ColumnError()
|
||||
except db_exc.DBDeadlock:
|
||||
raise exc.DBDeadLock()
|
||||
except db_exc.DBInvalidUnicodeParameter:
|
||||
raise exc.DBInvalidUnicodeParameter
|
||||
|
||||
return entity
|
||||
|
||||
@ -204,16 +273,32 @@ def entity_create(kls, values):
|
||||
def entity_update(kls, entity_id, values):
|
||||
session = get_session()
|
||||
|
||||
with session.begin():
|
||||
entity = __entity_get(kls, entity_id, session)
|
||||
if entity is None:
|
||||
raise exc.NotFound(_("%(name)s %(id)s not found") %
|
||||
{'name': kls.__name__, 'id': entity_id})
|
||||
try:
|
||||
with session.begin():
|
||||
entity = __entity_get(kls, entity_id, session)
|
||||
if entity is None:
|
||||
raise exc.NotFound(_("%(name)s %(id)s not found") %
|
||||
{'name': kls.__name__, 'id': entity_id})
|
||||
|
||||
values_copy = values.copy()
|
||||
values_copy["id"] = entity_id
|
||||
entity.update(values_copy)
|
||||
session.add(entity)
|
||||
values_copy = values.copy()
|
||||
values_copy["id"] = entity_id
|
||||
entity.update(values_copy)
|
||||
session.add(entity)
|
||||
|
||||
except db_exc.DBDuplicateEntry as de:
|
||||
raise exc.DBDuplicateEntry(object_name=kls.__name__,
|
||||
value=de.value)
|
||||
except db_exc.DBReferenceError as re:
|
||||
raise exc.DBReferenceError(object_name=kls.__name__,
|
||||
value=re.constraint, key=re.key)
|
||||
except db_exc.DBConnectionError:
|
||||
raise exc.DBConnectionError()
|
||||
except db_exc.ColumnError:
|
||||
raise exc.ColumnError()
|
||||
except db_exc.DBDeadlock:
|
||||
raise exc.DBDeadLock()
|
||||
except db_exc.DBInvalidUnicodeParameter:
|
||||
raise exc.DBInvalidUnicodeParameter
|
||||
|
||||
session = get_session()
|
||||
entity = __entity_get(kls, entity_id, session)
|
||||
@ -223,11 +308,25 @@ def entity_update(kls, entity_id, values):
|
||||
|
||||
def entity_hard_delete(kls, entity_id):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
query = model_query(kls, session)
|
||||
entity = query.filter_by(id=entity_id).first()
|
||||
if entity is None:
|
||||
raise exc.NotFound(_("%(name)s %(id)s not found") %
|
||||
{'name': kls.__name__, 'id': entity_id})
|
||||
|
||||
session.delete(entity)
|
||||
try:
|
||||
with session.begin():
|
||||
query = model_query(kls, session)
|
||||
entity = query.filter_by(id=entity_id).first()
|
||||
if entity is None:
|
||||
raise exc.NotFound(_("%(name)s %(id)s not found") %
|
||||
{'name': kls.__name__, 'id': entity_id})
|
||||
|
||||
session.delete(entity)
|
||||
|
||||
except db_exc.DBReferenceError as re:
|
||||
raise exc.DBReferenceError(object_name=kls.__name__,
|
||||
value=re.constraint, key=re.key)
|
||||
except db_exc.DBConnectionError:
|
||||
raise exc.DBConnectionError()
|
||||
except db_exc.ColumnError:
|
||||
raise exc.ColumnError()
|
||||
except db_exc.DBDeadlock:
|
||||
raise exc.DBDeadLock()
|
||||
except db_exc.DBInvalidUnicodeParameter:
|
||||
raise exc.DBInvalidUnicodeParameter()
|
||||
|
@ -4,7 +4,7 @@
|
||||
# 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
|
||||
# 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,
|
||||
@ -16,6 +16,7 @@
|
||||
from oslo.db.sqlalchemy.utils import InvalidSortKey
|
||||
from wsme.exc import ClientSideError
|
||||
|
||||
from storyboard.common import exception as exc
|
||||
from storyboard.db.api import base as api_base
|
||||
from storyboard.db import models
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
@ -50,8 +51,8 @@ def project_get_all(marker=None, limit=None, sort_field=None, sort_dir=None,
|
||||
marker=marker,
|
||||
sort_dir=sort_dir)
|
||||
except InvalidSortKey:
|
||||
raise ClientSideError(_("Invalid sort_field [%s]") % (sort_field,),
|
||||
status_code=400)
|
||||
raise exc.DBInvalidSortKey(
|
||||
_("Invalid sort_field [%s]") % (sort_field,))
|
||||
except ValueError as ve:
|
||||
raise ClientSideError(_("%s") % (ve,), status_code=400)
|
||||
|
||||
|
@ -91,8 +91,8 @@ def story_get_all(title=None, description=None, status=None, assignee_id=None,
|
||||
marker=marker,
|
||||
sort_dir=sort_dir)
|
||||
except InvalidSortKey:
|
||||
raise ClientSideError(_("Invalid sort_field [%s]") % (sort_field,),
|
||||
status_code=400)
|
||||
raise exc.DBInvalidSortKey(
|
||||
_("Invalid sort_field [%s]") % (sort_field,))
|
||||
except ValueError as ve:
|
||||
raise ClientSideError(_("%s") % (ve,), status_code=400)
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
# 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
|
||||
# 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,
|
||||
@ -16,6 +16,7 @@
|
||||
from oslo.db.sqlalchemy.utils import InvalidSortKey
|
||||
from wsme.exc import ClientSideError
|
||||
|
||||
from storyboard.common import exception as exc
|
||||
from storyboard.db.api import base as api_base
|
||||
from storyboard.db import models
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
@ -44,8 +45,8 @@ def task_get_all(marker=None, limit=None, sort_field=None, sort_dir=None,
|
||||
marker=marker,
|
||||
sort_dir=sort_dir)
|
||||
except InvalidSortKey:
|
||||
raise ClientSideError(_("Invalid sort_field [%s]") % (sort_field,),
|
||||
status_code=400)
|
||||
raise exc.DBInvalidSortKey(
|
||||
_("Invalid sort_field [%s]") % (sort_field,))
|
||||
except ValueError as ve:
|
||||
raise ClientSideError("%s" % (ve,), status_code=400)
|
||||
|
||||
|
@ -73,8 +73,8 @@ def user_update_preferences(user_id, preferences):
|
||||
for key in preferences:
|
||||
value = preferences[key]
|
||||
prefs = api_base.entity_get_all(models.UserPreference,
|
||||
user_id=user_id,
|
||||
key=key)
|
||||
user_id=user_id,
|
||||
key=key)
|
||||
|
||||
if prefs:
|
||||
pref = prefs[0]
|
||||
|
65
storyboard/tests/api/test_db_exceptions.py
Normal file
65
storyboard/tests/api/test_db_exceptions.py
Normal file
@ -0,0 +1,65 @@
|
||||
# Copyright (c) 2015 Mirantis 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
|
||||
|
||||
from storyboard.tests import base
|
||||
|
||||
|
||||
class TestDBExceptions(base.FunctionalTest):
|
||||
def setUp(self):
|
||||
super(TestDBExceptions, self).setUp()
|
||||
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
|
||||
|
||||
# test duplicate entry error
|
||||
# in this test we try to create two equal projects
|
||||
def test_duplicate_project_create(self):
|
||||
resource = '/projects'
|
||||
project = {
|
||||
'name': 'test-project-duplicate',
|
||||
'description': 'test_project_duplicate_description',
|
||||
}
|
||||
|
||||
# create project with name 'test-project-duplicate'
|
||||
response = self.post_json(resource, project)
|
||||
body = json.loads(response.body)
|
||||
self.assertEqual(project['name'], body['name'])
|
||||
self.assertEqual(project['description'], body['description'])
|
||||
|
||||
# repeat creating this project
|
||||
# because project with name 'test-project-duplicate' already exists, we
|
||||
# wait abort with code_status 400
|
||||
response = self.post_json(resource, project, expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
# test duplicate entry error
|
||||
# in this test we try to create two equal users
|
||||
def test_duplicate_user_create(self):
|
||||
# send user first time
|
||||
resource = '/users'
|
||||
user = {
|
||||
'username': 'test_duplicate',
|
||||
'full_name': 'Test duplicate',
|
||||
'email': 'dupe@example.com'
|
||||
}
|
||||
|
||||
response = self.post_json(resource, user)
|
||||
users_body = json.loads(response.body)
|
||||
self.assertEqual(user['username'], users_body['username'])
|
||||
self.assertEqual(user['full_name'], users_body['full_name'])
|
||||
self.assertEqual(user['email'], users_body['email'])
|
||||
|
||||
# send user again
|
||||
response = self.post_json(resource, user, expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
77
storyboard/tests/db/test_db_exceptions.py
Normal file
77
storyboard/tests/db/test_db_exceptions.py
Normal file
@ -0,0 +1,77 @@
|
||||
# Copyright (c) 2015 Mirantis 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 six
|
||||
|
||||
from storyboard.common import exception as exc
|
||||
from storyboard.db.api import projects
|
||||
from storyboard.db.api import tasks
|
||||
from storyboard.tests import base
|
||||
|
||||
|
||||
class TestDBDuplicateEntry(base.DbTestCase):
|
||||
def setUp(self):
|
||||
super(TestDBDuplicateEntry, self).setUp()
|
||||
|
||||
# create two projects with equal names
|
||||
def test_users(self):
|
||||
project = {
|
||||
'id': 10,
|
||||
'name': 'project',
|
||||
'description': 'Project 4 Description - foo'
|
||||
}
|
||||
|
||||
projects.project_create(project)
|
||||
self.assertRaises(exc.DBDuplicateEntry,
|
||||
lambda: projects.project_create(project))
|
||||
|
||||
|
||||
class TestDBReferenceError(base.DbTestCase):
|
||||
def setUp(self):
|
||||
super(TestDBReferenceError, self).setUp()
|
||||
|
||||
# create task with id of not existing story
|
||||
def test_teams(self):
|
||||
task = {
|
||||
'id': 10,
|
||||
'story_id': 100
|
||||
}
|
||||
|
||||
self.assertRaises(exc.DBReferenceError,
|
||||
lambda: tasks.task_create(task))
|
||||
|
||||
|
||||
class TestDbInvalidSortKey(base.DbTestCase):
|
||||
def setUp(self):
|
||||
super(TestDbInvalidSortKey, self).setUp()
|
||||
|
||||
# create project and sort his field with incorrect key
|
||||
def test_projects(self):
|
||||
project = {
|
||||
'id': 10,
|
||||
'name': 'testProject',
|
||||
'description': 'testProjectDescription'
|
||||
}
|
||||
|
||||
saved_project = projects.project_create(project)
|
||||
self.assertIsNotNone(saved_project)
|
||||
|
||||
for k, v in six.iteritems(project):
|
||||
self.assertEqual(saved_project[k], v)
|
||||
|
||||
self.assertRaises(exc.DBInvalidSortKey,
|
||||
lambda: projects.project_get_all(
|
||||
marker=10,
|
||||
sort_field='invalid_sort_field'))
|
Loading…
Reference in New Issue
Block a user