backend/api/teams/resources.py (166 lines of code) (raw):
from flask_restful import Resource, request, current_app
from schematics.exceptions import DataError
from backend.models.dtos.team_dto import TeamDTO, NewTeamDTO, UpdateTeamDTO
from backend.services.team_service import TeamService, TeamServiceError, NotFound
from backend.services.users.authentication_service import token_auth
from backend.services.organisation_service import OrganisationService
from backend.services.users.user_service import UserService
from distutils.util import strtobool
class TeamsRestAPI(Resource):
@token_auth.login_required
def post(self, team_id):
"""
Updates a team information
---
tags:
- teams
produces:
- application/json
parameters:
- in: header
name: Authorization
description: Base64 encoded session token
required: true
type: string
default: Token sessionTokenHere==
- name: team_id
in: path
description: Unique team ID
required: true
type: integer
default: 1
- in: body
name: body
required: true
description: JSON object for updating a team
schema:
properties:
name:
type: string
default: HOT - Mappers
logo:
type: string
default: https://tasks.hotosm.org/assets/img/hot-tm-logo.svg
members:
type: array
items:
schema:
$ref: "#/definitions/TeamMembers"
organisation:
type: string
default: HOT
description:
type: string
default: HOT's mapping editors
inviteOnly:
type: boolean
default: false
responses:
201:
description: Team updated successfully
400:
description: Client Error - Invalid Request
401:
description: Unauthorized - Invalid credentials
500:
description: Internal Server Error
"""
try:
team_dto = TeamDTO(request.get_json())
team_dto.team_id = team_id
team_dto.validate()
authenticated_user_id = token_auth.current_user()
team_details_dto = TeamService.get_team_as_dto(
team_id, authenticated_user_id
)
org = TeamService.assert_validate_organisation(team_dto.organisation_id)
TeamService.assert_validate_members(team_details_dto)
if not TeamService.is_user_team_manager(
team_id, authenticated_user_id
) and not OrganisationService.can_user_manage_organisation(
org.id, authenticated_user_id
):
return {"Error": "User is not a admin or a manager for the team"}, 401
except DataError as e:
current_app.logger.error(f"error validating request: {str(e)}")
return str(e), 400
try:
TeamService.update_team(team_dto)
return {"Status": "Updated"}, 200
except NotFound as e:
return {"Error": str(e)}, 404
except TeamServiceError as e:
return str(e), 402
except Exception as e:
error_msg = f"Team POST - unhandled error: {str(e)}"
current_app.logger.critical(error_msg)
return {"Error": error_msg}, 500
@token_auth.login_required
def patch(self, team_id):
"""
Updates a team
---
tags:
- teams
produces:
- application/json
parameters:
- in: header
name: Authorization
description: Base64 encoded session token
required: true
type: string
default: Token sessionTokenHere==
- name: team_id
in: path
description: The unique team ID
required: true
type: integer
default: 1
- in: body
name: body
required: true
description: JSON object for updating a team
schema:
properties:
name:
type: string
default: HOT - Mappers
logo:
type: string
default: https://tasks.hotosm.org/assets/img/hot-tm-logo.svg
members:
type: array
items:
schema:
$ref: "#/definitions/TeamMembers"
organisation:
type: string
default: HOT
description:
type: string
default: HOT's mapping editors
inviteOnly:
type: boolean
default: false
responses:
201:
description: Team updated successfully
400:
description: Client Error - Invalid Request
401:
description: Unauthorized - Invalid credentials
500:
description: Internal Server Error
"""
try:
team = TeamService.get_team_by_id(team_id)
team_dto = UpdateTeamDTO(request.get_json())
team_dto.team_id = team_id
team_dto.validate()
authenticated_user_id = token_auth.current_user()
if not TeamService.is_user_team_manager(
team_id, authenticated_user_id
) and not OrganisationService.can_user_manage_organisation(
team.organisation_id, authenticated_user_id
):
return {"Error": "User is not a admin or a manager for the team"}, 401
except DataError as e:
current_app.logger.error(f"error validating request: {str(e)}")
return str(e), 400
try:
TeamService.update_team(team_dto)
return {"Status": "Updated"}, 200
except NotFound as e:
return {"Error": str(e)}, 404
except TeamServiceError as e:
return str(e), 402
except Exception as e:
error_msg = f"Team PATCH - unhandled error: {str(e)}"
current_app.logger.critical(error_msg)
return {"Error": error_msg}, 500
def get(self, team_id):
"""
Retrieves a Team
---
tags:
- teams
produces:
- application/json
parameters:
- name: team_id
in: path
description: Unique team ID
required: true
type: integer
default: 1
- in: query
name: omitMemberList
type: boolean
description: Set it to true if you don't want the members list on the response.
default: False
responses:
200:
description: Team found
401:
description: Unauthorized - Invalid credentials
404:
description: Team not found
500:
description: Internal Server Error
"""
try:
authenticated_user_id = token_auth.current_user()
omit_members = strtobool(request.args.get("omitMemberList", "false"))
if authenticated_user_id is None:
user_id = 0
else:
user_id = authenticated_user_id
team_dto = TeamService.get_team_as_dto(team_id, user_id, omit_members)
return team_dto.to_primitive(), 200
except NotFound:
return {"Error": "Team Not Found"}, 404
except Exception as e:
error_msg = f"Team GET - unhandled error: {str(e)}"
current_app.logger.critical(error_msg)
return {"Error": error_msg}, 500
# TODO: Add delete API then do front end services and ui work
@token_auth.login_required
def delete(self, team_id):
"""
Deletes a Team
---
tags:
- teams
produces:
- application/json
parameters:
- in: header
name: Authorization
description: Base64 encoded session token
required: true
type: string
default: Token sessionTokenHere==
- name: team_id
in: path
description: The unique team ID
required: true
type: integer
default: 1
responses:
200:
description: Team deleted
401:
description: Unauthorized - Invalid credentials
403:
description: Forbidden - Team has associated projects
404:
description: Team not found
500:
description: Internal Server Error
"""
if not TeamService.is_user_team_manager(team_id, token_auth.current_user()):
return {"Error": "User is not a manager for the team"}, 401
try:
TeamService.delete_team(team_id)
return {"Success": "Team deleted"}, 200
except NotFound:
return {"Error": "Team Not Found"}, 404
except Exception as e:
error_msg = f"Team DELETE - unhandled error: {str(e)}"
current_app.logger.critical(error_msg)
return {"Error": error_msg}, 500
class TeamsAllAPI(Resource):
@token_auth.login_required
def get(self):
"""
Gets all teams
---
tags:
- teams
produces:
- application/json
parameters:
- in: header
name: Authorization
description: Base64 encoded session token
required: true
type: string
default: Token sessionTokenHere==
- in: query
name: team_name
description: name of the team to filter by
type: str
default: null
- in: query
name: member
description: user ID to filter teams that the users belongs to, user must be active.
type: str
default: null
- in: query
name: manager
description: user ID to filter teams that the users has MANAGER role
type: str
default: null
- in: query
name: member_request
description: user ID to filter teams that the user has send invite request to
type: str
default: null
- in: query
name: team_role
description: team role for project
type: str
default: null
- in: query
name: organisation
description: organisation ID to filter teams
type: integer
default: null
- in: query
name: omitMemberList
type: boolean
description: Set it to true if you don't want the members list on the response.
default: False
responses:
201:
description: Team list returned successfully
400:
description: Client Error - Invalid Request
401:
description: Unauthorized - Invalid credentials
500:
description: Internal Server Error
"""
try:
user_id = token_auth.current_user()
except Exception as e:
error_msg = f"Teams GET - unhandled error: {str(e)}"
current_app.logger.critical(error_msg)
return {"Error": error_msg}, 500
filters = {}
filters["user_id"] = user_id
filters["team_name_filter"] = request.args.get("team_name")
omit_members = strtobool(request.args.get("omitMemberList", "false"))
filters["omit_members"] = omit_members
try:
member_filter = request.args.get("member")
filters["member_filter"] = int(member_filter) if member_filter else None
manager_filter = request.args.get("manager")
filters["manager_filter"] = int(manager_filter) if manager_filter else None
role_filter = request.args.get("team_role")
filters["team_role_filter"] = role_filter
member_request_filter = request.args.get("member_request")
filters["member_request_filter"] = (
int(member_request_filter) if member_request_filter else None
)
organisation_filter = request.args.get("organisation")
filters["organisation_filter"] = (
int(organisation_filter) if organisation_filter else None
)
teams = TeamService.get_all_teams(**filters)
return teams.to_primitive(), 200
except Exception as e:
error_msg = f"User GET - unhandled error: {str(e)}"
current_app.logger.critical(error_msg)
return {"Error": error_msg}, 500
@token_auth.login_required
def post(self):
"""
Creates a new team
---
tags:
- teams
produces:
- application/json
parameters:
- in: header
name: Authorization
description: Base64 encoded session token
required: true
type: string
default: Token sessionTokenHere==
- in: body
name: body
required: true
description: JSON object for creating team
schema:
properties:
name:
type: string
default: HOT - Mappers
organisation_id:
type: integer
default: 1
description:
type: string
visibility:
type: string
default: PUBLIC
inviteOnly:
type: boolean
default: false
responses:
201:
description: Team created successfully
400:
description: Client Error - Invalid Request
401:
description: Unauthorized - Invalid credentials
403:
description: Unauthorized - Forbidden
500:
description: Internal Server Error
"""
user_id = token_auth.current_user()
try:
team_dto = NewTeamDTO(request.get_json())
team_dto.creator = user_id
team_dto.validate()
except DataError as e:
current_app.logger.error(f"error validating request: {str(e)}")
return str(e), 400
try:
organisation_id = team_dto.organisation_id
is_org_manager = OrganisationService.is_user_an_org_manager(
organisation_id, user_id
)
is_admin = UserService.is_user_an_admin(user_id)
if is_admin or is_org_manager:
team_id = TeamService.create_team(team_dto)
return {"teamId": team_id}, 201
else:
error_msg = (
"Team POST - User not permitted to create team for the Organisation"
)
return {"Error": error_msg}, 403
except TeamServiceError as e:
return str(e), 400
except NotFound:
error_msg = "Team POST - Organisation does not exist"
return {"Error": error_msg}, 400
except Exception as e:
error_msg = f"Team POST - unhandled error: {str(e)}"
current_app.logger.critical(error_msg)
return {"Error": error_msg}, 500