def test_expired_event_history(client, jwt, app): from namex.models import Request as RequestDAO, State, Name as NameDAO, User, Event from namex.services import EventRecorder # add a user for the comment user = User('test-user', '', '', '43e6a245-0bf7-4ccf-9bd0-e7fb85fd18cc', 'https://sso-dev.pathfinder.gov.bc.ca/auth/realms/sbc') user.save_to_db() # create JWT & setup header with a Bearer Token using the JWT token = jwt.create_jwt(claims, token_header) headers = { 'Authorization': 'Bearer ' + token, 'content-type': 'application/json' } nr = RequestDAO() nr.nrNum = 'NR 0000002' nr.stateCd = State.EXPIRED nr.requestId = 1460775 name1 = NameDAO() name1.choice = 1 name1.name = 'TEST NAME ONE' nr.names = [name1] nr.save_to_db() EventRecorder.record(user, Event.POST, nr, {}) # get the resource (this is the test) rv = client.get('/api/v1/events/NR%200000002', headers=headers) assert rv.status_code == 200 assert b'"user_action": "Expired by NRO"' in rv.data
def handle_patch_rollback(self, nr_model: Request, action: str): """ Roll back the Name Request. :param nr_model: :param action: :return: """ nr_svc = self.nr_service # This handles updates if the NR state is 'patchable' nr_model = self.update_nr(nr_model, State.CANCELLED, self.handle_nr_patch) # Only update the record in NRO if it's a real NR, otherwise the record won't exist if not is_temp_nr_num(nr_model.nrNum): # This handles the updates for NRO and Solr, if necessary # self.update_records_in_network_services(nr_model, update_solr=True) nr_model = self.update_request_in_nro(nr_model, self.save_nr) # Delete in solr for temp or real NR because it is cancelled if nr_model.entity_type_cd in [ 'CR', 'UL', 'BC', 'CP', 'PA', 'XCR', 'XUL', 'XCP', 'CC', 'FI', 'XCR', 'XUL', 'XCP' ]: SOLR_CORE = 'possible.conflicts' self.delete_solr_doc(SOLR_CORE, nr_model.nrNum) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH, nr_model, nr_svc.request_data) return nr_model
def post(self): try: # Creates a new NameRequestService, validates the app config, and sets the request data to the NameRequestService instance self.initialize() nr_svc = self.nr_service # Create a new DRAFT name request nr_model = nr_svc.create_name_request() # Handle state changes # Use update_nr as it enforces the State change pattern # Transition the DRAFT to the state specified in the request: # eg. one of [State.DRAFT, State.COND_RESERVE, State.RESERVED] nr_model = self.update_nr(nr_model, nr_svc.request_state_code, self.handle_nr_create) # Record the event EventRecorder.record(nr_svc.user, Event.POST, nr_model, nr_svc.request_data) # Update Solr - note that we don't save DRAFT name requests to Solr for corp entities only if nr_model.stateCd in [State.COND_RESERVE, State.RESERVED] and \ nr_model.entity_type_cd in \ ['CR', 'UL', 'BC', 'CP', 'PA', 'XCR', 'XUL', 'XCP', 'CC', 'FI', 'XCR', 'XUL', 'XCP']: self.create_solr_nr_doc(SOLR_CORE, 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), 201 except NameRequestException as err: return handle_exception(err, err.message, 500) except Exception as err: return handle_exception(err, repr(err), 500)
def handle_patch_request_refund(self, nr_model: Request): """ Can the NR and request a refund for ALL associated Name Request payments. :param nr_model: :return: """ nr_svc = self.nr_service # This handles updates if the NR state is 'patchable' nr_model = self.update_nr(nr_model, State.REFUND_REQUESTED, self.handle_nr_patch) # Handle the payments valid_states = [ PaymentState.COMPLETED.value, PaymentState.PARTIAL.value ] # Cancel any payments associated with the NR for payment in nr_model.payments.all(): if payment.payment_status_code in valid_states: # refund_payment(payment.payment_token, {'reason': 'Name Request user requested refund'}) refund_payment(payment.payment_token, {}) payment.payment_status_code = PaymentState.REFUND_REQUESTED.value payment.save_to_db() # This handles the updates for NRO and Solr, if necessary nr_model = self.update_records_in_network_services(nr_model, update_solr=True) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [request-refund]', nr_model, nr_svc.request_data) return nr_model
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)
def complete_reservation_payment(self, nr_model: RequestDAO, payment_id: int): """ Invoked when completing an in-progress Name Request reservation. :param nr_model: :param payment_id: :return: """ nr_svc = self.nr_service # Update the state of the payment payment = get_active_payment(nr_model, payment_id) sbc_payment_response = get_payment(payment.payment_token) # TODO: Throw errors if this fails! if sbc_payment_response.statusCode in [ PaymentStatusCode.COMPLETED.value, PaymentStatusCode.APPROVED.value ]: payment.payment_status_code = sbc_payment_response.statusCode payment.payment_completion_date = sbc_payment_response.createdOn payment.save_to_db() # This handles updates if the NR state is DRAFT, COND_RESERVE or RESERVED # If the state is COND_RESERVE update state to CONDITIONAL # If the state is RESERVED update state to APPROVED # Then update the name request as required if nr_model.stateCd == State.DRAFT: # If the state is DRAFT, leave it as a DRAFT nr_model = self.update_nr(nr_model, State.DRAFT, self.handle_nr_approve) if nr_model.stateCd == State.COND_RESERVE: # If the state is COND_RESERVE update state to CONDITIONAL, and update the name request as required nr_model = self.update_nr(nr_model, State.CONDITIONAL, self.handle_nr_approve) elif nr_model.stateCd == State.RESERVED: # If the state is RESERVED update state to APPROVED, and update the name request as required nr_model = self.update_nr(nr_model, State.APPROVED, self.handle_nr_approve) # Save the name request nr_model.save_to_db() # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [payment completed] RESERVE', nr_model, nr_model.json()) # Update the actions, as things change once the payment is successful self.nr_service.current_state_actions = get_nr_state_actions( nr_model.stateCd, nr_model) return nr_model
def complete_reapply_payment(self, nr_model: RequestDAO, payment_id: int): """ Invoked when re-applying for an existing Name Request reservation. Extend the Name Request's expiration date by 56 days. If the request action is set to REH, REN or REST, OR request type is 'RCR', 'RUL', 'BERE', 'RCC', 'RCP', 'RFI', 'XRCR', 'RLC', 'XRCP','RSO','XRSO' extend the expiration by an additional year (plus the default 56 days). :param nr_model: :param payment_id: :return: """ nr_svc = self.nr_service # Update the state of the payment payment = get_active_payment(nr_model, payment_id) sbc_payment_response = get_payment(payment.payment_token) # TODO: Throw errors if this fails! if sbc_payment_response.statusCode in [ PaymentStatusCode.COMPLETED.value, PaymentStatusCode.APPROVED.value ]: payment.payment_status_code = sbc_payment_response.statusCode payment.payment_completion_date = sbc_payment_response.createdOn payment.save_to_db() if nr_model.submitCount < 3: nr_model = nr_svc.extend_expiry_date(nr_model, datetime.utcnow()) nr_model = nr_svc.update_request_submit_count(nr_model) nr_model.save_to_db() else: # TODO: Make a custom exception for this? raise PaymentServiceError( message= 'Submit count maximum of 3 retries has been reached!') # This (optionally) handles the updates for NRO and Solr, if necessary update_solr = False nr_model = self.update_records_in_network_services( nr_model, update_solr) # Update the actions, as things change once the payment is successful self.nr_service.current_state_actions = get_nr_state_actions( nr_model.stateCd, nr_model) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [payment completed] REAPPLY', nr_model, nr_model.json()) return nr_model
def test_event_create_nrl(client, jwt, app): #add a user for the comment user = User('test-user', '', '', '43e6a245-0bf7-4ccf-9bd0-e7fb85fd18cc', 'url') user.save_to_db() # create JWT & setup header with a Bearer Token using the JWT headers = create_header(jwt, [User.EDITOR]) nr = create_base_nr() before_record_date = datetime.utcnow() EventRecorder.record(user, Event.POST, nr, nr.json()) # get the resource (this is the test) rv = client.get('/api/v1/events/NR%200000002', headers=headers) assert rv.status_code == 200 assert rv.data response = json.loads(rv.data) assert response['transactions'] assert len(response['transactions']) == 1 assert response['transactions'][0]['additionalInfo'] == 'test' assert response['transactions'][0]['consent_dt'] == None assert response['transactions'][0]['consentFlag'] == None assert response['transactions'][0][ 'eventDate'] > before_record_date.isoformat() assert response['transactions'][0]['expirationDate'] == None assert response['transactions'][0]['names'] == [{ 'choice': 1, 'comment': None, 'conflict1': '', 'conflict1_num': '', 'conflict2': '', 'conflict2_num': '', 'conflict3': '', 'conflict3_num': '', 'consumptionDate': None, 'corpNum': None, 'decision_text': '', 'designation': None, 'id': 1, 'name': 'TEST NAME ONE', 'name_type_cd': None, 'state': 'NE' }] assert response['transactions'][0]['priorityCd'] == None assert response['transactions'][0]['requestTypeCd'] == 'CR' assert response['transactions'][0]['request_action_cd'] == 'NEW' assert response['transactions'][0]['stateCd'] == State.PENDING_PAYMENT assert response['transactions'][0]['user_action'] == 'Created NRL' assert response['transactions'][0]['user_name'] == 'test-user'
def handle_patch_resend(self, nr_model: Request): nr_svc = self.nr_service # This handles updates if the NR state is 'patchable' nr_model = self.update_nr(nr_model, nr_model.stateCd, self.handle_nr_patch) # This handles the updates for NRO and Solr, if necessary nr_model = self.update_records_in_network_services(nr_model, update_solr=False) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [re-send]', nr_model, nr_svc.request_data) return nr_model
def update_nr(self, nr_model): nr_svc = self.nr_service # Use apply_state_change to change state, as it enforces the State change pattern # apply_state_change takes the model, updates it to the specified state, and executes the callback handler nr_model = nr_svc.apply_state_change(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) return nr_model
def handle_patch_request_refund(self, nr_model: Request): """ Can the NR and request a refund for ALL associated Name Request payments. :param nr_model: :return: """ nr_svc = self.nr_service # This handles updates if the NR state is 'patchable' nr_model = self.update_nr(nr_model, State.REFUND_REQUESTED, self.handle_nr_patch) # Handle the payments valid_states = [ PaymentState.APPROVED.value, PaymentState.COMPLETED.value, PaymentState.PARTIAL.value ] refund_value = 0 # Check for NR that has been renewed - do not refund any payments. # UI should not order refund for an NR renewed/reapplied it. if not any( payment.payment_action == Payment.PaymentActions.REAPPLY.value for payment in nr_model.payments.all()): # Try to refund all payments associated with the NR for payment in nr_model.payments.all(): if payment.payment_status_code in valid_states: # Some refunds may fail. Some payment methods are not refundable and return HTTP 400 at the refund. # The refund status is checked from the payment_response and a appropriate message is displayed by the UI. refund_payment(payment.payment_token, {}) payment_response = get_payment(payment.payment_token) payment.payment_status_code = PaymentState.REFUND_REQUESTED.value payment.save_to_db() refund_value += payment_response.receipts[0][ 'receiptAmount'] if len( payment_response.receipts) else 0 publish_email_notification(nr_model.nrNum, 'refund', '{:.2f}'.format(refund_value)) # This handles the updates for NRO and Solr, if necessary nr_model = self.update_records_in_network_services(nr_model, update_solr=True) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [request-refund]', nr_model, nr_model.json()) return nr_model
def handle_patch_checkout(self, nr_model: Request): nr_svc = self.nr_service # This handles updates if the NR state is 'patchable' nr_model = self.update_nr(nr_model, State.INPROGRESS, self.handle_nr_patch) # Lock nro Request row (set status=H) nro_warnings = self.lock_request_in_nro(nr_model) if nro_warnings: on_success = False return self.on_nro_update_complete(nr_model, on_success, nro_warnings) EventRecorder.record(nr_svc.user, Event.PATCH + ' [checkout]', nr_model, {}) return nr_model
def complete_upgrade_payment(self, nr_model: RequestDAO, payment_id: int): """ Invoked when upgrading an existing Name Request reservation to PRIORITY status. :param nr_model: :param payment_id: :return: """ nr_svc = self.nr_service if nr_model.stateCd not in [State.DRAFT, State.PENDING_PAYMENT]: raise PaymentServiceError( message= 'Error upgrading Name Request, request is in an invalid state!' ) # Update the state of the payment payment = get_active_payment(nr_model, payment_id) sbc_payment_response = get_payment(payment.payment_token) # TODO: Throw errors if this fails! if sbc_payment_response.statusCode in [ PaymentStatusCode.COMPLETED.value, PaymentStatusCode.APPROVED.value ]: payment.payment_status_code = sbc_payment_response.statusCode payment.payment_completion_date = sbc_payment_response.createdOn payment.save_to_db() nr_model.priorityCd = 'Y' nr_model.priorityDate = datetime.utcnow() # Save the name request nr_model.save_to_db() # This (optionally) handles the updates for NRO and Solr, if necessary update_solr = False nr_model = self.update_records_in_network_services( nr_model, update_solr) # Update the actions, as things change once the payment is successful self.nr_service.current_state_actions = get_nr_state_actions( nr_model.stateCd, nr_model) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [payment completed] UPGRADE', nr_model, nr_model.json()) return nr_model
def handle_patch_checkin(self, nr_model: Request): nr_svc = self.nr_service # This handles updates if the NR state is 'patchable' nr_model = self.update_nr(nr_model, State.DRAFT, self.handle_nr_patch) # Set status back to D after edit is complete nro_warnings = self.unlock_request_in_nro(nr_model) if nro_warnings: on_success = False return self.on_nro_update_complete(nr_model, on_success, nro_warnings) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [checkin]', nr_model, {}) return nr_model
def handle_patch_edit(self, nr_model): nr_svc = self.nr_service # This handles updates if the NR state is 'patchable' nr_model = self.update_nr_fields(nr_model, nr_model.stateCd) # This handles the updates for NRO and Solr, if necessary update_solr = False nr_model = self.update_records_in_network_services( nr_model, update_solr) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [edit]', nr_model, nr_svc.request_data) return nr_model
def handle_patch_cancel(self, nr_model: Request): """ Cancel the Name Request. :param nr_model: :return: """ nr_svc = self.nr_service # This handles updates if the NR state is 'patchable' nr_model = self.update_nr(nr_model, State.CANCELLED, self.handle_nr_patch) # This handles the updates for NRO and Solr, if necessary nr_model = self.update_records_in_network_services(nr_model, update_solr=True) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [cancel]', nr_model, nr_svc.request_data) return nr_model
def delete(self, nr_id, payment_id): try: # Find the existing name request nr_model = RequestDAO.query.get(nr_id) if not nr_model: return jsonify(message=f'No NR found with id: {nr_id}.'), 404 # Find the existing payment record payment = PaymentDAO.find_by_payment_token(payment_id) if not payment: return jsonify( message=f'No payment record with id: {payment_id}.'), 404 # check payment record state is CREATED current_payment_state = payment.payment_status_code if current_payment_state != PaymentStatusCode.CREATED.value: return jsonify( message= f'Unable to cancel a payment record in {current_payment_state} state.' ), 400 try: # cancelling may change with refactor cancel_payment(payment.payment_token) payment.payment_status_code = PaymentState.CANCELLED.value payment.save_to_db() nr_svc = self.nr_service EventRecorder.record( nr_svc.user, Event.DELETE + f' [payment cancelled] {payment.payment_action}', nr_model, 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'] = get_nr_state_actions( nr_model.stateCd, nr_model) return jsonify(response_data), 200 except PaymentServiceError as err: # should only get here if there was a conflict (payment went through before cancel happened) return handle_exception(err, err.message, 409) except Exception as err: return handle_exception(err, repr(err), 500)
def test_get_inprogress_event_history(client, jwt, app): # add a user for the comment user = User('test-user', '', '', '43e6a245-0bf7-4ccf-9bd0-e7fb85fd18cc', 'url') user.save_to_db() # create JWT & setup header with a Bearer Token using the JWT headers = create_header(jwt, [User.EDITOR]) nr = create_base_nr() nr.stateCd = State.DRAFT nr.save_to_db() EventRecorder.record(user, Event.POST + ' [payment completed] CREATE', nr, nr.json()) nr.stateCd = State.INPROGRESS nr.save_to_db() EventRecorder.record(user, Event.PATCH, nr, { 'state': 'INPROGRESS' }) # get the resource (this is the test) rv = client.get('/api/v1/events/NR%200000002', headers=headers) assert rv.status_code == 200 assert b'"user_action": "Load NR"' in rv.data
def test_decision_event_history(client, jwt, app): from namex.models import Request as RequestDAO, State, Name as NameDAO, User, Event from namex.services import EventRecorder # add a user for the comment user = User('test-user', '', '', '43e6a245-0bf7-4ccf-9bd0-e7fb85fd18cc', 'https://sso-dev.pathfinder.gov.bc.ca/auth/realms/sbc') user.save_to_db() headers = create_header(jwt, [User.EDITOR]) nr = create_base_nr() nr.stateCd = State.REJECTED nr.save_to_db() EventRecorder.record(user, Event.PATCH, nr, {'state': 'REJECTED'}) # get the resource (this is the test) rv = client.get('/api/v1/events/NR%200000002', headers=headers) assert rv.status_code == 200 assert b'"user_action": "Decision"' in rv.data
def test_edit_event_history(client, jwt, app): from namex.models import Request as RequestDAO, State, Name as NameDAO, User, Event from namex.services import EventRecorder # add a user for the comment user = User('test-user', '', '', '43e6a245-0bf7-4ccf-9bd0-e7fb85fd18cc', 'https://sso-dev.pathfinder.gov.bc.ca/auth/realms/sbc') user.save_to_db() headers = create_header(jwt, [User.EDITOR]) nr = create_base_nr() nr.stateCd = State.INPROGRESS nr.additionalInfo = 'additional' nr.save_to_db() EventRecorder.record(user, Event.PUT, nr, nr.json()) # get the resource (this is the test) rv = client.get('/api/v1/events/NR%200000002', headers=headers) assert rv.status_code == 200 assert b'"user_action": "Edit NR Details (NameX)"' in rv.data
def test_consumed_event_history(client, jwt, app): from namex.models import State, User, Event from namex.services import EventRecorder # add a user for the comment user = User('nro_service_account', '', '', '8ca7d47a-024e-4c85-a367-57c9c93de1cd', 'https://sso-dev.pathfinder.gov.bc.ca/auth/realms/sbc') user.save_to_db() headers = create_header(jwt, [User.EDITOR]) nr = create_base_nr() nr.stateCd = State.CONSUMED nr.save_to_db() EventRecorder.record_as_system(Event.UPDATE_FROM_NRO, nr, nr.json()) # get the resource (this is the test) rv = client.get('/api/v1/events/NR%200000002', headers=headers) assert rv.status_code == 200 assert b'"user_action": "Get NR Details from NRO"' in rv.data
def job(user: User, max_rows: int, delay: int) -> (int, bool): row_count = 0 try: reqs = db.session.query(Request). \ filter(Request.stateCd == State.INPROGRESS). \ filter(Request.lastUpdate <= text('(now() at time zone \'utc\') - INTERVAL \'{delay} SECONDS\''.format(delay=delay))). \ order_by(Request.lastUpdate.asc()). \ limit(max_rows). \ with_for_update().all() for r in reqs: row_count += 1 current_app.logger.debug('processing: {}'.format(r.nrNum)) print ('nr {}, state: {} last_update:{}'.format(r.nrNum, r.stateCd, r.lastUpdate)) # if this NR was previously in DRAFT, reset it to that state (ie: the user walked away from an open edit window) if r.previousStateCd == State.DRAFT: r.stateCd = State.DRAFT r.previousStateCd = None # otherwise put it on hold else: r.stateCd = State.HOLD db.session.add(r) EventRecorder.record(user, Event.MARKED_ON_HOLD, r, {}, save_to_session=True) db.session.commit() return row_count, True except Exception as err: current_app.logger.error(err) db.session.rollback() return -1, False
def cancel_payment(self, nr_model: RequestDAO, payment_id: int): # Cancel payment with specified id. valid_states = [PaymentState.CREATED.value] for payment in nr_model.payments.all(): if payment.id == payment_id and payment.payment_status_code in valid_states: sbc_payment_response = get_payment(payment.payment_token) if sbc_payment_response.statusCode in [ PaymentStatusCode.COMPLETED.value, PaymentStatusCode.APPROVED.value ]: raise PaymentServiceError( message= 'Error cancelling payment. Payment is in a completed state!' ) cancel_payment(payment.payment_token) payment.payment_status_code = PaymentState.CANCELLED.value payment.save_to_db() # record the event nr_svc = self.nr_service EventRecorder.record( nr_svc.user, Event.DELETE + f' [payment cancelled] {payment.payment_action}', nr_model, nr_model.json()) return nr_model
def handle_patch_rollback(self, nr_model, action): """ Roll back the Name Request. :param nr_model: :param action: :return: """ nr_svc = self.nr_service # This handles updates if the NR state is 'patchable' nr_model = self.update_nr_fields(nr_model, State.CANCELLED) # Only update the record in NRO if it's a real NR, otherwise the record won't exist if not is_temp_nr_num(nr_model.nrNum): # This handles the updates for NRO and Solr, if necessary self.update_records_in_network_services(nr_model, update_solr=True) else: temp_nr_num = nr_model.nrNum # Update SOLR self.update_solr_service(nr_model, temp_nr_num) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH, nr_model, nr_svc.request_data) return nr_model
def test_edit_inprogress_event_history(client, jwt, app): from namex.models import Request as RequestDAO, State, Name as NameDAO, User, Event from namex.services import EventRecorder # add a user for the comment user = User('test-user', '', '', '43e6a245-0bf7-4ccf-9bd0-e7fb85fd18cc', 'https://sso-dev.pathfinder.gov.bc.ca/auth/realms/sbc') user.save_to_db() headers = create_header(jwt, [User.EDITOR]) nr = RequestDAO() nr.nrNum = 'NR 0000002' nr.stateCd = State.DRAFT nr.requestId = 1460775 nr._source = 'NRO' name1 = NameDAO() name1.choice = 1 name1.name = 'TEST NAME ONE' nr.names = [name1] nr.save_to_db() EventRecorder.record(user, Event.POST, nr, nr.json()) nr.stateCd = State.INPROGRESS nr.save_to_db() EventRecorder.record(user, Event.PUT, nr, { "additional": "additional", "furnished": "N" }) # get the resource (this is the test) rv = client.get('/api/v1/events/NR%200000002', headers=headers) assert rv.status_code == 200 assert b'"user_action": "Edit NR Details (NameX)"' in rv.data
def job(app, namex_db, nro_connection, user, max_rows=100): """Process the NRs that have been updated in the NamesDB. Most updates will go away as NRO (the legacy UI for the NamesDB) is decommissioned. The following states allow the following changes: - all changes allowed: DRAFT, PENDING_PAYMENT - no changes allowed: INPROGRESS, REFUND_REQUESTED, REJECTED, EXPIRED, HISTORICAL, COMPLETED - set cancelled state: CANCELLED - all changes, except for state: HOLD - consumed info only: RESERVED, COND_RESERVE, APPROVED, CONDITIONAL """ row_count = 0 try: ora_con = nro_connection # get the NRs from Oracle NamesDB of interest result, col_names = job_result_set(ora_con, max_rows) for r in result: row_count += 1 row = ora_row_to_dict(col_names, r) nr_num = row['nr_num'] nr = Request.find_by_nr(nr_num) action = row['action'] current_app.logger.debug( 'processing: {}, NameX state: {}, action: {}'.format( nr_num, None if (not nr) else nr.stateCd, action)) # NO CHANGES ALLOWED if nr and (nr.stateCd in [ State.INPROGRESS, State.REFUND_REQUESTED, State.REJECTED, State.EXPIRED, State.HISTORICAL, State.COMPLETED ]): success = update_feeder_row( ora_con, row_id=row['id'], status='C', send_count=1 + 0 if (row['send_count'] is None) else row['send_count'], error_message='Ignored - Request: not processed') ora_con.commit() # continue to next row current_app.logger.info( 'skipping: {}, NameX state: {}, action: {}'.format( nr_num, None if (not nr) else nr.stateCd, action)) continue # ignore existing NRs not in a completed state or draft, or in a completed state and not furnished if nr and ( nr.stateCd not in State.COMPLETED_STATE + [State.DRAFT] or (nr.stateCd in State.COMPLETED_STATE and nr.furnished == 'N')): success = update_feeder_row( ora_con, row_id=row['id'], status='C', send_count=1 + 0 if (row['send_count'] is None) else row['send_count'], error_message='Ignored - Request: not processed') ora_con.commit() continue # for any NRs in a completed state or new NRs not existing in NameX else: # pylint: disable=R1724: Unnecessary "else" try: # get submitter ora_cursor = ora_con.cursor() nr_header = get_nr_header(ora_cursor, nr_num) nr_submitter = get_nr_submitter(ora_cursor, nr_header['request_id']) # get pending payments pending_payments = [] if nr: pending_payments = [ x for x in nr.payments.all() if x.payment_status_code == PaymentStatusCode.CREATED.value ] # ignore if: # - NR does not exist and NR originated in namex (handles racetime condition for when it is still in the process of saving) # - NR has a pending update from namex (pending payment) if (not nr and nr_submitter and nr_submitter.get('submitter', '') == 'namex') or (nr and len(pending_payments) > 0): success = update_feeder_row( ora_con, row_id=row['id'], status='C', send_count=1 + 0 if (row['send_count'] is None) else row['send_count'], error_message='Ignored - Request: not processed') ora_con.commit() else: nr = nro.fetch_nro_request_and_copy_to_namex_request( user, nr_number=nr_num, name_request=nr) namex_db.session.add(nr) EventRecorder.record(user, Event.UPDATE_FROM_NRO, nr, nr.json(), save_to_session=True) current_app.logger.debug( 'EventRecorder should have been saved to by now, although not committed' ) success = update_feeder_row( ora_con, row_id=row['id'], status='C', send_count=1 + 0 if (row['send_count'] is None) else row['send_count'], error_message=None) if success: ora_con.commit() current_app.logger.debug('Oracle commit done') namex_db.session.commit() current_app.logger.debug('Postgresql commit done') else: raise Exception() except Exception as err: current_app.logger.error(err.with_traceback(None)) success = update_feeder_row( ora_con, row_id=row['id'], status=row['status'], send_count=1 + 0 if (row['send_count'] is None) else row['send_count'], error_message=err.with_traceback(None)) namex_db.session.rollback() ora_con.commit() return row_count except Exception as err: current_app.logger.error('Update Failed:', err.with_traceback(None)) return -1
def move_control_of_request_from_nro(self, nr, user, closed_nr=False): """ HIGHLY DESTRUCTIVE CALL This will move the loci of control of a request from NRO to NameX In doing so it'll update the NameX record if it is out of sync with the NRO info with the CURRENT NRO information OVER-WRITING the NameX info It will set the NameX lastUpdate to NOW SAFETY checks: This WON'T do anything if the NRO record is not in Draft This WON'T do anything if the NameX record is not in Draft This WON'T do anything if the NameX lastUpdate is newer than the NameX.nroLastUpdate field :param nr: :param user: :param closed_nr: boolean :return: """ warnings = [] if not (nr and user): warnings.append({ 'type': 'warn', 'code': 'unable_to_move_control_from_nro', 'message': 'NameRequest and User must be provided to attempt to move control from NRO.' }) return warnings # save the current state, as we'll need to set it back to this before returning nr_saved_state = nr.stateCd # get the last modification timestamp before we alter the record try: nro_last_ts = self.get_last_update_timestamp(nr.requestId) except (NROServicesError, Exception) as err: nro_last_ts = None warnings.append({ 'type': 'warn', 'code': 'unable_to_get_last_nro_ts', 'message': 'Unable to get last time the NR was updated in NRO' }) if not closed_nr: current_app.logger.debug('set state to h') try: self.set_request_status_to_h(nr.nrNum, user.username) except (NROServicesError, Exception) as err: warnings.append({ 'type': 'warn', 'code': 'unable_to_set_NR_status_in_NRO_to_H', 'message': 'Unable to set the NR in NRO to HOLD. ' 'Please contact support to alert them of this issue' ' and provide the Request #.' }) current_app.logger.debug('get state') try: nro_req_state = self.get_current_request_state(nr.nrNum) except (NROServicesError, Exception) as err: nro_req_state = None if nro_req_state is not 'H': warnings.append({ 'type': 'warn', 'code': 'unable_to_verify_NR_state_of_H', 'message': 'Unable to get the current state of the NRO Request' }) current_app.logger.debug( 'nro state not set to H, nro-package call must have silently failed - ugh' ) current_app.logger.debug('update records') current_app.logger.debug('nro_last_ts: {}'.format(nro_last_ts)) current_app.logger.debug('nr.nroLastUpdate: {}'.format( nr.nroLastUpdate)) if 'nro_last_ts' in locals() and nro_last_ts != nr.nroLastUpdate: current_app.logger.debug( 'nro updated since namex was last updated') try: # mark the NR as being updated nr.stateCd = State.NRO_UPDATING nr.save_to_db() nrf = self.fetch_nro_request_and_copy_to_namex_request( user, nr_number=nr.nrNum, name_request=nr) if nrf: nr = nrf nr.stateCd = nr_saved_state nr.save_to_db() EventRecorder.record(user, Event.UPDATE_FROM_NRO, nr, {}) except Exception as missed_error: warnings.append({ 'type': 'warn', 'code': 'unable_to_update_request_from_NRO', 'message': 'Unable to update the Request from the NRO system,' ' please manually verify record is up to date before' ' approving/rejecting.' }) current_app.logger.error(missed_error.with_traceback(None)) finally: # set the NR back to its initial state nr.stateCd = nr_saved_state nr.save_to_db() return warnings if len(warnings) > 0 else None
elif payment_action == PaymentDAO.PaymentActions.REAPPLY.value: # TODO: handle this (refund payment and prevent action?) if nr_model.stateCd != State.APPROVED \ and nr_model.expirationDate + timedelta(hours=NAME_REQUEST_EXTENSION_PAD_HOURS) < datetime.utcnow(): msg = f'Extend NR for payment.id={payment.id} nr_model.state{nr_model.stateCd}, nr_model.expires:{nr_model.expirationDate}' current_app.logger.debug(msg) nr_model.expirationDate = nr_model.expirationDate + timedelta( days=NAME_REQUEST_LIFESPAN_DAYS) payment.payment_completion_date = datetime.utcnow() nr_model.save_to_db() payment.save_to_db() EventRecorder.record( nr_svc.user, Event.POST + f' [payment completed { payment_action }]', nr_model, nr_model.json()) if payment_action in [ payment.PaymentActions.UPGRADE.value, payment.PaymentActions.REAPPLY.value ]: change_flags = { 'is_changed__request': True, 'is_changed__previous_request': False, 'is_changed__applicant': False, 'is_changed__address': False, 'is_changed__name1': False, 'is_changed__name2': False, 'is_changed__name3': False, 'is_changed__nwpta_ab': False, 'is_changed__nwpta_sk': False,
def job(app, namex_db, nro_connection, user, max_rows=100): row_count = 0 try: ora_con = nro_connection result, col_names = job_result_set(ora_con, max_rows) for r in result: row_count += 1 row = ora_row_to_dict(col_names, r) nr_num = row['nr_num'] nr = Request.find_by_nr(nr_num) action = row['action'] current_app.logger.debug( 'processing: {}, NameX state: {}, action: {}'.format( nr_num, None if (not nr) else nr.stateCd, action)) if nr and (nr.stateCd != State.DRAFT): if action != 'X': success = update_feeder_row( ora_con, id=row['id'], status='C', send_count=1 + 0 if (row['send_count'] is None) else row['send_count'], error_message='Ignored - Request: not processed') ora_con.commit() continue try: nr = nro.fetch_nro_request_and_copy_to_namex_request( user, nr_number=nr_num, name_request=nr) namex_db.session.add(nr) EventRecorder.record(user, Event.UPDATE_FROM_NRO, nr, {}, save_to_session=True) success = update_feeder_row( ora_con, id=row['id'], status='C', send_count=1 + 0 if (row['send_count'] is None) else row['send_count'], error_message=None) if success: ora_con.commit() namex_db.session.commit() else: raise Exception() except Exception as err: current_app.logger.error(err.with_traceback(None)) success = update_feeder_row( ora_con, id=row['id'], status=row['status'], send_count=1 + 0 if (row['send_count'] is None) else row['send_count'], error_message=err.with_traceback(None)) namex_db.session.rollback() ora_con.commit() return row_count except Exception as err: current_app.logger.error('Update Failed:', err.with_traceback(None)) return -1
# current_app.logger.debug(str(q.statement.compile( dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True})) ) for r in q.all(): row_count += 1 current_app.logger.debug('processing: {}'.format(r.nrNum)) try: nro_data_pump_update(r, ora_cursor, expires_days) db.session.add(r) EventRecorder.record(user, Event.NRO_UPDATE, r, r.json(), save_to_session=True) ora_con.commit() db.session.commit() JobTracker.job_detail(db, job_id, r.nrNum) except Exception as err: current_app.logger.error(err) current_app.logger.error('ERROR: {}'.format(r.nrNum)) db.session.rollback() ora_con.rollback() JobTracker.job_detail_error(db, job_id, r.nrNum, str(err)) JobTracker.end_job(db, job_id, datetime.utcnow(), 'success') except Exception as err: