Пример #1
0
def activation_code_by_code_delete(code: str) -> Response:
    """
    Delete an activation code based on a unique code.
    :param code: The activation code to delete.
    :return: A response object for the DELETE API request.
    """
    existing_code: Code = ActivationCodeDao.get_activation_code(code=code)

    if existing_code is None:
        response = jsonify({
            'self':
            f'/v2/activation_code/{code}',
            'deleted':
            False,
            'error':
            'there is no existing activation code with this code'
        })
        response.status_code = 400
        return response

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')
    jwt_email = jwt_claims.get('email')

    if existing_code.email == jwt_email:
        current_app.logger.info(
            f'User {jwt_username} is deleting activation code {existing_code.activation_code}.'
        )
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to delete activation code {existing_code.activation_code}.'
        )
        response = jsonify({
            'self':
            f'/v2/activation_code/{code}',
            'deleted':
            False,
            'error':
            f'User {jwt_username} is not authorized to delete activation code {existing_code.activation_code}.'
        })
        response.status_code = 400
        return response

    is_deleted = ActivationCodeDao.delete_code(activation_code=code)

    if is_deleted:
        response = jsonify({
            'self': f'/v2/activation_code/{code}',
            'deleted': True
        })
        response.status_code = 204
        return response
    else:
        response = jsonify({
            'self': f'/v2/activation_code/{code}',
            'deleted': False,
            'error': 'failed to delete the activation code'
        })
        response.status_code = 500
        return response
Пример #2
0
def comment_with_id_delete(comment_id):
    """
    Delete an existing comment.
    :param comment_id: The unique identifier for a comment.
    :return: A response object for the DELETE API request.
    """
    existing_comment: Comment = CommentDao.get_comment_by_id(
        comment_id=comment_id)

    if existing_comment is None:
        response = jsonify({
            'self': f'/v2/comments/{comment_id}',
            'deleted': False,
            'error': 'there is no existing comment with this id'
        })
        response.status_code = 400
        return response

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if existing_comment.username == jwt_username:
        current_app.logger.info(
            f'User {jwt_username} is deleting a comment with id {existing_comment.comment_id}.'
        )
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to delete a comment with id {existing_comment.comment_id}.'
        )
        response = jsonify({
            'self':
            f'/v2/comments/{comment_id}',
            'deleted':
            False,
            'error':
            f'User {jwt_username} is not authorized to delete a comment with id {existing_comment.comment_id}.'
        })
        response.status_code = 400
        return response

    is_deleted = CommentDao.delete_comment_by_id(comment_id=comment_id)

    if is_deleted:
        response = jsonify({
            'self': f'/v2/comments/{comment_id}',
            'deleted': True,
        })
        response.status_code = 204
        return response
    else:
        response = jsonify({
            'self': f'/v2/comments/{comment_id}',
            'deleted': False,
            'error': 'failed to delete the comment'
        })
        response.status_code = 500
        return response
Пример #3
0
def user_memberships_by_username_put(username) -> Response:
    """
    Update the team and group memberships of a user.
    :param username: Username that uniquely identifies a user.
    :return: A response object for the PUT API request.
    """
    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if username == jwt_username:
        current_app.logger.info(
            f'User {jwt_username} is updating their memberships.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to update the memberships of user {username}.'
        )
        response = jsonify({
            'self':
            f'/v2/users/memberships/{username}',
            'updated':
            False,
            'error':
            f'User {jwt_username} is not authorized to update the memberships of user {username}.'
        })
        response.status_code = 400
        return response

    membership_data: dict = request.get_json()
    teams_joined = membership_data.get('teams_joined')
    teams_left = membership_data.get('teams_left')
    groups_joined = membership_data.get('groups_joined')
    groups_left = membership_data.get('groups_left')

    committed: bool = False

    try:
        committed = TeamMemberDao.update_user_memberships(
            username, teams_joined, teams_left, groups_joined, groups_left)
    except SQLAlchemyError as e:
        current_app.logger.error(str(e))

    if committed:
        response = jsonify({
            'self': f'/v2/users/memberships/{username}',
            'updated': True,
        })
        response.status_code = 201
        return response
    else:
        response = jsonify({
            'self': f'/v2/users/memberships/{username}',
            'updated': False,
            'error': "failed to update the user's memberships"
        })
        response.status_code = 500
        return response
Пример #4
0
def user_update_last_login_by_username_put(username) -> Response:
    """
    Change the last login date of a user with a given username.
    :param username: Username that uniquely identifies a user.
    :return: A response object for the PUT API request.
    """
    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if username == jwt_username:
        current_app.logger.info(f'User {jwt_username} logged in.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to update the last login date of user {username}.'
        )
        response = jsonify({
            'self':
            f'/v2/users/{username}/update_last_login',
            'last_login_updated':
            False,
            'error':
            f'User {jwt_username} is not authorized to update the last login date of user {username}.'
        })
        response.status_code = 400
        return response

    last_login_updated: bool = UserDao.update_user_last_login(username)
    if last_login_updated:
        response = jsonify({
            'self': f'/v2/users/{username}/update_last_login',
            'last_login_updated': True
        })
        response.status_code = 200
        return response
    else:
        response = jsonify({
            'self':
            f'/v2/users/{username}/update_last_login',
            'last_login_updated':
            False,
            'error':
            'an unexpected error occurred updating the users last login date'
        })
        response.status_code = 500
        return response
