示例#1
0
    def patch(self, nr_id, action):
        """
        Roll back a Name Request to a usable state in case of a frontend error.
        :param nr_id:
        :param action:
        :return:
        """
        try:
            if not full_access_to_name_request(request):
                return {
                    "message": "You do not have access to this NameRequest."
                }, 403

            # Find the existing name request
            nr_model = Request.query.get(nr_id)

            # Creates a new NameRequestService, validates the app config, and sets request_data to the NameRequestService instance
            self.initialize()
            nr_svc = self.nr_service

            nr_svc.nr_num = nr_model.nrNum
            nr_svc.nr_id = nr_model.id

            # This could be moved out, but it's fine here for now
            def validate_patch_request(data):
                # TODO: Validate the data payload
                # Use the NR model state as the default, as the state change may not be included in the PATCH request
                is_valid = False
                msg = ''
                # This handles updates if the NR state is 'patchable'
                if NameRequestRollbackActions.has_value(action):
                    is_valid = True
                else:
                    msg = 'Invalid rollback action'

                return is_valid, msg

            is_valid_patch, validation_msg = validate_patch_request(
                self.request_data)
            validation_msg = validation_msg if not len(
                validation_msg) > 0 else 'Invalid request for PATCH'

            if not is_valid_patch:
                raise InvalidInputError(message=validation_msg)

            # This handles updates if the NR state is 'patchable'
            nr_model = self.handle_patch_rollback(nr_model, action)

            current_app.logger.debug(nr_model.json())
            response_data = nr_model.json()
            # Add the list of valid Name Request actions for the given state to the response
            response_data['actions'] = nr_svc.current_state_actions
            return jsonify(response_data), 200

        except NameRequestException as err:
            return handle_exception(err, err.message, 500)
        except Exception as err:
            return handle_exception(err, repr(err), 500)
示例#2
0
    def put(self, nr_id):
        """
        NOT used for Existing Name Request updates that only change the Name Request. Use 'patch' instead.
        State changes handled include state changes to [DRAFT, COND_RESERVE, RESERVED, COND_RESERVE to CONDITIONAL, RESERVED to APPROVED]
        :param nr_id:
        :return:
        """
        try:
            # Find the existing name request
            nr_model = Request.query.get(nr_id)

            # Creates a new NameRequestService, validates the app config, and sets request_data to the NameRequestService instance
            self.initialize()
            nr_svc = self.nr_service

            nr_svc.nr_num = nr_model.nrNum
            nr_svc.nr_id = nr_model.id

            valid_update_states = [
                State.DRAFT, State.COND_RESERVE, State.RESERVED,
                State.PENDING_PAYMENT
            ]

            # This could be moved out, but it's fine here for now
            def validate_put_request(data):
                is_valid = False
                msg = ''
                if data.get('stateCd') in valid_update_states:
                    is_valid = True

                return is_valid, msg

            is_valid_put, validation_msg = validate_put_request(
                self.request_data)
            validation_msg = validation_msg if not len(
                validation_msg) > 0 else 'Invalid request for PUT'

            if not is_valid_put:
                raise InvalidInputError(message=validation_msg)

            if nr_model.stateCd in valid_update_states:
                nr_model = self.update_nr(nr_model, nr_model.stateCd,
                                          self.handle_nr_update)

                # Record the event
                EventRecorder.record(nr_svc.user, Event.PUT, nr_model,
                                     nr_svc.request_data)

            current_app.logger.debug(nr_model.json())
            response_data = nr_model.json()
            # Add the list of valid Name Request actions for the given state to the response
            response_data['actions'] = nr_svc.current_state_actions
            return jsonify(response_data), 200
        except NameRequestException as err:
            return handle_exception(err, err.message, 500)
        except Exception as err:
            return handle_exception(err, repr(err), 500)
