Merge "Project Groups API"

This commit is contained in:
Jenkins
2014-06-03 03:02:41 +00:00
committed by Gerrit Code Review
4 changed files with 236 additions and 45 deletions

View File

@@ -11,6 +11,14 @@ Projects
.. rest-controller:: storyboard.api.v1.projects:ProjectsController
:webprefix: /v1/projects
Project Groups
==============
.. rest-controller:: storyboard.api.v1.project_groups:ProjectGroupsController
:webprefix: /v1/project_groups
.. rest-controller:: storyboard.api.v1.project_groups:ProjectsSubcontroller
:webprefix: /v1/project_groups/<project_group_id>/projects
Stories
=======
.. rest-controller:: storyboard.api.v1.stories:StoriesController

View File

@@ -13,64 +13,160 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from oslo.config import cfg
from pecan import rest
from pecan.secure import secure
from wsme.exc import ClientSideError
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan
import storyboard.api.auth.authorization_checks as checks
import storyboard.api.v1.wsme_models as wsme_models
from storyboard.api.v1 import base
from storyboard.api.v1.projects import Project
from storyboard.db.api import project_groups
CONF = cfg.CONF
class ProjectGroup(base.APIBase):
"""Represents a group of projects."""
name = wtypes.text
"""A unique name, used in URLs, identifying the project group. All
lowercase, no special characters. Examples: infra, compute.
"""
title = wtypes.text
"""The full name of the project group, which can contain spaces, special
characters, etc.
"""
@classmethod
def sample(cls):
return cls(
name="Infra",
title="Awesome projects")
class ProjectsSubcontroller(rest.RestController):
"""This controller should be used to list, add or remove projects from a
Project Group.
"""
@secure(checks.guest)
@wsme_pecan.wsexpose([Project], int)
def get(self, project_group_id):
"""Get projects inside a project group.
:param project_group_id: An ID of the project group
"""
project_group = project_groups.project_group_get(project_group_id)
if not project_group:
raise ClientSideError("The requested project group does not exist")
return [Project.from_db_model(project)
for project in project_group.projects]
@secure(checks.superuser)
@wsme_pecan.wsexpose([Project], int, int)
def put(self, project_group_id, project_id):
"""Add a project to a project_group
"""
project_groups.project_group_add_project(project_group_id, project_id)
project_group = project_groups.project_group_get(project_group_id)
return [Project.from_db_model(project)
for project in project_group.projects]
@secure(checks.superuser)
@wsme_pecan.wsexpose([Project], int, int)
def delete(self, project_group_id, project_id):
"""Delete a project from a project_group
"""
project_groups.project_group_delete_project(project_group_id,
project_id)
project_group = project_groups.project_group_get(project_group_id)
return [Project.from_db_model(project)
for project in project_group.projects]
class ProjectGroupsController(rest.RestController):
"""REST controller for Project Groups.
At this moment it provides read-only operations.
NOTE: PUT requests should be used to update only top-level fields.
The nested fields (projects) should be updated using requests to a
/projects subcontroller
"""
@secure(checks.guest)
@wsme_pecan.wsexpose(wsme_models.ProjectGroup, int)
def get_one(self, id):
@wsme_pecan.wsexpose(ProjectGroup, int)
def get_one(self, project_group_id):
"""Retrieve information about the given project group.
:param name: project group name.
:param project_group_id: project group id.
"""
group = wsme_models.ProjectGroup.get(id=id)
group = project_groups.project_group_get(project_group_id)
if not group:
raise ClientSideError("Project Group %s not found" % id,
raise ClientSideError("Project Group %s not found" %
project_group_id,
status_code=404)
return group
return ProjectGroup.from_db_model(group)
@secure(checks.guest)
@wsme_pecan.wsexpose([wsme_models.ProjectGroup])
def get(self):
@wsme_pecan.wsexpose([ProjectGroup], int, int)
def get(self, marker=None, limit=None):
"""Retrieve a list of projects groups."""
groups = wsme_models.ProjectGroup.get_all()
return groups
if limit is None:
limit = CONF.page_size_default
limit = min(CONF.page_size_maximum, max(1, limit))
groups = project_groups.project_group_get_all(marker=marker,
limit=limit)
return [ProjectGroup.from_db_model(group) for group in groups]
@secure(checks.superuser)
@wsme_pecan.wsexpose(wsme_models.ProjectGroup,
body=wsme_models.ProjectGroup)
def post(self, group):
@wsme_pecan.wsexpose(ProjectGroup, body=ProjectGroup)
def post(self, project_group):
"""Create a new project group.
:param group: a project group within the request body.
:param project_group: a project group within the request body.
"""
created_group = wsme_models.ProjectGroup.create(wsme_entry=group)
created_group = project_groups.project_group_create(
project_group.as_dict())
if not created_group:
raise ClientSideError("Could not create ProjectGroup")
return created_group
return ProjectGroup.from_db_model(created_group)
@secure(checks.superuser)
@wsme_pecan.wsexpose(wsme_models.ProjectGroup, int,
body=wsme_models.ProjectGroup)
def put(self, id, group):
@wsme_pecan.wsexpose(ProjectGroup, int, body=ProjectGroup)
def put(self, id, project_group):
"""Modify this project group.
:param id: An ID of the project group.
:param group: a project group within the request body.
:param project_group: a project group within the request body.
"""
updated_group = wsme_models.ProjectGroup.update("id", id, group)
updated_group = project_groups.project_group_update(
id,
project_group.as_dict())
if not updated_group:
raise ClientSideError("Could not update group %s" % id)
return updated_group
return ProjectGroup.from_db_model(updated_group)
projects = ProjectsSubcontroller()

View File

@@ -189,26 +189,6 @@ def update_db_model(cls, db_entry, wsme_entry):
return db_entry
class ProjectGroup(_Base):
"""Represents a group of projects."""
name = wtypes.text
"""A unique name, used in URLs, identifying the project group. All
lowercase, no special characters. Examples: infra, compute.
"""
title = wtypes.text
"""The full name of the project group, which can contain spaces, special
characters, etc.
"""
@classmethod
def sample(cls):
return cls(
name="Infra",
title="Awesome project")
class Permission(_Base):
"""Permissions can be associated with users and teams."""
pass
@@ -254,7 +234,6 @@ class Team(_Base):
SQLALCHEMY_TO_WSME = {
sqlalchemy_models.ProjectGroup: ProjectGroup,
sqlalchemy_models.Permission: Permission,
sqlalchemy_models.Comment: Comment,
sqlalchemy_models.StoryTag: StoryTag

View File

@@ -0,0 +1,108 @@
# Copyright (c) 2014 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.
from sqlalchemy.orm import subqueryload
from wsme.exc import ClientSideError
from storyboard.common import exception as exc
from storyboard.db.api import base as api_base
from storyboard.db.api import projects
from storyboard.db import models
def _entity_get(id, session=None):
if not session:
session = api_base.get_session()
query = session.query(models.ProjectGroup)\
.options(subqueryload(models.ProjectGroup.projects))\
.filter_by(id=id)
return query.first()
def project_group_get(project_group_id):
return _entity_get(project_group_id)
def project_group_get_all(marker=None, limit=None, **kwargs):
return api_base.entity_get_all(models.ProjectGroup,
marker=marker,
limit=limit,
**kwargs)
def project_group_get_count(**kwargs):
return api_base.entity_get_count(models.ProjectGroup, **kwargs)
def project_group_create(values):
return api_base.entity_create(models.ProjectGroup, values)
def project_group_update(project_group_id, values):
return api_base.entity_update(models.ProjectGroup, project_group_id,
values)
def project_group_add_project(project_group_id, project_id):
session = api_base.get_session()
with session.begin():
project_group = _entity_get(project_group_id, session)
if project_group is None:
raise exc.NotFound("%s %s not found"
% ("Project Group", project_group_id))
project = projects.project_get(project_id)
if project is None:
raise exc.NotFound("%s %s not found"
% ("Project", project_id))
if project_id in [p.id for p in project_group.projects]:
raise ClientSideError("The Project %d is already in "
"Project Group %d" %
(project_id, project_group_id))
project_group.projects.append(project)
session.add(project_group)
return project_group
def project_group_delete_project(project_group_id, project_id):
session = api_base.get_session()
with session.begin():
project_group = _entity_get(project_group_id, session)
if project_group is None:
raise exc.NotFound("%s %s not found"
% ("Project Group", project_group_id))
project = projects.project_get(project_id)
if project is None:
raise exc.NotFound("%s %s not found"
% ("Project", project_id))
if project_id not in [p.id for p in project_group.projects]:
raise ClientSideError("The Project %d is not in "
"Project Group %d" %
(project_id, project_group_id))
project_entry = [p for p in project_group.projects
if p.id == project_id][0]
project_group.projects.remove(project_entry)
session.add(project_group)
return project_group