Пример #5
0
def comment_with_id_soft_delete(comment_id):
    """
    Soft delete a comment based on a unique id.
    :param comment_id: Unique identifier for a comment.
    :return: A response object for the DELETE API request.
    """
    existing_comment: Comment = CommentDao.get_comment_by_id(
        comment_id=comment_id)

    if existing_comment is None:
        response = jsonify({
            'self': f'/v2/comments/soft/{comment_id}',
            'deleted': False,
            'error': 'there is no existing comment with this id'
        })
        response.status_code = 400
        return response

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if existing_comment.username == jwt_username:
        current_app.logger.info(
            f'User {jwt_username} is soft deleting a comment with id {existing_comment.comment_id}.'
        )
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to soft delete a comment with id {existing_comment.comment_id}.'
        )
        response = jsonify({
            'self':
            f'/v2/comments/soft/{comment_id}',
            'deleted':
            False,
            'error':
            f'User {jwt_username} is not authorized to soft delete a comment with id '
            f'{existing_comment.comment_id}.'
        })
        response.status_code = 400
        return response

    # Update the comment model to reflect the soft delete
    existing_comment.deleted = True
    existing_comment.deleted_date = datetime.now()
    existing_comment.deleted_app = 'saints-xctf-api'
    existing_comment.modified_date = datetime.now()
    existing_comment.modified_app = 'saints-xctf-api'

    is_deleted: bool = CommentDao.soft_delete_comment(existing_comment)

    if is_deleted:
        response = jsonify({
            'self': f'/v2/comments/soft/{comment_id}',
            'deleted': True,
        })
        response.status_code = 204
        return response
    else:
        response = jsonify({
            'self': f'/v2/comments/soft/{comment_id}',
            'deleted': False,
            'error': 'failed to soft delete the comment'
        })
        response.status_code = 500
        return response
Пример #6
0
def comment_with_id_put(comment_id):
    """
    Update an existing comment.
    :param comment_id: The unique identifier for a comment.
    :return: A response object for the PUT API request.
    """
    old_comment: Comment = CommentDao.get_comment_by_id(comment_id=comment_id)

    if old_comment is None:
        response = jsonify({
            'self': f'/v2/comments/{comment_id}',
            'updated': False,
            'comment': None,
            'error': 'there is no existing comment with this id'
        })
        response.status_code = 400
        return response

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if old_comment.username == jwt_username:
        current_app.logger.info(
            f'User {jwt_username} is updating a comment with id {old_comment.comment_id}.'
        )
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to update a comment with id {old_comment.comment_id}.'
        )
        response = jsonify({
            'self':
            f'/v2/comments/{comment_id}',
            'updated':
            False,
            'comment':
            None,
            'error':
            f'User {jwt_username} is not authorized to update a comment with id {old_comment.comment_id}.'
        })
        response.status_code = 400
        return response

    comment_data: dict = request.get_json()
    new_comment = Comment(comment_data)

    if old_comment != new_comment:

        new_comment.modified_date = datetime.now()
        new_comment.modified_app = 'saints-xctf-api'

        is_updated = CommentDao.update_comment(comment=new_comment)

        if is_updated:
            updated_comment: Comment = CommentDao.get_comment_by_id(
                comment_id=new_comment.comment_id)
            updated_comment_dict: dict = CommentData(updated_comment).__dict__

            response = jsonify({
                'self': f'/v2/comments/{comment_id}',
                'updated': True,
                'comment': updated_comment_dict
            })
            response.status_code = 200
            return response
        else:
            response = jsonify({
                'self': f'/v2/comments/{comment_id}',
                'updated': False,
                'comment': None,
                'error': 'the comment failed to update'
            })
            response.status_code = 500
            return response
    else:
        response = jsonify({
            'self':
            f'/v2/comments/{comment_id}',
            'updated':
            False,
            'comment':
            None,
            'error':
            'the comment submitted is equal to the existing comment with the same id'
        })
        response.status_code = 400
        return response
Пример #7
0
def log_feed_get(filter_by, bucket, limit, offset) -> Response:
    """
    Get a list of exercise logs based on certain filters.
    :param filter_by: The filtering mechanism for the exercise logs.
    You can filter by user (username) or group (group_name).
    :param bucket: The bucket to filter by (either a username or a group name)
    :param limit: The maximum number of logs to return
    :param offset: The number of logs to skip from the results of this filter before returning
    :return: A response object for the GET API request.
    """
    logs: ResultProxy = None
    count: int = 0
    limit = int(limit)
    offset = int(offset)

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if filter_by == 'group' or filter_by == 'groups':
        logs = LogDao.get_group_log_feed(group_id=int(bucket), limit=limit, offset=offset)
        count = LogDao.get_group_log_feed_count(group_id=int(bucket)).first()['count']
    elif filter_by == 'user' or filter_by == 'users' or filter_by == 'username':
        logs = LogDao.get_user_log_feed(username=bucket, limit=limit, offset=offset)
        count = LogDao.get_user_log_feed_count(username=bucket).first()['count']
    elif filter_by == 'all':
        logs = LogDao.get_log_feed(limit=limit, offset=offset, username=jwt_username)
        count = LogDao.get_log_feed_count().first()['count']

    pages = int((count - 1) / limit) + 1

    # Generate LogFeed API URLs
    self_url = f'/v2/log_feed/{filter_by}/{bucket}/{limit}/{offset}'

    prev_offset = offset - limit
    if prev_offset >= 0:
        prev_url = f'/v2/log_feed/{filter_by}/{bucket}/{limit}/{prev_offset}'
    else:
        prev_url = None

    if logs is None or logs.rowcount == 0:
        response = jsonify({
            'self': self_url,
            'next': None,
            'prev': prev_url,
            'logs': None,
            'pages': 0,
            'error': 'no logs found in this feed'
        })
        response.status_code = 500
        return response
    else:
        log_list = []
        for log in logs:
            comments: list = CommentDao.get_comments_by_log_id(log.log_id)
            comments = [CommentData(comment).__dict__ for comment in comments]

            log_list.append({
                'log_id': log.log_id,
                'username': log.username,
                'first': log.first,
                'last': log.last,
                'name': log.name,
                'location': log.location,
                'date': str(log.date) if log.date is not None else None,
                'type': log.type,
                'distance': log.distance,
                'metric': log.metric,
                'miles': log.miles,
                'time': str(log.time) if log.time is not None else None,
                'pace': str(log.pace) if log.pace is not None else None,
                'feel': log.feel,
                'description': log.description,
                'comments': comments
            })

        next_url = f'/v2/log_feed/{filter_by}/{bucket}/{limit}/{offset + limit}'

        response = jsonify({
            'self': self_url,
            'next': next_url,
            'prev': prev_url,
            'logs': log_list,
            'pages': pages,
        })
        response.status_code = 200
        return response