示例#3
0
    def patch(self, nr_id, nr_action: str):
        """
        Update a specific set of fields and/or a provided action. Fields excluded from the payload will not be updated.
        The following data format is expected when providing a data payload:
        { 'stateCd': 'CANCELLED' }  # Fields to update

        We use this to:
        - Edit a subset of NR fields
        - Cancel an NR
        - Change the state of an NR
        :param nr_id:
        :param nr_action: One of [CHECKOUT, CHECKIN, EDIT, CANCEL, RESEND]
        :return:
        """
        try:
            if not full_access_to_name_request(request):
                return {
                    "message": "You do not have access to this NameRequest."
                }, 403

            nr_action = str(nr_action).upper(
            )  # Convert to upper-case, just so we can support lower case action strings
            nr_action = NameRequestPatchActions[nr_action].value \
                if NameRequestPatchActions.has_value(nr_action) \
                else NameRequestPatchActions.EDIT.value

            # Find the existing name request
            nr_model = Request.query.get(nr_id)

            def initialize(_self):
                _self.validate_config(current_app)
                request_json = request.get_json()

                if nr_action:
                    _self.nr_action = nr_action

                if nr_action is NameRequestPatchActions.CHECKOUT.value:
                    # Make sure the NR isn't already checked out
                    checked_out_by_different_user = nr_model.checkedOutBy is not None and nr_model.checkedOutBy != request_json.get(
                        'checkedOutBy', None)
                    if checked_out_by_different_user:
                        raise NameRequestIsInProgressError()

                    # set the user id of the request to name_request_service_account
                    service_account_user = User.find_by_username(
                        'name_request_service_account')
                    nr_model.userId = service_account_user.id

                    # The request payload will be empty when making this call, add them to the request
                    _self.request_data = {
                        # Doesn't have to be a UUID but this is easy and works for a pretty unique token
                        'checkedOutBy': str(uuid4()),
                        'checkedOutDt': datetime.now()
                    }
                    # Set the request data to the service
                    _self.nr_service.request_data = self.request_data
                elif nr_action is NameRequestPatchActions.CHECKIN.value:
                    # The request payload will be empty when making this call, add them to the request
                    _self.request_data = {
                        'checkedOutBy': None,
                        'checkedOutDt': None
                    }
                    # Set the request data to the service
                    _self.nr_service.request_data = self.request_data
                else:
                    super().initialize()

            initialize(self)

            nr_svc = self.nr_service
            nr_svc.nr_num = nr_model.nrNum
            nr_svc.nr_id = nr_model.id

            # This could be moved out, but it's fine here for now
            def validate_patch_request(data):
                # Use the NR model state as the default, as the state change may not be included in the PATCH request
                request_state = data.get('stateCd', nr_model.stateCd)
                is_valid = False
                msg = ''

                # Handles updates if the NR state is 'patchable'
                if request_state in request_editable_states:
                    is_valid = True
                elif request_state in contact_editable_states:
                    is_valid = True
                else:
                    msg = 'Invalid state change requested - the Name Request state cannot be changed to [' + data.get(
                        'stateCd', '') + ']'

                # Check the action, make sure it's valid
                if not NameRequestPatchActions.has_value(nr_action):
                    is_valid = False
                    msg = 'Invalid Name Request PATCH action, please use one of [' + ', '.join(
                        [action.value
                         for action in NameRequestPatchActions]) + ']'
                return is_valid, msg

            is_valid_patch, validation_msg = validate_patch_request(
                self.request_data)
            validation_msg = validation_msg if not len(
                validation_msg) > 0 else 'Invalid request for PATCH'

            if not is_valid_patch:
                raise InvalidInputError(message=validation_msg)

            def handle_patch_actions(action, model):
                return {
                    NameRequestPatchActions.CHECKOUT.value:
                    self.handle_patch_checkout,
                    NameRequestPatchActions.CHECKIN.value:
                    self.handle_patch_checkin,
                    NameRequestPatchActions.EDIT.value:
                    self.handle_patch_edit,
                    NameRequestPatchActions.CANCEL.value:
                    self.handle_patch_cancel,
                    NameRequestPatchActions.RESEND.value:
                    self.handle_patch_resend,
                    NameRequestPatchActions.REQUEST_REFUND.value:
                    self.handle_patch_request_refund
                }.get(action)(model)

            # This handles updates if the NR state is 'patchable'
            nr_model = handle_patch_actions(nr_action, nr_model)

            current_app.logger.debug(nr_model.json())
            response_data = nr_model.json()

            # Don't return the whole response object if we're checking in or checking out
            if nr_action == NameRequestPatchActions.CHECKOUT.value:
                response_data = {
                    'id': nr_id,
                    'checkedOutBy': response_data.get('checkedOutBy'),
                    'checkedOutDt': response_data.get('checkedOutDt'),
                    'state': response_data.get('state', ''),
                    'stateCd': response_data.get('stateCd', ''),
                    'actions': nr_svc.current_state_actions
                }
                return jsonify(response_data), 200

            if nr_action == NameRequestPatchActions.CHECKIN.value:
                response_data = {
                    'id': nr_id,
                    'state': response_data.get('state', ''),
                    'stateCd': response_data.get('stateCd', ''),
                    'actions': nr_svc.current_state_actions
                }
                return jsonify(response_data), 200

            # Add the list of valid Name Request actions for the given state to the response
            if (nr_action == NameRequestPatchActions.REQUEST_REFUND.value):
                response_data['actions'] = []
            else:
                response_data['actions'] = nr_svc.current_state_actions
            return jsonify(response_data), 200

        except NameRequestIsInProgressError as err:
            # Might as well use the Mozilla WebDAV HTTP Locked status, it's pretty close
            return handle_exception(err, err.message, 423)
        except NameRequestException as err:
            return handle_exception(err, err.message, 500)
        except Exception as err:
            return handle_exception(err, repr(err), 500)
