diff --git a/storyboard/api/v1/teams.py b/storyboard/api/v1/teams.py index 2850e8b2..b3d50f7c 100644 --- a/storyboard/api/v1/teams.py +++ b/storyboard/api/v1/teams.py @@ -29,6 +29,7 @@ from storyboard.api.v1 import wmodels from storyboard.common import decorators from storyboard.common import exception as exc from storyboard.db.api import base as api_base +from storyboard.db.api import projects as projects_api from storyboard.db.api import teams as teams_api from storyboard.db.api import users as users_api @@ -96,6 +97,65 @@ class UsersSubcontroller(rest.RestController): teams_api.team_delete_user(team_id, user_id) +class ProjectsSubcontroller(rest.RestController): + """This controller should be used to list, add or remove projects from a Team. + """ + @decorators.db_exceptions + @secure(checks.guest) + @wsme_pecan.wsexpose([wmodels.Project], int) + def get(self, team_id): + """Get projects related to a team. + + Example:: + + curl https://my.example.org/api/v1/teams/1/projects + + :param team_id: An ID of the team. + """ + + team = teams_api.team_get(team_id) + + if not team: + raise exc.NotFound(_("Team %s not found") % team_id) + + return [wmodels.Project.from_db_model(project) + for project in team.projects] + + @decorators.db_exceptions + @secure(checks.superuser) + @wsme_pecan.wsexpose(wmodels.Project, int, int) + def put(self, team_id, project_id): + """Relate a project to a team. + + Example:: + + TODO + + :param team_id: An ID of the team. + :param project_id: An ID of the project. + """ + + teams_api.team_add_project(team_id, project_id) + project = projects_api.project_get(project_id) + + return wmodels.Project.from_db_model(project) + + @decorators.db_exceptions + @secure(checks.superuser) + @wsme_pecan.wsexpose(None, int, int, status_code=204) + def delete(self, team_id, project_id): + """Delete a user from a team. + + Example:: + + TODO + + :param team_id: An ID of the team. + :param user_id: An ID of the user. + """ + teams_api.team_delete_project(team_id, project_id) + + class TeamsController(rest.RestController): """REST controller for Teams.""" @@ -220,6 +280,7 @@ class TeamsController(rest.RestController): raise exc.NotFound(_("Team %s not found") % team_id) users = UsersSubcontroller() + projects = ProjectsSubcontroller() def _is_int(self, s): try: @@ -234,7 +295,7 @@ class TeamsController(rest.RestController): # It's a request by a name or id first_token = args[0] if self._is_int(first_token): - if len(args) > 1 and args[1] == "users": + if len(args) > 1 and args[1] in ("projects", "users"): # Route to users subcontroller return super(TeamsController, self)._route(args, request) diff --git a/storyboard/db/api/teams.py b/storyboard/db/api/teams.py index 34953832..6c901750 100644 --- a/storyboard/db/api/teams.py +++ b/storyboard/db/api/teams.py @@ -19,6 +19,7 @@ from wsme.exc import ClientSideError from storyboard._i18n import _ 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.api import users from storyboard.db import models @@ -108,6 +109,55 @@ def team_delete_user(team_id, user_id): return team +def team_add_project(team_id, project_id): + session = api_base.get_session() + + with session.begin(subtransactions=True): + team = _entity_get(team_id, session) + if team is None: + raise exc.NotFound(_("Team %s not found") % team_id) + + project = projects.project_get(project_id) + if project is None: + raise exc.NotFound(_("Project %s not found") % project_id) + + if project_id in [p.id for p in team.projects]: + raise ClientSideError(_("The Project %(user_id)d is already " + "in Team %(team_id)d") % + {'project_id': project_id, + 'team_id': team_id}) + + team.projects.append(project) + session.add(team) + + return team + + +def team_delete_project(team_id, project_id): + session = api_base.get_session() + + with session.begin(subtransactions=True): + team = _entity_get(team_id, session) + if team is None: + raise exc.NotFound(_("Team %s not found") % team_id) + + project = projects.project_get(project_id) + if project is None: + raise exc.NotFound(_("Project %s not found") % project_id) + + if project_id not in [p.id for p in team.projects]: + raise ClientSideError(_("The Project %(user_id)d is not in " + "Team %(team_id)d") % + {'project_id': project_id, + 'team_id': team_id}) + + project_entry = [p for p in team.projects if p.id == project_id][0] + team.projects.remove(project_entry) + session.add(team) + + return team + + def team_delete(team_id): team = team_get(team_id)