def notification_by_id_delete(notification_id) -> Response:
    """
    Hard delete an existing notification with a given unique ID.
    :param notification_id: Unique identifier for a user's notification.
    :return: A response object for the DELETE API request.
    """
    existing_notification: Notification = NotificationDao.get_notification_by_id(
        notification_id=notification_id)

    if existing_notification is None:
        response = jsonify({
            'self':
            f'/v2/notifications/{notification_id}',
            'deleted':
            False,
            'error':
            'There is no existing notification with this id.'
        })
        response.status_code = 400
        return response

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if existing_notification.username == jwt_username:
        current_app.logger.info(
            f'User {jwt_username} is deleting their notification with id {existing_notification.notification_id}.'
        )
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to delete a notification sent to {existing_notification.username}.'
        )
        response = jsonify({
            'self':
            f'/v2/notifications/{notification_id}',
            'deleted':
            False,
            'error':
            f'User {jwt_username} is not authorized to delete a notification sent to '
            f'{existing_notification.username}.'
        })
        response.status_code = 400
        return response

    is_deleted = NotificationDao.delete_notification_by_id(
        notification_id=notification_id)

    if is_deleted:
        response = jsonify({
            'self': f'/v2/notifications/{notification_id}',
            'deleted': True,
        })
        response.status_code = 204
        return response
    else:
        response = jsonify({
            'self': f'/v2/notifications/{notification_id}',
            'deleted': False,
            'error': 'failed to delete the notification'
        })
        response.status_code = 500
        return response
Пример #9
0
def user_by_username_put(username) -> Response:
    """
    Update an existing user with a given username.
    :param username: Username that uniquely identifies a user.
    :return: A response object for the PUT API request.
    """
    old_user: User = UserDao.get_user_by_username(username=username)

    if old_user is None:
        response = jsonify({
            'self':
            f'/v2/users/{username}',
            'updated':
            False,
            'user':
            None,
            'error':
            'there is no existing user with this username'
        })
        response.status_code = 400
        return response

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if username == jwt_username:
        current_app.logger.info(
            f'User {jwt_username} is updating their user details.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to update user {username}.'
        )
        response = jsonify({
            'self':
            f'/v2/users/{username}',
            'updated':
            False,
            'user':
            None,
            'error':
            f'User {jwt_username} is not authorized to update user {username}.'
        })
        response.status_code = 400
        return response

    user_data: dict = request.get_json()
    new_user = User(user_data)

    if new_user != old_user:
        is_updated = UserDao.update_user(username, new_user)

        if is_updated:
            updated_user = UserDao.get_user_by_username(username)

            updated_user_dict: dict = UserData(updated_user).__dict__

            if updated_user_dict.get('profilepic') is not None:
                try:
                    updated_user_dict['profilepic'] = updated_user_dict[
                        'profilepic'].decode('utf-8')
                except AttributeError:
                    pass

            response = jsonify({
                'self': f'/v2/users/{username}',
                'updated': True,
                'user': updated_user_dict
            })
            response.status_code = 200
            return response
        else:
            response = jsonify({
                'self': f'/v2/users/{username}',
                'updated': False,
                'user': None,
                'error': 'the user failed to update'
            })
            response.status_code = 500
            return response
    else:
        response = jsonify({
            'self':
            f'/v2/users/{username}',
            'updated':
            False,
            'user':
            None,
            'error':
            'the user submitted is equal to the existing user with the same username'
        })
        response.status_code = 400
        return response
Пример #10
0
def logs_post() -> Response:
    """
    Create a new exercise log.
    :return: A response object for the POST API request.
    """
    log_data: dict = request.get_json()

    if log_data is None:
        response = jsonify({
            'self': f'/v2/logs',
            'added': False,
            'log': None,
            'error': "the request body isn't populated"
        })
        response.status_code = 400
        return response

    log_to_add: Log = Log(log_data)

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if log_to_add.username == jwt_username:
        current_app.logger.info(f'User {jwt_username} is uploading a new exercise log.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to upload an exercise log for user {log_to_add.username}.'
        )
        response = jsonify({
            'self': f'/v2/logs',
            'added': False,
            'log': None,
            'error': f'User {jwt_username} is not authorized to upload an exercise log for user {log_to_add.username}.'
        })
        response.status_code = 400
        return response

    if None in [log_to_add.username, log_to_add.first, log_to_add.last, log_to_add.date,
                log_to_add.type, log_to_add.feel]:
        response = jsonify({
            'self': f'/v2/logs',
            'added': False,
            'log': None,
            'error': "'username', 'first', 'last', 'date', 'type', and 'feel' are required fields"
        })
        response.status_code = 400
        return response

    # Compute pace and miles based on time, metric, and distance
    if log_to_add.distance and log_to_add.metric:
        log_to_add.miles = to_miles(log_to_add.metric, log_to_add.distance)
        log_to_add.pace = calculate_mile_pace(log_to_add.miles, log_to_add.time)

    log_to_add.time_created = datetime.now()
    log_to_add.created_date = datetime.now()
    log_to_add.created_app = 'saints-xctf-api'
    log_to_add.created_user = None
    log_to_add.modified_date = None
    log_to_add.modified_app = None
    log_to_add.modified_user = None
    log_to_add.deleted_date = None
    log_to_add.deleted_app = None
    log_to_add.deleted_user = None
    log_to_add.deleted = False

    log_added_successfully = LogDao.add_log(new_log=log_to_add)

    if log_added_successfully:
        log_added: Log = LogDao.get_log_by_id(log_id=log_to_add.log_id)

        log_dict: dict = LogData(log_added).__dict__

        if log_dict.get('date') is not None:
            log_dict['date'] = str(log_dict['date'])
        if log_dict.get('time') is not None:
            log_dict['time'] = str(log_dict['time'])
        if log_dict.get('pace') is not None:
            log_dict['pace'] = str(log_dict['pace'])

        response = jsonify({
            'self': '/v2/logs',
            'added': True,
            'log': log_dict
        })
        response.status_code = 200
        return response
    else:
        response = jsonify({
            'self': '/v2/logs',
            'added': False,
            'log': None,
            'error': 'failed to create a new log'
        })
        response.status_code = 500
        return response