示例#4
0
    def patch(self, nr_id, nr_action):
        """
        Update a specific set of fields and/or a provided action. Fields excluded from the payload will not be updated.
        The following data format is expected when providing a data payload:
        { 'stateCd': 'CANCELLED' }  # Fields to update

        We use this to:
        - Edit a subset of NR fields
        - Cancel an NR
        - Change the state of an NR to [CANCELLED, INPROGRESS, HOLD, APPROVED, REJECTED
        - Apply the following actions to an NR [EDIT, UPGRADE, CANCEL, REFUND, REAPPLY, RESEND]
        :param nr_id:
        :param nr_action: One of [EDIT, UPGRADE, CANCEL, REFUND, REAPPLY, RESEND]
        :return:
        """
        try:
            # Find the existing name request
            nr_model = Request.query.get(nr_id)

            # Creates a new NameRequestService, validates the app config, and sets request_data to the NameRequestService instance
            self.initialize()
            nr_svc = self.nr_service

            nr_action = str(nr_action).upper(
            )  # Convert to upper-case, just so we can support lower case action strings
            nr_action = NameRequestActions[nr_action].value \
                if NameRequestActions.has_value(nr_action) \
                else NameRequestActions.EDIT.value

            nr_svc.nr_num = nr_model.nrNum
            nr_svc.nr_id = nr_model.id

            # This could be moved out, but it's fine here for now
            def validate_patch_request(data):
                # Use the NR model state as the default, as the state change may not be included in the PATCH request
                request_state = data.get('stateCd', nr_model.stateCd)
                is_valid = False
                msg = ''

                # Handles updates if the NR state is 'patchable'
                if request_state in request_editable_states:
                    is_valid = True
                elif request_state in contact_editable_states:
                    is_valid = True
                else:
                    msg = 'Invalid state change requested - the Name Request state cannot be changed to [' + data.get(
                        'stateCd', '') + ']'

                return is_valid, msg

            is_valid_patch, validation_msg = validate_patch_request(
                self.request_data)
            validation_msg = validation_msg if not len(
                validation_msg) > 0 else 'Invalid request for PATCH'

            if not is_valid_patch:
                raise InvalidInputError(message=validation_msg)

            def handle_patch_actions(action, model):
                return {
                    NameRequestActions.EDIT.value:
                    self.handle_patch_edit,
                    NameRequestActions.UPGRADE.value:
                    self.handle_patch_upgrade,
                    NameRequestActions.CANCEL.value:
                    self.handle_patch_cancel,
                    NameRequestActions.REFUND.value:
                    self.handle_patch_refund,
                    # TODO: This is a frontend only action throw an error!
                    # NameRequestActions.RECEIPT.value: self.patch_receipt,
                    NameRequestActions.REAPPLY.value:
                    self.handle_patch_reapply,
                    NameRequestActions.RESEND.value:
                    self.handle_patch_resend
                }.get(action)(model)

            # This handles updates if the NR state is 'patchable'
            nr_model = handle_patch_actions(nr_action, nr_model)

            current_app.logger.debug(nr_model.json())
            response_data = nr_model.json()
            # Add the list of valid Name Request actions for the given state to the response
            response_data['actions'] = nr_svc.current_state_actions
            return jsonify(response_data), 200

        except NameRequestException as err:
            return handle_exception(err, err.message, 500)
        except Exception as err:
            return handle_exception(err, repr(err), 500)
