diff --git a/legal-api/src/legal_api/resources/v2/business/business_filings/business_filings.py b/legal-api/src/legal_api/resources/v2/business/business_filings/business_filings.py index 62fed838cd..c8880f02c6 100644 --- a/legal-api/src/legal_api/resources/v2/business/business_filings/business_filings.py +++ b/legal-api/src/legal_api/resources/v2/business/business_filings/business_filings.py @@ -473,7 +473,7 @@ def check_and_update_nr(filing): effective_date = filing.json["filing"]["header"].get("effectiveDate", None) if effective_date: effective_date = datetime.datetime.fromisoformat(effective_date) - if (nr_number and not flags.is_on("enable-sandbox")): + if nr_number: nr_response = namex.query_nr_number(nr_number) # If there is an effective date, check if we need to extend the NR expiration if effective_date and namex.is_date_past_expiration(nr_response.json(), effective_date): diff --git a/legal-api/src/legal_api/services/filings/validations/change_of_name.py b/legal-api/src/legal_api/services/filings/validations/change_of_name.py index 82955feb71..9dd86c4e63 100644 --- a/legal-api/src/legal_api/services/filings/validations/change_of_name.py +++ b/legal-api/src/legal_api/services/filings/validations/change_of_name.py @@ -19,7 +19,7 @@ from legal_api.errors import Error from legal_api.models import Business -from legal_api.services import flags, namex +from legal_api.services import namex from legal_api.services.utils import get_str @@ -35,9 +35,6 @@ def validate(business: Business, filing: dict) -> Error: nr_number = get_str(filing, nr__number_path) if nr_number: - # Skip the NR check in the Sandbox - if flags.is_on("enable-sandbox"): - return None # ensure NR is approved or conditionally approved nr_response = namex.query_nr_number(nr_number).json() validation_result = namex.validate_nr(nr_response) diff --git a/legal-api/src/legal_api/services/filings/validations/common_validations.py b/legal-api/src/legal_api/services/filings/validations/common_validations.py index 7c3956fe81..69f86bf786 100644 --- a/legal-api/src/legal_api/services/filings/validations/common_validations.py +++ b/legal-api/src/legal_api/services/filings/validations/common_validations.py @@ -626,12 +626,6 @@ def validate_name_request(filing_json: dict, # pylint: disable=too-many-locals filing_type: str, accepted_request_types: Optional[list] = None) -> list: """Validate name request section.""" - # This is added specifically for the sandbox environment. - # i.e. NR check should only ever have feature flag disabled for sandbox environment. - if flags.is_on("enable-sandbox"): - current_app.logger.debug("Skipping name request validation for Sandbox.") - return [] - nr_path = f"/filing/{filing_type}/nameRequest" nr_number_path = f"{nr_path}/nrNumber" legal_name_path = f"{nr_path}/legalName" diff --git a/legal-api/src/legal_api/services/namex.py b/legal-api/src/legal_api/services/namex.py index b097a08fd2..37c8184893 100644 --- a/legal-api/src/legal_api/services/namex.py +++ b/legal-api/src/legal_api/services/namex.py @@ -125,20 +125,6 @@ def update_nr_as_future_effective(nr_json, future_effective_date: datetime): @staticmethod def validate_nr(nr_json): """Provide validation info based on a name request response payload.""" - # Initial validation result state - from . import flags # pylint: disable=import-outside-toplevel - - # This is added specifically for the sandbox environment. - # i.e. NR check should only ever have feature flag disabled for sandbox environment. - if flags.is_on("enable-sandbox"): - return { - "is_consumable": True, - "is_approved": True, - "is_expired": False, - "consent_required": None, - "consent_received": None - } - is_consumable = False is_approved = False is_expired = False @@ -191,15 +177,6 @@ def is_date_past_expiration(nr_json, date_time): @staticmethod def get_approved_name(nr_json) -> str: """Get an approved name from nr json, if any.""" - from . import flags # pylint: disable=import-outside-toplevel - - # This is added specifically for the sandbox environment. - # i.e. NR check should only ever have feature flag disabled for sandbox environment. - if flags.is_on("enable-sandbox"): - return next((name["name"] for name in nr_json["names"] - if name["state"] - in ["APPROVED", "CONDITION"]), None) - nr_name = None state_to_check = None nr_state = nr_json["state"] diff --git a/legal-api/tests/unit/resources/v2/test_name_requests.py b/legal-api/tests/unit/resources/v2/test_name_requests.py index 9e87112b0b..760fdebea2 100644 --- a/legal-api/tests/unit/resources/v2/test_name_requests.py +++ b/legal-api/tests/unit/resources/v2/test_name_requests.py @@ -249,14 +249,6 @@ def test_validate_nr_not_consumable_rejected(): assert not validation_result['consent_received'] -def test_validate_nr_rejected_skips_nr_validate(): - """Assert that nr mock data that has been rejected skips nr check.""" - - with patch.object(flags, 'is_on', return_value=True): - validation_result = namex.validate_nr(nr_not_consumable_rejected) - assert validation_result == skip_nr_check_validation_result - - def test_validate_nr_not_consumable_expired(): """Assert that nr mock data is not consumable as it has expired.""" with patch.object(flags, 'is_on', return_value=False): @@ -268,12 +260,6 @@ def test_validate_nr_not_consumable_expired(): assert not validation_result['consent_required'] assert not validation_result['consent_received'] -def test_validate_nr_expired_skips_nr_validate(): - """Assert that nr mock data that has expired skips nr check.""" - with patch.object(flags, 'is_on', return_value=True): - validation_result = namex.validate_nr(nr_not_consumable_expired) - assert validation_result == skip_nr_check_validation_result - def test_validate_nr_already_consumed(): """Assert that nr mock data has already been consumed.""" @@ -287,13 +273,6 @@ def test_validate_nr_already_consumed(): assert not validation_result['consent_received'] -def test_validate_nr_already_consumed_skips_nr_validate(): - """Assert that nr mock data has already been consumed skips nr check.""" - with patch.object(flags, 'is_on', return_value=True): - validation_result = namex.validate_nr(nr_already_consumed) - assert validation_result == skip_nr_check_validation_result - - def test_validate_nr_consent_required_not_received(): """Assert that nr mock data is conditionally approved, but consent not received.""" with patch.object(flags, 'is_on', return_value=False): @@ -307,15 +286,6 @@ def test_validate_nr_consent_required_not_received(): assert not validation_result['consent_received'] -def test_validate_nr_consent_required_not_received_skips_nr_validate(): - """Assert that nr mock data is conditionally approved, but consent not received, skips nr check.""" - with patch.object(flags, 'is_on', return_value=True): - nr_consent_required = copy.deepcopy(nr_consumable_conditional) - nr_consent_required['consentFlag'] = 'Y' - validation_result = namex.validate_nr(nr_consent_required) - assert validation_result == skip_nr_check_validation_result - - def test_validate_nr_consent_required_received(): """Assert that nr mock data is conditionally approved and consent was received.""" with patch.object(flags, 'is_on', return_value=False): @@ -356,30 +326,6 @@ def test_validate_nr_consent_required_received(): assert not validation_result['consent_received'] -def test_validate_nr_consent_required_received_skips_nr_check(): - """Assert that nr mock data is conditionally approved and consent was received, skips nr check.""" - with patch.object(flags, 'is_on', return_value=True): - validation_result = namex.validate_nr(nr_consumable_conditional) - assert validation_result == skip_nr_check_validation_result - - # N = consent waived - nr_consent_waived = copy.deepcopy(nr_consumable_conditional) - nr_consent_waived['consentFlag'] = 'N' - validation_result = namex.validate_nr(nr_consent_waived) - assert validation_result == skip_nr_check_validation_result - - # None = consent not required - nr_consent_not_required = copy.deepcopy(nr_consumable_conditional) - nr_consent_not_required['consentFlag'] = None - validation_result = namex.validate_nr(nr_consent_not_required) - assert validation_result == skip_nr_check_validation_result - - nr_consent_not_required = copy.deepcopy(nr_consumable_conditional) - nr_consent_not_required['consentFlag'] = '' - validation_result = namex.validate_nr(nr_consent_not_required) - assert validation_result == skip_nr_check_validation_result - - def test_get_approved_name(): """Get Approved/Conditional Approved name.""" with patch.object(flags, 'is_on', return_value=False): diff --git a/queue_services/business-emailer/poetry.lock b/queue_services/business-emailer/poetry.lock index 929f70f770..836f080cb9 100644 --- a/queue_services/business-emailer/poetry.lock +++ b/queue_services/business-emailer/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "alembic" @@ -1218,7 +1218,7 @@ description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version == \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")" +markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\"" files = [ {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, @@ -3696,5 +3696,5 @@ test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-it [metadata] lock-version = "2.1" -python-versions = "^3.13" -content-hash = "08599c7ea58868e11cbbc61d5e3db2431857e7bffdd1978d00fcc9de53992a92" +python-versions = ">=3.13,<3.14" +content-hash = "9e813c9b375252925838a07ca1e10f0d39e03623e951cd3d74558f51636d53b4" diff --git a/queue_services/business-emailer/pyproject.toml b/queue_services/business-emailer/pyproject.toml index 3a776548c8..718e7a10e7 100644 --- a/queue_services/business-emailer/pyproject.toml +++ b/queue_services/business-emailer/pyproject.toml @@ -8,7 +8,7 @@ readme = "README.md" [tool.poetry.dependencies] -python = "^3.13" +python = ">=3.13,<3.14" alembic = "1.15.2" aniso8601 = "10.0.0" arrow = "1.3.0" diff --git a/queue_services/business-emailer/src/business_emailer/services/namex.py b/queue_services/business-emailer/src/business_emailer/services/namex.py index ca27da3ece..8452fd7678 100644 --- a/queue_services/business-emailer/src/business_emailer/services/namex.py +++ b/queue_services/business-emailer/src/business_emailer/services/namex.py @@ -13,62 +13,15 @@ # limitations under the License. """This provides the service for namex-api calls.""" -import http -from datetime import datetime -from enum import Enum -import datedelta -import dpath.util -import pytz import requests from business_account.AccountService import AccountService -from business_model.models import Filing from flask import current_app -def get_str_from_json_filing(filing: dict, path: str) -> str | None: - """Extract a str from the JSON filing, at the provided path. - - Args: - filing (Dict): A valid registry_schema filing. - path (str): The path to the date, which is in ISO Format. - - Examples: - >>>get_str_from_json_filing( - filing={'filing':{'header':{'name': 'annualReport'}}}, - path='filing/header/name') - 'annualReport' - - """ - try: - raw = dpath.util.get(filing, path) - return str(raw) - except (IndexError, KeyError, TypeError, ValueError): - return None - - class NameXService: """Provides services to use the namex-api.""" - DATE_FORMAT = "%Y-%m-%dT%H:%M:%S%z" - DATE_FORMAT_WITH_MILLISECONDS = "%Y-%m-%dT%H:%M:%S.%f%z" - - class State(Enum): - """Name request states.""" - - APPROVED = "APPROVED" - CANCELLED = "CANCELLED" - COMPLETED = "COMPLETED" - CONDITIONAL = "CONDITIONAL" - CONSUMED = "CONSUMED" - DRAFT = "DRAFT" - EXPIRED = "EXPIRED" - HISTORICAL = "HISTORICAL" - HOLD = "HOLD" - INPROGRESS = "INPROGRESS" - REJECTED = "REJECTED" - NRO_UPDATING = "NRO_UPDATING" - @staticmethod def query_nr_number(identifier: str): """Return a JSON object with name request information.""" @@ -83,168 +36,3 @@ def query_nr_number(identifier: str): }) return nr_response - - @staticmethod - def query_nr_numbers(identifiers): - """Return a JSON object with name request information.""" - namex_url = current_app.config.get("NAMEX_SVC_URL") - - token = AccountService.get_bearer_token() - - # Perform proxy call with identifiers (e.g. NR 1234567, NR 1234568) - nr_response = requests.post(namex_url + "requests/search", - json={"identifiers": identifiers}, - headers={ - "Content-Type": "application/json", - "Authorization": "Bearer " + token - }) - - return nr_response - - @staticmethod - def update_nr(nr_json): - """Update name request with nr_json.""" - auth_url = current_app.config.get("NAMEX_AUTH_SVC_URL") - username = current_app.config.get("NAMEX_SERVICE_CLIENT_USERNAME") - secret = current_app.config.get("NAMEX_SERVICE_CLIENT_SECRET") - namex_url = current_app.config.get("NAMEX_SVC_URL") - - # Get access token for namex-api in a different keycloak realm - auth = requests.post(auth_url, auth=(username, secret), headers={ - "Content-Type": "application/x-www-form-urlencoded"}, data={"grant_type": "client_credentials"}) - - # Return the auth response if an error occurs - if auth.status_code != http.HTTPStatus.OK.value: - return auth.json() - - token = dict(auth.json())["access_token"] - - # Perform update proxy call using nr number (e.g. NR 1234567) - nr_response = requests.put(namex_url + "requests/" + nr_json["nrNum"], headers={ - "Content-Type": "application/json", - "Authorization": "Bearer " + token - }, json=nr_json) - - return nr_response - - @staticmethod - def update_nr_as_future_effective(nr_json, future_effective_date: datetime): - """Set expiration date of a name request to the future effective date and update the name request.""" - # Convert to namex supported timezone - future_effective_date = future_effective_date.astimezone(pytz.timezone("GMT")) - - # add expiration buffer as future-effective-filings processing may not be immediate - future_effective_date += datedelta.datedelta(days=1) - - nr_json["expirationDate"] = future_effective_date.strftime(NameXService.DATE_FORMAT) - update_nr_response = NameXService.update_nr(nr_json) - - return update_nr_response - - @staticmethod - def validate_nr(nr_json): - """Provide validation info based on a name request response payload.""" - # Initial validation result state - from . import flags # pylint: disable=import-outside-toplevel - - # This is added specifically for the sandbox environment. - # i.e. NR check should only ever have feature flag disabled for sandbox environment. - if flags.is_on("enable-sandbox"): - return { - "is_consumable": True, - "is_approved": True, - "is_expired": False, - "consent_required": None, - "consent_received": None - } - - is_consumable = False - is_approved = False - is_expired = False - consent_required = None - consent_received = None - nr_state = nr_json["state"] - - # Is approved - if nr_state == NameXService.State.APPROVED.value: - is_approved = True - is_consumable = True - - # Is conditionally approved - elif nr_state == NameXService.State.CONDITIONAL.value: - is_approved = True - consent_required = True - consent_received = False - - # Check if consent received - # Y = consent required and not received, R = consent required and received - # N = consent waived, None = consent not required - if nr_json["consentFlag"] == "R": - consent_received = True - is_consumable = True - elif nr_json["consentFlag"] == "N" or not nr_json["consentFlag"]: - consent_required = False - is_consumable = True - - elif nr_state == NameXService.State.EXPIRED.value: - is_expired = True - - return { - "is_consumable": is_consumable, - "is_approved": is_approved, - "is_expired": is_expired, - "consent_required": consent_required, - "consent_received": consent_received - } - - @staticmethod - def is_date_past_expiration(nr_json, date_time): - """Return true if the inputted date time is passed the name request expiration.""" - try: - expiration_date = datetime.strptime(nr_json["expirationDate"], NameXService.DATE_FORMAT) - except ValueError: - expiration_date = datetime.strptime(nr_json["expirationDate"], NameXService.DATE_FORMAT_WITH_MILLISECONDS) - expiration_date = expiration_date.astimezone(pytz.timezone("GMT")) - return expiration_date < date_time - - @staticmethod - def get_approved_name(nr_json) -> str | None: - """Get an approved name from nr json, if any.""" - from . import flags # pylint: disable=import-outside-toplevel - - # This is added specifically for the sandbox environment. - # i.e. NR check should only ever have feature flag disabled for sandbox environment. - if flags.is_on("enable-sandbox"): - return next((name["name"] for name in nr_json["names"] - if name["state"] - in ["APPROVED", "CONDITION"]), None) - - nr_name = None - state_to_check = None - nr_state = nr_json["state"] - - if nr_state == NameXService.State.APPROVED.value: - state_to_check = "APPROVED" - elif nr_state == NameXService.State.CONDITIONAL.value: - state_to_check = "CONDITION" # Name state is different from NR state - else: # When NR is not approved - return None - - for name in nr_json["names"]: - if name["state"] == state_to_check: - nr_name = name["name"] - break - - return nr_name - - @staticmethod - def has_correction_changed_name(filing) -> bool: - """Has correction changed the legal name.""" - corrected_filing = Filing.find_by_id(filing["filing"]["correction"]["correctedFilingId"]) - nr_path = "/filing/incorporationApplication/nameRequest/nrNumber" - legal_name_path = "/filing/incorporationApplication/nameRequest/legalName" - old_nr_number = get_str_from_json_filing(corrected_filing.json, nr_path) - new_nr_number = get_str_from_json_filing(filing, nr_path) - old_legal_name = get_str_from_json_filing(corrected_filing.json, legal_name_path) - new_legal_name = get_str_from_json_filing(filing, legal_name_path) - return old_nr_number != new_nr_number or old_legal_name != new_legal_name diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_dissolution_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_dissolution_notification.py index d7a919ba60..fc32a54dfa 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_dissolution_notification.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_dissolution_notification.py @@ -97,10 +97,10 @@ def test_firms_dissolution_notification(app, session, status, legal_type, submit {'filingId': filing.id, 'type': 'dissolution', 'option': status}, token) if status == 'PAID': assert email['content']['subject'] == \ - 'JANE A DOE - Confirmation of Filing from the Business Registry' + f'{legal_name} - Confirmation of Filing from the Business Registry' else: assert email['content']['subject'] == \ - 'JANE A DOE - Dissolution Documents from the Business Registry' + f'{legal_name} - Dissolution Documents from the Business Registry' if submitter_role: assert f'{submitter_role}@email.com' in email['recipients'] diff --git a/queue_services/business-filer/src/business_filer/filing_processors/filing_components/name_request.py b/queue_services/business-filer/src/business_filer/filing_processors/filing_components/name_request.py index 34acedf53e..bce294b43f 100644 --- a/queue_services/business-filer/src/business_filer/filing_processors/filing_components/name_request.py +++ b/queue_services/business-filer/src/business_filer/filing_processors/filing_components/name_request.py @@ -42,7 +42,6 @@ from business_filer.common.services.account_service import AccountService from business_filer.exceptions import QueueException -from business_filer.services import Flags from business_filer.services.utils import get_str @@ -51,10 +50,6 @@ def consume_nr(business: Business, filing_type: str | None = None): """Update the nr to a consumed state.""" try: - if Flags.is_on("enable-sandbox"): - current_app.logger.info("Skip consuming NR") - return - filing_type = filing_type if filing_type else filing.filing_type # skip this if none (nrNumber will not be available for numbered company) if nr_num := get_str(filing.filing_json, f"/filing/{filing_type}/nameRequest/nrNumber"):