Пример #11
0
def group_members_by_group_id_and_username_delete(group_id: str,
                                                  username: str) -> Response:
    """
    Soft delete a group membership.  The membership is identified by a group's identifier and a user's username.
    :param group_id: Unique id which identifies a group within a team.
    :param username: Unique name for a user.
    :return: A response object for the DELETE API request.
    """
    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    group_member: GroupMember = GroupMemberDao.get_group_member(
        group_id=int(group_id), username=jwt_username)

    if group_member is not None and group_member.user == 'admin' and group_member.status == 'accepted':
        current_app.logger.info(
            f'Admin user {jwt_username} is deleting the group membership for user {username} in group with id '
            f'{group_id}.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to delete the group membership for user {username} in group with '
            f'id {group_id}.')
        response = jsonify({
            'self':
            f'/v2/groups/members/{group_id}/{username}',
            'deleted':
            False,
            'error':
            f'User {jwt_username} is not authorized to delete the group membership for user {username} in '
            f'group with id {group_id}.'
        })
        response.status_code = 400
        return response

    membership_deleted = GroupMemberDao.soft_delete_group_member(
        int(group_id), username)

    if membership_deleted:
        team: Team = TeamDao.get_team_by_group_id(int(group_id))
        user_groups: ResultProxy = GroupMemberDao.get_user_groups_in_team(
            username, team.name)

        # If the user has no more group memberships in this team, remove them from the team.
        if user_groups.rowcount == 0:
            TeamMemberDao.update_user_memberships(username=username,
                                                  teams_joined=[],
                                                  teams_left=[team.name],
                                                  groups_joined=[],
                                                  groups_left=[])

        response = jsonify({
            'self': f'/v2/groups/members/{group_id}/{username}',
            'deleted': True,
        })
        response.status_code = 204
        return response
    else:
        response = jsonify({
            'self': f'/v2/groups/members/{group_id}/{username}',
            'deleted': False,
            'error': 'Failed to delete the group membership.'
        })
        response.status_code = 500
        return response
Пример #12
0
def group_members_by_group_id_and_username_put(group_id: str,
                                               username: str) -> Response:
    """
    Update a group membership.  The membership is identified by a group's identifier and a user's username.
    :param group_id: Unique id which identifies a group within a team.
    :param username: Unique name for a user.
    :return: A response object for the PUT API request.
    """
    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    group_member: GroupMember = GroupMemberDao.get_group_member(
        group_id=int(group_id), username=jwt_username)

    if group_member is not None and group_member.user == 'admin' and group_member.status == 'accepted':
        current_app.logger.info(
            f'Admin user {jwt_username} is updating the group membership for user {username} in group with id '
            f'{group_id}.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to update the group membership for user {username} in group with '
            f'id {group_id}.')
        response = jsonify({
            'self':
            f'/v2/groups/members/{group_id}/{username}',
            'updated':
            False,
            'group_member':
            None,
            'error':
            f'User {jwt_username} is not authorized to update the group membership for user {username} in '
            f'group with id {group_id}.'
        })
        response.status_code = 400
        return response

    group_member_data: dict = request.get_json()
    status = group_member_data.get('status')
    user = group_member_data.get('user')

    is_updated = GroupMemberDao.update_group_member(int(group_id), username,
                                                    status, user)

    if is_updated:
        team: Team = TeamDao.get_team_by_group_id(int(group_id))
        team_membership: ResultProxy = TeamMemberDao.get_user_team_membership(
            username=username, team_name=team.name)

        if team_membership.rowcount > 0:
            for membership in team_membership:
                if membership.user != 'accepted':
                    TeamMemberDao.accept_user_team_membership(
                        username=username,
                        team_name=team.name,
                        updating_username=jwt_username)

        updated_group_member = GroupMemberDao.get_group_member(
            int(group_id), username)
        updated_group_member_dict: dict = GroupMemberData(
            updated_group_member).__dict__

        response = jsonify({
            'self': f'/v2/groups/members/{group_id}/{username}',
            'updated': True,
            'group_member': updated_group_member_dict
        })
        response.status_code = 200
        return response
    else:
        response = jsonify({
            'self': f'/v2/groups/members/{group_id}/{username}',
            'updated': False,
            'group_member': None,
            'error': 'The group membership failed to update.'
        })
        response.status_code = 500
        return response