示例#5
0
    def get(self):
        try:
            filters = []

            # Validate the request
            if len(request.args) == 0:
                raise InvalidInputError(
                    message='No query parameters were specified in the request'
                )

            nr_num_query_str = get_query_param_str('nrNum')
            email_address_query_str = get_query_param_str('emailAddress')
            phone_number_query_str = get_query_param_str('phoneNumber')

            if not nr_num_query_str:
                raise InvalidInputError(message='An nrNum must be provided')
            else:
                if not email_address_query_str and not phone_number_query_str:
                    raise InvalidInputError(
                        message=
                        'Either an emailAddress or phoneNumber must be provided'
                    )

            # Continue
            nr_num = parse_nr_num(nr_num_query_str)
            email_address = email_address_query_str

            phone_number = get_query_param_str('phoneNumber')
            # Filter on addresses
            # address_line = get_query_param_str('addrLine1')

            if nr_num:
                filters.append(func.lower(Request.nrNum) == nr_num.lower())
            if phone_number:
                strip_phone_number_chars_regex = r"[^0-9]"
                filters.append(
                    Request.applicants.any(
                        func.regexp_replace(
                            Applicant.phoneNumber,
                            strip_phone_number_chars_regex, '', 'g').contains(
                                re.sub(strip_phone_number_chars_regex, '',
                                       phone_number))))

            if email_address:
                filters.append(
                    Request.applicants.any(
                        func.lower(Applicant.emailAddress).startswith(
                            email_address.lower())))
            '''
            Filter on addresses
            if address_line:
                filters.append(
                    Request.applicants.any(
                        func.lower(Applicant.addrLine1).startswith(address_line.lower())
                    )
                )
            '''

            criteria = RequestQueryCriteria(nr_num=nr_num, filters=filters)

            results = Request.find_by_criteria(criteria)

            if not results:
                results = []

        except InvalidInputError as err:
            return handle_exception(err, err.message, 400)
        except Exception as err:
            return handle_exception(err,
                                    'Error retrieving the NR from the db.',
                                    500)

        if nr_num and len(results) == 1:
            nr_model = results[0]

            if nr_model.requestTypeCd and (not nr_model.entity_type_cd
                                           or not nr_model.request_action_cd):
                # If requestTypeCd is set, but a request_entity (entity_type_cd) and a request_action (request_action_cd)
                # are not, use get_mapped_entity_and_action_code to map the values from the requestTypeCd
                entity_type, request_action = get_mapped_entity_and_action_code(
                    nr_model.requestTypeCd)
                nr_model.entity_type_cd = entity_type
                nr_model.request_action_cd = request_action

            response_data = nr_model.json()
            # Add the list of valid Name Request actions for the given state to the response
            response_data['actions'] = get_nr_state_actions(
                results[0].stateCd, results[0])
            return jsonify(response_data), 200
        elif len(results) > 0:
            # We won't add the list of valid Name Request actions for the given state to the response if we're sending back a list
            # If the user / client accessing this data needs the Name Request actions, GET the individual record using NameRequest.get
            # This method, NameRequests.get is for Existing NR Search
            return jsonify(list(map(lambda result: result.json(),
                                    results))), 200

        # We won't add the list of valid Name Request actions for the given state to the response if we're sending back a list
        # If the user / client accessing this data needs the Name Request actions, GET the individual record using NameRequest.get
        # This method, NameRequests.get is for Existing NR Search
        return jsonify(results), 200
