from flask import Blueprint, request from zope import component from .utils import authz_restrict_to_syscom, create_streaming_response from ceo_common.errors import BadRequest from ceo_common.interfaces import ILDAPService, IConfig from ceod.transactions.members import UpdateMemberPositionsTransaction bp = Blueprint('positions', __name__) @bp.route('/', methods=['GET'], strict_slashes=False) def get_positions(): ldap_srv = component.getUtility(ILDAPService) positions = {} for user in ldap_srv.get_users_with_positions(): for position in user.positions: if position not in positions: positions[position] = [] positions[position].append(user.uid) return positions @bp.route('/', methods=['POST'], strict_slashes=False) @authz_restrict_to_syscom def update_positions(): cfg = component.getUtility(IConfig) body = request.get_json(force=True) required = cfg.get('positions_required') available = cfg.get('positions_available') # remove falsy values and parse multiple users in each position # Example: "user1,user2, user3" -> ["user1","user2","user3"] position_to_usernames = {} for position, usernames in body.items(): if not usernames: continue if type(usernames) is list: position_to_usernames[position] = usernames elif type(usernames) is str: position_to_usernames[position] = usernames.replace(' ', '').split(',') else: raise BadRequest('usernames must be a list or comma-separated string') # check for duplicates (i.e. one username specified twice in the same list) for usernames in position_to_usernames.values(): if len(usernames) != len(set(usernames)): raise BadRequest('username may only be specified at most once for a position') for position in position_to_usernames.keys(): if position not in available: raise BadRequest(f'unknown position: {position}') for position in required: if position not in position_to_usernames: raise BadRequest(f'missing required position: {position}') txn = UpdateMemberPositionsTransaction(position_to_usernames) return create_streaming_response(txn)