Пример #13
0
def group_by_id_put(group_id: str) -> Response:
    """
    Update a group based on the unique group id.
    :param group_id: Unique id which identifies a group.
    :return: A response object for the PUT API request.
    """
    old_group = GroupDao.get_group_by_id(int(group_id))

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    group_member: GroupMember = GroupMemberDao.get_group_member(
        group_id=int(group_id), username=jwt_username)

    if group_member is not None and group_member.user == 'admin' and group_member.status == 'accepted':
        current_app.logger.info(
            f'Admin user {jwt_username} is updating a group with id {group_id}.'
        )
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to update a group with id {group_id}.'
        )
        response = jsonify({
            'self':
            f'/v2/groups/{group_id}',
            'updated':
            False,
            'group':
            None,
            'error':
            f'User {jwt_username} is not authorized to update a group with id {group_id}.'
        })
        response.status_code = 400
        return response

    if old_group is None:
        response = jsonify({
            'self': f'/v2/groups/{group_id}',
            'updated': False,
            'group': None,
            'error': 'There is no existing group with this id.'
        })
        response.status_code = 400
        return response

    group_data: dict = request.get_json()
    new_group = Group(group_data)

    if new_group != old_group:
        new_group.modified_date = datetime.now()
        new_group.modified_app = 'saints-xctf-api'

        is_updated = GroupDao.update_group(group=new_group)

        if is_updated:
            updated_group = GroupDao.get_group_by_id(int(group_id))
            updated_group_dict: dict = GroupData(updated_group).__dict__

            response = jsonify({
                'self': f'/v2/groups/{group_id}',
                'updated': True,
                'group': updated_group_dict
            })
            response.status_code = 200
            return response
        else:
            response = jsonify({
                'self': f'/v2/groups/{group_id}',
                'updated': False,
                'group': None,
                'error': 'The group failed to update.'
            })
            response.status_code = 500
            return response
    else:
        response = jsonify({
            'self':
            f'/v2/groups/{group_id}',
            'updated':
            False,
            'group':
            None,
            'error':
            'The group submitted is equal to the existing group with the same id.'
        })
        response.status_code = 400
        return response
Пример #14
0
def group_by_group_name_put(team_name: str, group_name: str) -> Response:
    """
    Update a group in the database.
    :param team_name: Unique name which identifies a team.
    :param group_name: Unique name which identifies a group within a team.
    :return: A response object for the PUT API request.
    """
    old_group_row: Optional[RowProxy] = GroupDao.get_group(
        team_name=team_name, group_name=group_name)

    if old_group_row is None:
        response = jsonify({
            'self': f'/v2/groups/{team_name}/{group_name}',
            'updated': False,
            'group': None,
            'error': 'there is no existing group with this name'
        })
        response.status_code = 400
        return response

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    group_member: GroupMember = GroupMemberDao.get_group_member_by_group_name(
        team_name=team_name, group_name=group_name, username=jwt_username)

    if group_member is not None and group_member.user == 'admin' and group_member.status == 'accepted':
        current_app.logger.info(
            f'Admin user {jwt_username} is updating a group with name {group_name} in team {team_name}.'
        )
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to update a group with name {group_name} in team {team_name}.'
        )
        response = jsonify({
            'self':
            f'/v2/groups/{team_name}/{group_name}',
            'updated':
            False,
            'group':
            None,
            'error':
            f'User {jwt_username} is not authorized to update a group with name {group_name} in team '
            f'{team_name}.'
        })
        response.status_code = 400
        return response

    old_group_dict = {key: value for key, value in old_group_row.items()}
    old_group = Group(old_group_dict)
    group_data: dict = request.get_json()
    new_group = Group(group_data)

    if old_group != new_group:

        new_group.modified_date = datetime.now()
        new_group.modified_app = 'saints-xctf-api'

        is_updated = GroupDao.update_group(group=new_group)

        if is_updated:
            updated_group_row: Optional[RowProxy] = GroupDao.get_group(
                team_name=team_name, group_name=new_group.group_name)

            updated_group_dict = {
                key: value
                for key, value in updated_group_row.items()
            }
            response = jsonify({
                'self': f'/v2/groups/{team_name}/{group_name}',
                'updated': True,
                'group': updated_group_dict
            })
            response.status_code = 200
            return response
        else:
            response = jsonify({
                'self': f'/v2/groups/{team_name}/{group_name}',
                'updated': False,
                'group': None,
                'error': 'the group failed to update'
            })
            response.status_code = 500
            return response
    else:
        response = jsonify({
            'self':
            f'/v2/groups/{team_name}/{group_name}',
            'updated':
            False,
            'group':
            None,
            'error':
            'the group submitted is equal to the existing group with the same name'
        })
        response.status_code = 400
        return response
Пример #15
0
def activation_code_by_code_soft_delete(code: str) -> Response:
    """
    Soft delete an activation code based on a unique code.
    :param code: The activation code to delete.
    :return: A response object for the DELETE API request.
    """
    existing_code: Code = ActivationCodeDao.get_activation_code(code=code)

    if existing_code is None:
        response = jsonify({
            'self':
            f'/v2/activation_code/soft/{code}',
            'deleted':
            False,
            'error':
            'there is no existing activation code with this code'
        })
        response.status_code = 400
        return response

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')
    jwt_email = jwt_claims.get('email')

    if existing_code.email == jwt_email:
        current_app.logger.info(
            f'User {jwt_username} is soft deleting activation code {existing_code.activation_code}.'
        )
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to soft delete activation code {existing_code.activation_code}.'
        )
        response = jsonify({
            'self':
            f'/v2/activation_code/soft/{code}',
            'deleted':
            False,
            'error':
            f'User {jwt_username} is not authorized to soft delete activation code '
            f'{existing_code.activation_code}.'
        })
        response.status_code = 400
        return response

    # Update the activation code model to reflect the soft delete
    existing_code.deleted = True
    existing_code.deleted_date = datetime.now()
    existing_code.deleted_app = 'saints-xctf-api'
    existing_code.modified_date = datetime.now()
    existing_code.modified_app = 'saints-xctf-api'

    is_deleted = ActivationCodeDao.soft_delete_code(existing_code)

    if is_deleted:
        response = jsonify({
            'self': f'/v2/activation_code/soft/{code}',
            'deleted': True
        })
        response.status_code = 204
        return response
    else:
        response = jsonify({
            'self': f'/v2/activation_code/soft/{code}',
            'deleted': False,
            'error': 'failed to soft delete the activation code'
        })
        response.status_code = 500
        return response
