Newer
Older
# SPDX-FileCopyrightText: Helmholtz-Zentrum Dresden-Rossendorf e.V. (HZDR)
#
# SPDX-License-Identifier: MIT
import os
import re
from dotenv import load_dotenv
from helmholtz_cloud_agent.core.main import HCAApplication
from helmholtz_cloud_agent.messages import (
ErrorV1,
ResourceAllocateV1,
ResourceCreatedV1,
)
from httpx import HTTPError
from pydantic import BaseModel, Field, TypeAdapter, ValidationError, field_validator
from .errors import ValidationExceptionFactory
from .gitlab_api import GitlabApi
from .logging import Logging
from .mattermost_api import MattermostApi
from .resource_spec import MmTeamResourceSpecV1
app = HCAApplication()
logger = Logging()
@app.handle(message_type="ResourceAllocateV1")
def resource_allocate_v1(
properties, payload: ResourceAllocateV1
) -> Union[ResourceCreatedV1, ErrorV1]:
"""
Allocates resource, i.e. creates a Mattermost team.
Args:
properties:
Properties of the request.
payload (ResourceAllocateV1):
Payload of the request.
Returns:
Union[ResourceCreatedV1, ErrorV1]:
On success, an object with the user ID is returned;
on failure, an object with the error message is returned.
Raises:
ValidationError:
Errors reported during validation.
"""
load_dotenv(dotenv_path="./mattermost/.env")
mm_token: str = os.getenv("MM_TOKEN")
gl_token: str = os.getenv("GL_TOKEN")
mm_domain: str = os.getenv("MM_DOMAIN")
gl_url: str = os.getenv("GL_URL")
mm_bot_username: str = os.getenv("MM_BOT_USERNAME")
if (
mm_token is None
or gl_token is None
or mm_domain is None
or gl_url is None
or mm_bot_username is None
):
raise KeyError("At least one environment variable is missing.")
gl_api = GitlabApi(logger, gl_url, gl_token)
mm_api = MattermostApi(logger, mm_domain, mm_token)
dataclass_validator = TypeAdapter(MmTeamResourceSpecV1)
try:
spec = dataclass_validator.validate_python(payload.specification)
except ValidationError as validation_error:
validation_exception_factory: ValidationExceptionFactory = (
ValidationExceptionFactory(validation_error)
)
exception_list: List = validation_exception_factory.create_exception_list()
logger.log_validation_exception(exception_list)
return ErrorV1(type="Validation Errors", message=str(exception_list))
team_name = spec.team_name
team_slug = spec.team_slug
invite_only = spec.invite_only
extern_uid = payload.target_entity.user_id_target
mm_bot_user = mm_api.get_user_by_username(mm_bot_username)
try:
requesting_gl_user = gl_api.get_user(extern_uid=extern_uid)
except IndexError:
message_account = (
f"Team '{team_name}' has not been created, because user ({extern_uid}) is not yet known "
f"in the Helmholtz Codebase or the Helmholtz ID account is not linked to the Helmholtz "
f"Codebase account."
logger.log_info_message(
message=message_account,
user=extern_uid,
team_name=team_name,
team_slug=team_slug,
)
return ErrorV1(type="User Unknown Error", message=message_account)
# note: get user by gitlab id/auth data is not supported by MM API, but email is a unique mapping as well
username = requesting_gl_user.username
requesting_mm_user = mm_api.get_user_by_email(requesting_gl_user.email)
is_team_slug_available = mm_api.is_team_slug_available(team_slug=team_slug)
if not is_team_slug_available:
message_slug = (
f"Team '{team_name}' has not been created, "
f"because team slug '{team_slug}' has already been taken."
)
logger.log_info_message(
message=message_slug,
user=username,
team_name=team_name,
team_slug=team_slug,
return ErrorV1(type="Team Slug Error", message=message_slug)
try:
team_id = mm_api.create_team(
team_name=team_name,
team_slug=team_slug,
invite_only=invite_only,
)
success_team_creation = team_id != ""
if not success_team_creation:
message_team = (
f"Team '{team_name}' has not been created and team ID has not been set."
)
logger.log_info_message(
message=message_team,
user=username,
team_name=team_name,
team_slug=team_slug,
return ErrorV1(type="Team ID Error", message=message_team)
logger.log_info_message(
message=f"Created team (ID='{team_id}') with name '{team_name}' and slug '{team_slug}' "
f"that is invite_only={str(invite_only)}.",
user=username,
team_name=team_name,
team_slug=team_slug,
)
team_type = mm_api.update_team_privacy(team_id=team_id, invite_only=invite_only)
success_team_privacy = team_type != ""
if not success_team_privacy:
message_privacy = f"Privacy of team '{team_name}' could not be set to invite_only={str(invite_only)}."
logger.log_info_message(
message=message_privacy,
user=username,
team_name=team_name,
team_slug=team_slug,
return ErrorV1(type="Team Privacy Error", message=message_privacy)
logger.log_info_message(
message=f"Set privacy (invite_only={str(invite_only)}) of team (ID='{team_id}') "
f"to type={team_type}.",
user=username,
team_name=team_name,
team_slug=team_slug,
)
success_status_add = mm_api.add_user_to_team(
user_id=requesting_mm_user["id"], as_team_admin=True, team_id=team_id
)
success_status_remove = True
if success_status_add and requesting_mm_user["id"] != mm_bot_user["id"]:
success_status_remove = mm_api.remove_user_from_team(
user_id=mm_bot_user["id"], team_id=team_id
)
if success_status_add and success_status_remove:
# success
return ResourceCreatedV1(id=team_id)
elif success_status_add and not success_status_remove:
# partially successful
message_bot = f"Team creation of team '{team_name}' succeeded, but removal of bot from team failed."
logger.log_info_message(
message=message_bot,
user=username,
team_name=team_name,
team_slug=team_slug,
return ErrorV1(type="Bot Removal Error", message=message_bot)
else:
# failed
message_failed = f"Creation of requested team '{team_name}' failed."
logger.log_info_message(
message=message_failed,
user=username,
team_name=team_name,
team_slug=team_slug,
return ErrorV1(type="Team Creation Error", message=message_failed)
except HTTPError as http_error:
message_http = (
f"Unexpected HTTPError: status={str(http_error.response.status_code)}, "
f"msg={http_error.response.reason_phrase}"
logger.log_info_message(
message=message_http,
user=username,
team_name=team_name,
team_slug=team_slug,
# catch all HTTP errors: 400 <= status <= 599
return ErrorV1(type="HTTP Error", message=message_http)
except Exception as err:
message_error = f"Unexpected {err=}, {type(err)=}"
logger.log_info_message(
message=message_error,
user=username,
team_name=team_name,
team_slug=team_slug,
return ErrorV1(type="Error", message=message_error)
app.run()