"""Contract Lifecycle Schedule handlers.""" from abacus_common_logic.constants.error import ERROR_ENTITY_DOES_NOT_EXIST from abacus_common_logic.views.item_view import ItemView from abacus_common_logic.views.validations import validated_request_body from flask import Blueprint from flask import request from owsrequest import flask_request from owsresponse import response from owsresponse.adaptors.flask import flaskify from abacus_contract.constants.error import ERROR_ACCOUNT_NOT_FOUND_FOR_CONTRACT, \ ERROR_CODE_AUTHORIZATION, ERROR_CODE_NOT_FOUND, ERROR_INVALID_IDS from abacus_contract.logic import contract as contract_logic from abacus_contract.logic import contract_lifecycle_schedule as logic from abacus_contract.models.contract_lifecycle_schedule import \ ContractLifecycleSchedule from abacus_contract.schemas.contract_lifecycle_schedule import \ ContractLifecycleSchedulePostSchema from abacus_contract.schemas.contract_lifecycle_schedule import \ ContractLifecycleSchedulePutSchema from abacus_contract.schemas.contract_lifecycle_schedule import \ ContractLifecycleScheduleSchema from abacus_contract.utils.authorization import pdp_authorize_many_accounts from abacus_contract.utils.format_error import validation_error from abacus_contract.utils.request import get_optional_numeric_list_from_params contract_lifecycle_schedule_api = Blueprint( 'contract_lifecycle_schedule_api', __name__ ) class ContractLifecycleScheduleItemView(ItemView): """View for finding a contract lifecycle schedule by ID.""" model_class = ContractLifecycleSchedule object_detail_schema = ContractLifecycleScheduleSchema() def get(self, object_id): """Find object by its id or return an error.""" access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify(response.create_error_response( code=ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, )) obj = self.model_class.get_by_id(object_id) if not obj or (obj.deleted_at is not None or obj.deleted_by is not None): return flaskify(response.create_not_found_response( message=ERROR_ENTITY_DOES_NOT_EXIST.format( object_type='ContractLifecycleSchedule', object_id=object_id ) )) body = self.object_detail_schema.dump(obj) return flaskify(response.Response(message=body, status=200)) contract_lifecycle_schedule_api.add_url_rule( '/contract-lifecycle-schedule//', methods=['GET'], view_func=ContractLifecycleScheduleItemView.as_view('contract_lifecycle_schedule') ) @contract_lifecycle_schedule_api.route( '/contract//contract-lifecycle-schedules/', methods=['GET'] ) def get_contract_lifecycle_schedules_by_contract_id(contract_id: int): """GET a list of contract lifecycle schedules for a contract_id. Arg: contract_id(int): id of the contract """ access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: account_id = contract_logic.get_account_id_by_contract_id(contract_id) if not account_id: # Can't verify that user has access to something we can't find return flaskify(response.create_error_response( code=ERROR_CODE_NOT_FOUND, message=ERROR_ACCOUNT_NOT_FOUND_FOR_CONTRACT, status=404, )) else: authorized = pdp_authorize_many_accounts([account_id]) if not authorized: return flaskify(response.create_error_response( code=ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=403, )) return flaskify( logic.get_contract_lifecycle_schedules_by_contract_id(contract_id) ) @contract_lifecycle_schedule_api.route( '/contract-lifecycle-schedules/dataloader', methods=['POST'] ) def get_contract_lifecycle_schedules_by_ids(): """Get contract lifecycle schedules by list of ids. NOTE: This endpoint uses a POST method to allow for a larger list of query args. """ try: schedule_ids = get_optional_numeric_list_from_params() except ValueError: return flaskify( validation_error( ERROR_INVALID_IDS.format(object='ContractLifecycleSchedule') ) ) schedules_response = logic.get_contract_lifecycle_schedules_by_ids(schedule_ids) access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: contract_ids = [ s.get('data').get('contract_id') for s in schedules_response.message if s and s.get('data') ] accounts_ids = contract_logic.get_account_ids_by_contract_ids(contract_ids) authorized = pdp_authorize_many_accounts(accounts_ids) if not authorized: return flaskify(response.create_error_response( code=ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=403, )) return flaskify(schedules_response) @contract_lifecycle_schedule_api.route( '/contract//contract-lifecycle-schedules/', methods=['POST'] ) def create_contract_lifecycle_schedules(contract_id: int): """Create one or two contract lifecycle schedules along with an optional lifecycle. Args: contract_id (int): ID of the contract """ access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify(response.create_error_response( code=ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, )) post_request_body = validated_request_body( ContractLifecycleSchedulePostSchema(many=True) ) return flaskify( logic.create_contract_lifecycle_schedules( contract_id, post_request_body, allow_end_date_in_past=bool( request.args.get('allow_end_date_in_past', False)), ) ) @contract_lifecycle_schedule_api.route( '/contract//contract-lifecycle-schedules/', methods=['PUT'] ) def update_contract_lifecycle_schedules(contract_id: int): """Update contract lifecycle schedules. Args: contract_id (int): ID of the contract """ access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify(response.create_error_response( code=ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, )) put_request_body = validated_request_body( ContractLifecycleSchedulePutSchema(many=True) ) return flaskify( logic.update_contract_lifecycle_schedules( contract_id, put_request_body, allow_end_date_in_past=bool( request.args.get('allow_end_date_in_past', False)), ) )