Пример #16
0
def forgot_password_get(username) -> Response:
    """
    Retrieve an existing forgot password code for a specific user.
    :param username: Uniquely identifies a user.
    :return: JSON with the resulting Forgot Password object and relevant metadata.
    """
    user: User = UserDao.get_user_by_username(username=username)

    # If the user cant be found, try searching the email column in the database
    if user is None:
        email = username
        user: User = UserDao.get_user_by_email(email=email)

    if user is None:
        response = jsonify({
            'self':
            f'/v2/forgot_password/{username}',
            'forgot_password_codes': [],
            'error':
            'There is no user associated with this username/email.'
        })
        response.status_code = 400
        return response

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if user.username == jwt_username:
        current_app.logger.info(
            f'User {jwt_username} is accessing their forgot password codes.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to access forgot password codes for user {user.username}.'
        )
        response = jsonify({
            'self':
            f'/v2/forgot_password/{username}',
            'created':
            False,
            'error':
            f'User {jwt_username} is not authorized to access forgot password codes for user {user.username}.'
        })
        response.status_code = 400
        return response

    forgot_password_codes: ResultProxy = ForgotPasswordDao.get_forgot_password_codes(
        username=user.username)

    if forgot_password_codes is None:
        response = jsonify({
            'self': f'/v2/forgot_password/{username}',
            'forgot_password_codes': [],
            'error': 'This user has no forgot password codes.'
        })
        response.status_code = 400
        return response
    else:
        forgot_password_list = []
        for code in forgot_password_codes:
            fpw = ForgotPasswordData(None)
            fpw.forgot_code = code[0]
            fpw.username = code[1]
            fpw.expires = code[2]
            fpw.deleted = code[3]
            forgot_password_list.append(fpw.__dict__)

        response = jsonify({
            'self': f'/v2/forgot_password/{username}',
            'forgot_password_codes': forgot_password_list,
        })
        response.status_code = 200
        return response
Пример #17
0
def log_by_id_put(log_id) -> Response:
    """
    Update an existing exercise log based on a unique ID.
    :param log_id: The unique identifier for an exercise log.
    :return: A response object for the PUT API request.
    """
    old_log: Log = LogDao.get_log_by_id(log_id=log_id)

    if old_log is None:
        response = jsonify({
            'self': f'/v2/logs/{log_id}',
            'updated': False,
            'log': None,
            'error': 'there is no existing log with this id'
        })
        response.status_code = 400
        return response

    log_data: dict = request.get_json()
    new_log = Log(log_data)

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if old_log.username == jwt_username:
        current_app.logger.info(f'User {jwt_username} is updating their exercise log with id {log_id}.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to update an exercise log owned by user {old_log.username}.'
        )
        response = jsonify({
            'self': f'/v2/logs/{log_id}',
            'updated': False,
            'log': None,
            'error': f'User {jwt_username} is not authorized to update an exercise log owned by user '
                     f'{old_log.username}.'
        })
        response.status_code = 400
        return response

    if new_log.distance and new_log.metric:
        new_log.miles = to_miles(new_log.metric, new_log.distance)
        new_log.pace = calculate_mile_pace(new_log.miles, new_log.time)

    if old_log != new_log:
        new_log.modified_date = datetime.now()
        new_log.modified_app = 'saints-xctf-api'

        is_updated: bool = LogDao.update_log(new_log)

        if is_updated:
            updated_log: Log = LogDao.get_log_by_id(log_id=new_log.log_id)

            log_dict: dict = LogData(updated_log).__dict__

            if log_dict.get('date') is not None:
                log_dict['date'] = str(log_dict['date'])
            if log_dict.get('time') is not None:
                log_dict['time'] = str(log_dict['time'])
            if log_dict.get('pace') is not None:
                log_dict['pace'] = str(log_dict['pace'])

            response = jsonify({
                'self': f'/v2/logs/{log_id}',
                'updated': True,
                'log': log_dict
            })
            response.status_code = 200
            return response
        else:
            response = jsonify({
                'self': f'/v2/logs/{log_id}',
                'updated': False,
                'log': None,
                'error': 'the log failed to update'
            })
            response.status_code = 500
            return response

    else:
        response = jsonify({
            'self': f'/v2/logs/{log_id}',
            'updated': False,
            'log': None,
            'error': 'the log submitted is equal to the existing log with the same id'
        })
        response.status_code = 400
        return response
Пример #18
0
def log_by_id_delete(log_id) -> Response:
    """
    Delete an existing exercise log based on a unique ID.
    :param log_id: The unique identifier for an exercise log.
    :return: A response object for the DELETE API request.
    """
    existing_log: Log = LogDao.get_log_by_id(log_id=log_id)

    if existing_log is None:
        response = jsonify({
            'self': f'/v2/logs/{log_id}',
            'deleted': False,
            'error': 'There is no existing exercise log with this id.'
        })
        response.status_code = 400
        return response

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if existing_log.username == jwt_username:
        current_app.logger.info(f'User {jwt_username} is deleting their exercise log with id {log_id}.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to delete an exercise log owned by user {existing_log.username}.'
        )
        response = jsonify({
            'self': f'/v2/logs/{log_id}',
            'deleted': False,
            'error': f'User {jwt_username} is not authorized to delete an exercise log owned by user '
                     f'{existing_log.username}.'
        })
        response.status_code = 400
        return response

    are_comments_deleted = CommentDao.delete_comments_by_log_id(log_id=log_id)

    if not are_comments_deleted:
        response = jsonify({
            'self': f'/v2/logs/{log_id}',
            'deleted': False,
            'error': 'failed to delete the comments on this log'
        })
        response.status_code = 500
        return response

    is_log_deleted = LogDao.delete_log(log_id=log_id)

    if is_log_deleted:
        response = jsonify({
            'self': f'/v2/logs/{log_id}',
            'deleted': True,
        })
        response.status_code = 204
        return response
    else:
        response = jsonify({
            'self': f'/v2/logs/{log_id}',
            'deleted': False,
            'error': 'failed to delete the log'
        })
        response.status_code = 500
        return response