示例#6
0
    def get(self):
        try:
            if not full_access_to_name_request(request):
                return {"message": "You do not have access to this NameRequest."}, 403

            filters = []

            nr_num_query_str = request.headers['Bcreg-Nr'] or request.headers['Bcreg-Nrl']
            email_address_query_str = request.headers['Bcreg-User-Email']
            phone_number_query_str = request.headers['Bcreg-User-Phone']

            if not nr_num_query_str:
                raise InvalidInputError(message='An nrNum must be provided')
            else:
                if not email_address_query_str and not phone_number_query_str:
                    raise InvalidInputError(message='Either an emailAddress or phoneNumber must be provided')

            # Continue
            nr_num = parse_nr_num(nr_num_query_str)
            email_address = email_address_query_str
            phone_number = phone_number_query_str
            # Filter on addresses
            # address_line = get_query_param_str('addrLine1')

            if nr_num:
                filters.append(func.lower(Request.nrNum) == nr_num.lower())
            if phone_number:
                strip_phone_number_chars_regex = r"[^0-9]"
                filters.append(
                    Request.applicants.any(
                        func.regexp_replace(Applicant.phoneNumber, strip_phone_number_chars_regex, '', 'g').contains(re.sub(strip_phone_number_chars_regex, '', phone_number))
                    )
                )

            if email_address:
                filters.append(
                    Request.applicants.any(
                        func.lower(Applicant.emailAddress).startswith(email_address.lower())
                    )
                )

            '''
            Filter on addresses
            if address_line:
                filters.append(
                    Request.applicants.any(
                        func.lower(Applicant.addrLine1).startswith(address_line.lower())
                    )
                )
            '''

            criteria = RequestQueryCriteria(
                nr_num=nr_num,
                filters=filters
            )

            results = Request.find_by_criteria(criteria)

            if not results:
                results = []

        except InvalidInputError as err:
            return handle_exception(err, err.message, 400)
        except Exception as err:
            return handle_exception(err, 'Error retrieving the NR from the db.', 500)

        if nr_num and len(results) == 1:
            nr_model = results[0]

            if nr_model.requestTypeCd and (not nr_model.entity_type_cd or not nr_model.request_action_cd):
                # If requestTypeCd is set, but a request_entity (entity_type_cd) and a request_action (request_action_cd)
                # are not, use get_mapped_entity_and_action_code to map the values from the requestTypeCd
                entity_type, request_action = get_mapped_entity_and_action_code(nr_model.requestTypeCd)
                nr_model.entity_type_cd = entity_type
                nr_model.request_action_cd = request_action

            response_data = nr_model.json()

            # If draft, get the wait time and oldest queued request
            if nr_model.stateCd == 'DRAFT':
                service = WaitTimeStatsService()
                wait_time_response = service.get_waiting_time_dict()
                response_data.update(wait_time_response)

            # Add the list of valid Name Request actions for the given state to the response
            response_data['actions'] = get_nr_state_actions(results[0].stateCd, results[0])
            return jsonify(response_data), 200
        elif len(results) > 0:
            # We won't add the list of valid Name Request actions for the given state to the response if we're sending back a list
            # If the user / client accessing this data needs the Name Request actions, GET the individual record using NameRequest.get
            # This method, NameRequests.get is for Existing NR Search
            return jsonify(list(map(lambda result: result.json(), results))), 200

        # We won't add the list of valid Name Request actions for the given state to the response if we're sending back a list
        # If the user / client accessing this data needs the Name Request actions, GET the individual record using NameRequest.get
        # This method, NameRequests.get is for Existing NR Search
        return jsonify(results), 200
示例#7
0
def parse_nr_num(nr_num_str):
    nr_num = normalize_nr_num(nr_num_str) if nr_num_str else None
    if nr_num_str and not nr_num:
        raise InvalidInputError(message='Invalid NR number format provided')

    return nr_num