Пример #19
0
def user_by_username_soft_delete(username) -> Response:
    """
    Soft delete an existing user with a given username.
    :param username: Username that uniquely identifies a user.
    :return: A response object for the DELETE API request.
    """
    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if username == jwt_username:
        current_app.logger.info(
            f'User {jwt_username} is soft deleting their user.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to soft delete user {username}.'
        )
        response = jsonify({
            'self':
            f'/v2/users/soft/{username}',
            'deleted':
            False,
            'error':
            f'User {jwt_username} is not authorized to soft delete user {username}.'
        })
        response.status_code = 400
        return response

    existing_user: User = UserDao.get_user_by_username(username=username)

    if existing_user is None:
        response = jsonify({
            'self':
            f'/v2/users/soft/{username}',
            'deleted':
            False,
            'error':
            'there is no existing user with this username'
        })
        response.status_code = 400
        return response

    # Update the user model to reflect the soft delete
    existing_user.deleted = True
    existing_user.deleted_date = datetime.now()
    existing_user.deleted_app = 'saints-xctf-api'
    existing_user.modified_date = datetime.now()
    existing_user.modified_app = 'saints-xctf-api'

    is_deleted: bool = UserDao.soft_delete_user(existing_user)

    if is_deleted:
        response = jsonify({
            'self': f'/v2/users/soft/{username}',
            'deleted': True,
        })
        response.status_code = 204
        return response
    else:
        response = jsonify({
            'self': f'/v2/users/soft/{username}',
            'deleted': False,
            'error': 'failed to soft delete the user'
        })
        response.status_code = 500
        return response
Пример #20
0
def log_by_id_soft_delete(log_id) -> Response:
    """
    Soft delete an exercise log based on a unique id.
    :param log_id: Unique identifier for an exercise log.
    :return: A response object for the DELETE API request.
    """
    existing_log: Log = LogDao.get_log_by_id(log_id=log_id)

    if existing_log is None:
        response = jsonify({
            'self': f'/v2/logs/soft/{log_id}',
            'deleted': False,
            'error': 'there is no existing exercise log with this id'
        })
        response.status_code = 400
        return response

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if existing_log.username == jwt_username:
        current_app.logger.info(f'User {jwt_username} is soft deleting their exercise log with id {log_id}.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to soft delete an exercise log owned by user '
            f'{existing_log.username}.'
        )
        response = jsonify({
            'self': f'/v2/logs/{log_id}',
            'deleted': False,
            'error': f'User {jwt_username} is not authorized to soft delete an exercise log owned by user '
                     f'{existing_log.username}.'
        })
        response.status_code = 400
        return response

    if existing_log.deleted:
        response = jsonify({
            'self': f'/v2/logs/soft/{log_id}',
            'deleted': False,
            'error': 'this exercise log is already soft deleted'
        })
        response.status_code = 400
        return response

    # Update the comment model to reflect the soft delete
    existing_log.deleted = True
    existing_log.deleted_date = datetime.now()
    existing_log.deleted_app = 'saints-xctf-api'
    existing_log.modified_date = datetime.now()
    existing_log.modified_app = 'saints-xctf-api'

    is_deleted: bool = LogDao.soft_delete_log(existing_log)

    if is_deleted:
        response = jsonify({
            'self': f'/v2/logs/soft/{log_id}',
            'deleted': True,
        })
        response.status_code = 204
        return response
    else:
        response = jsonify({
            'self': f'/v2/logs/soft/{log_id}',
            'deleted': False,
            'error': 'failed to soft delete the log'
        })
        response.status_code = 500
        return response
Пример #21
0
def notification_by_id_put(notification_id) -> Response:
    """
    Update an existing notification based on its identifier.
    :param notification_id: Unique identifier for a user's notification.
    :return: A response object for the PUT API request.
    """
    notification_id = int(notification_id)
    old_notification = NotificationDao.get_notification_by_id(
        notification_id=notification_id)

    if old_notification is None:
        response = jsonify({
            'self':
            f'/v2/notifications/{notification_id}',
            'updated':
            False,
            'notification':
            None,
            'error':
            'there is no existing notification with this id'
        })
        response.status_code = 400
        return response

    notification_data: dict = request.get_json()
    new_notification = Notification(notification_data)
    new_notification.notification_id = notification_id

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if old_notification.username == jwt_username:
        current_app.logger.info(
            f'User {jwt_username} is updating their notification with id {new_notification.notification_id}.'
        )
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to update a notification sent to {old_notification.username}.'
        )
        response = jsonify({
            'self':
            f'/v2/notifications/{notification_id}',
            'updated':
            False,
            'notification':
            None,
            'error':
            f'User {jwt_username} is not authorized to update a notification sent to '
            f'{old_notification.username}.'
        })
        response.status_code = 400
        return response

    if old_notification != new_notification:
        new_notification.modified_date = datetime.now()
        new_notification.modified_app = 'saints-xctf-api'

        is_updated = NotificationDao.update_notification(
            notification=new_notification)

        if is_updated:
            updated_notification = NotificationDao.get_notification_by_id(
                notification_id=notification_id)
            notification_dict = NotificationData(updated_notification).__dict__
            notification_dict['time'] = str(notification_dict['time'])

            response = jsonify({
                'self': f'/v2/notifications/{notification_id}',
                'updated': True,
                'notification': notification_dict
            })
            response.status_code = 200
            return response
        else:
            response = jsonify({
                'self': f'/v2/notifications/{notification_id}',
                'updated': False,
                'notification': None,
                'error': 'the notification failed to update'
            })
            response.status_code = 500
            return response
    else:
        response = jsonify({
            'self':
            f'/v2/notifications/{notification_id}',
            'updated':
            False,
            'notification':
            None,
            'error':
            'the notification submitted is equal to the existing notification with the same id'
        })
        response.status_code = 400
        return response
Пример #22
0
def comment_post():
    """
    Create a new comment.
    :return: A response object for the POST API request.
    """
    comment_data: dict = request.get_json()

    if comment_data is None:
        response = jsonify({
            'self': f'/v2/comments',
            'added': False,
            'comment': None,
            'error': "the request body isn't populated"
        })
        response.status_code = 400
        return response

    comment_to_add = Comment(comment_data)

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if comment_to_add.username == jwt_username:
        # You are so loved.
        current_app.logger.info(
            f'User {jwt_username} is creating a comment on log {comment_to_add.log_id}.'
        )
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to create a comment for user {comment_to_add.username}.'
        )
        response = jsonify({
            'self':
            f'/v2/comments',
            'added':
            False,
            'comment':
            None,
            'error':
            f'User {jwt_username} is not authorized to create a comment for user {comment_to_add.username}.'
        })
        response.status_code = 400
        return response

    if None in [
            comment_to_add.username, comment_to_add.first, comment_to_add.last,
            comment_to_add.log_id
    ]:
        response = jsonify({
            'self':
            f'/v2/comments',
            'added':
            False,
            'comment':
            None,
            'error':
            "'username', 'first', 'last', and 'log_id' are required fields"
        })
        response.status_code = 400
        return response

    comment_to_add.time = datetime.now()
    comment_to_add.created_date = datetime.now()
    comment_to_add.created_app = 'saints-xctf-api'
    comment_to_add.created_user = None
    comment_to_add.modified_date = None
    comment_to_add.modified_app = None
    comment_to_add.modified_user = None
    comment_to_add.deleted_date = None
    comment_to_add.deleted_app = None
    comment_to_add.deleted_user = None
    comment_to_add.deleted = False

    comment_added_successfully: bool = CommentDao.add_comment(
        new_comment=comment_to_add)

    if comment_added_successfully:
        comment_added = CommentDao.get_comment_by_id(comment_to_add.comment_id)
        comment_added_dict: dict = CommentData(comment_added).__dict__

        response = jsonify({
            'self': '/v2/comments',
            'added': True,
            'comment': comment_added_dict
        })
        response.status_code = 200
        return response
    else:
        response = jsonify({
            'self': '/v2/comments',
            'added': False,
            'comment': None,
            'error': 'failed to create a new comment'
        })
        response.status_code = 500
        return response
Пример #23
0
def notification_by_id_soft_delete(notification_id) -> Response:
    """
    Soft delete an existing notification with a given unique ID.
    :param notification_id: The unique identifier for a user notification
    :return: A response object for the DELETE API request.
    """
    existing_notification: Notification = NotificationDao.get_notification_by_id(
        notification_id=notification_id)

    if existing_notification is None:
        response = jsonify({
            'self':
            f'/v2/notifications/soft/{notification_id}',
            'deleted':
            False,
            'error':
            'there is no existing notification with this id'
        })
        response.status_code = 400
        return response

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if existing_notification.username == jwt_username:
        current_app.logger.info(
            f'User {jwt_username} is soft deleting their notification with id {existing_notification.notification_id}.'
        )
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to soft delete a notification sent to '
            f'{existing_notification.username}.')
        response = jsonify({
            'self':
            f'/v2/notifications/soft/{notification_id}',
            'deleted':
            False,
            'error':
            f'User {jwt_username} is not authorized to soft delete a notification sent to '
            f'{existing_notification.username}.'
        })
        response.status_code = 400
        return response

    # Update the notification model to reflect the soft delete
    existing_notification.deleted = True
    existing_notification.deleted_date = datetime.now()
    existing_notification.deleted_app = 'saints-xctf-api'
    existing_notification.modified_date = datetime.now()
    existing_notification.modified_app = 'saints-xctf-api'

    is_deleted: bool = NotificationDao.soft_delete_notification(
        existing_notification)

    if is_deleted:
        response = jsonify({
            'self': f'/v2/notifications/soft/{notification_id}',
            'deleted': True,
        })
        response.status_code = 204
        return response
    else:
        response = jsonify({
            'self': f'/v2/notifications/soft/{notification_id}',
            'deleted': False,
            'error': 'failed to soft delete the notification'
        })
        response.status_code = 500
        return response
Пример #24
0
def flair_post():
    """
    Endpoint for creating flair used on a users profile.
    :return: JSON with the resulting Flair object and relevant metadata.
    """
    flair_data: dict = request.get_json()

    if flair_data is None:
        response = jsonify({
            'self': f'/v2/flair',
            'added': False,
            'error': "the request body isn't populated"
        })
        response.status_code = 400
        return response

    username = flair_data.get('username')
    flair_content = flair_data.get('flair')

    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if username == jwt_username:
        current_app.logger.info(f'User {jwt_username} is creating a flair for their profile.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to create a flair for user {username}.'
        )
        response = jsonify({
            'self': f'/v2/flair',
            'added': False,
            'error': f'User {jwt_username} is not authorized to create a flair for user {username}.'
        })
        response.status_code = 400
        return response

    if username is None or flair_content is None:
        response = jsonify({
            'self': f'/v2/flair',
            'added': False,
            'error': "'username' and 'flair' are required fields"
        })
        response.status_code = 400
        return response

    flair = Flair({
        'username': username,
        'flair': flair_content
    })

    flair.created_date = datetime.now()
    flair.created_app = 'saints-xctf-api'
    flair.created_user = None
    flair.modified_date = None
    flair.modified_app = None
    flair.modified_user = None
    flair.deleted_date = None
    flair.deleted_app = None
    flair.deleted_user = None
    flair.deleted = False

    flair_added: bool = FlairDao.add_flair(flair)

    if flair_added:
        new_flair: Flair = FlairDao.get_flair_by_content(username, flair_content)
        new_flair_dict: dict = FlairData(new_flair).__dict__

        response = jsonify({
            'self': f'/v2/flair',
            'added': True,
            'flair': new_flair_dict
        })
        response.status_code = 201
        return response
    else:
        response = jsonify({
            'self': f'/v2/flair',
            'added': False,
            'flair': None,
            'error': 'the flair creation failed'
        })
        response.status_code = 500
        return response