From 5319c3b45b585a184da2467b8e43409c68a2af3f Mon Sep 17 00:00:00 2001 From: Kumara Raja Date: Mon, 11 May 2026 10:01:22 +0100 Subject: [PATCH 1/5] sonarqube blocker and high fixes --- scripts/githooks/scan-secrets.sh | 10 +- src/pytest_nhsd_apim/apigee_edge.py | 112 +++++++++++++++++------ src/pytest_nhsd_apim/identity_service.py | 70 +++++++++----- src/pytest_nhsd_apim/log.py | 7 +- 4 files changed, 142 insertions(+), 57 deletions(-) diff --git a/scripts/githooks/scan-secrets.sh b/scripts/githooks/scan-secrets.sh index be4b791..4be7e1a 100755 --- a/scripts/githooks/scan-secrets.sh +++ b/scripts/githooks/scan-secrets.sh @@ -52,9 +52,13 @@ function get-cmd-to-run() { "staged-changes") cmd="protect --source $dir --verbose --staged" ;; + *) + echo "Unknown option: $check" + exit 1 + ;; esac # Include base line file if it exists - if [ -f "$dir/scripts/config/.gitleaks-baseline.json" ]; then + if [[ -f "$dir/scripts/config/.gitleaks-baseline.json" ]]; then cmd="$cmd --baseline-path $dir/scripts/config/.gitleaks-baseline.json" fi # Include the config file @@ -94,8 +98,8 @@ function run-gitleaks-in-docker() { # ============================================================================== function is-arg-true() { - - if [[ "$1" =~ ^(true|yes|y|on|1|TRUE|YES|Y|ON)$ ]]; then + input_value="$1" + if [[ "$input_value" =~ ^(true|yes|y|on|1|TRUE|YES|Y|ON)$ ]]; then return 0 else return 1 diff --git a/src/pytest_nhsd_apim/apigee_edge.py b/src/pytest_nhsd_apim/apigee_edge.py index ac5da7a..5336540 100644 --- a/src/pytest_nhsd_apim/apigee_edge.py +++ b/src/pytest_nhsd_apim/apigee_edge.py @@ -8,7 +8,7 @@ import functools import warnings -from datetime import datetime +from datetime import datetime, timezone from typing import Callable from uuid import uuid4 @@ -69,7 +69,9 @@ def _get_proxy_json(session, nhsd_apim_proxy_url): + "Please check the validity of the APIGEE credentials and token as well as any headers." ) deployment_resp = session.get(f"{nhsd_apim_proxy_url}/deployments") - assert deployment_resp.status_code == 200, deployment_err_msg.format(deployment_resp.content) + assert deployment_resp.status_code == 200, deployment_err_msg.format( + deployment_resp.content + ) deployment_json = deployment_resp.json() # Should be the case @@ -90,7 +92,9 @@ def _get_proxy_json(session, nhsd_apim_proxy_url): @pytest.fixture() -def _identity_service_proxy(_apigee_edge_session, nhsd_apim_config, _identity_service_proxy_name): +def _identity_service_proxy( + _apigee_edge_session, nhsd_apim_config, _identity_service_proxy_name +): """ Get the current revision deployed and pull proxy metadata. """ @@ -108,7 +112,9 @@ def _apigee_proxy(_apigee_edge_session, nhsd_apim_config, nhsd_apim_proxy_name): Get the current revision deployed and pull proxy metadata. """ org = nhsd_apim_config["APIGEE_ORGANIZATION"] - apigee_edge_api_proxy_url = APIGEE_BASE_URL + f"organizations/{org}/apis/{nhsd_apim_proxy_name}" + apigee_edge_api_proxy_url = ( + APIGEE_BASE_URL + f"organizations/{org}/apis/{nhsd_apim_proxy_name}" + ) return _get_proxy_json(_apigee_edge_session, apigee_edge_api_proxy_url) @@ -147,13 +153,21 @@ def _proxy_products(_apigee_edge_session, nhsd_apim_proxy_name, nhsd_apim_config empty in other fixtures. """ global _APIGEE_PRODUCTS - proxy_products = [product for product in _APIGEE_PRODUCTS if nhsd_apim_proxy_name in product["proxies"]] + proxy_products = [ + product + for product in _APIGEE_PRODUCTS + if nhsd_apim_proxy_name in product["proxies"] + ] if len(proxy_products) == 0: # Refresh the list and try again... _APIGEE_PRODUCTS = get_all_products(_apigee_edge_session, nhsd_apim_config) - proxy_products = [product for product in _APIGEE_PRODUCTS if nhsd_apim_proxy_name in product["proxies"]] + proxy_products = [ + product + for product in _APIGEE_PRODUCTS + if nhsd_apim_proxy_name in product["proxies"] + ] if len(proxy_products) == 0: raise ValueError(f"No products grant access to proxy {nhsd_apim_proxy_name}") @@ -220,12 +234,18 @@ def _identity_service_proxy_names(_proxy_product_with_scope): """ Get a list of `identity-service` proxies for which we can match a given/required APIGEE `scope`. """ - return [proxy for proxy in _proxy_product_with_scope["proxies"] if proxy.startswith("identity-service")] + return [ + proxy + for proxy in _proxy_product_with_scope["proxies"] + if proxy.startswith("identity-service") + ] @pytest.fixture() @log_method -def _identity_service_proxy_name(_identity_service_proxy_names, nhsd_apim_authorization): +def _identity_service_proxy_name( + _identity_service_proxy_names, nhsd_apim_authorization +): """ Make a reasonable choice about which identity-service proxy to use. @@ -240,10 +260,14 @@ def _identity_service_proxy_name(_identity_service_proxy_names, nhsd_apim_author if not nhsd_apim_authorization: return None - keycloak = next(filter(lambda name: "-mock" in name, _identity_service_proxy_names), None) + keycloak = next( + filter(lambda name: "-mock" in name, _identity_service_proxy_names), None + ) if keycloak: return keycloak - warnings.warn(f"Unable to find mock auth generation 2 in {_identity_service_proxy_names}.") + warnings.warn( + f"Unable to find mock auth generation 2 in {_identity_service_proxy_names}." + ) return _identity_service_proxy_names[0] @@ -287,7 +311,11 @@ def test_app(nhsd_apim_test_app) -> Callable: @pytest.fixture(scope="session") @log_method def nhsd_apim_test_app( - _create_test_app, _apigee_edge_session, _apigee_app_base_url, _apigee_app_base_url_no_dev, _test_app_id + _create_test_app, + _apigee_edge_session, + _apigee_app_base_url, + _apigee_app_base_url_no_dev, + _test_app_id, ) -> Callable: """ A Callable that gets you the current state of the test app. @@ -318,9 +346,13 @@ def app(force_refresh=False): if _TEST_APP and not force_refresh: return _TEST_APP if _test_app_id: - resp = _apigee_edge_session.get(_apigee_app_base_url_no_dev + "/" + _test_app_id) + resp = _apigee_edge_session.get( + _apigee_app_base_url_no_dev + "/" + _test_app_id + ) else: - resp = _apigee_edge_session.get(_apigee_app_base_url + "/" + _create_test_app["name"]) + resp = _apigee_edge_session.get( + _apigee_app_base_url + "/" + _create_test_app["name"] + ) _TEST_APP = resp.json() return _TEST_APP @@ -350,7 +382,9 @@ def unsubscribe(): app_name = app["name"] for cred in app["credentials"]: key = cred["consumerKey"] - resp = _apigee_edge_session.delete(_apigee_app_base_url + f"/{app_name}/keys/{key}") + resp = _apigee_edge_session.delete( + _apigee_app_base_url + f"/{app_name}/keys/{key}" + ) app = nhsd_apim_test_app(force_refresh=True) return unsubscribe @@ -365,16 +399,20 @@ def get_matching_creds(app, product_name): def approved(x): return x["status"] == "approved" - now = int(1000 * datetime.utcnow().timestamp()) + now = int(1000 * datetime.now(timezone.utc).timestamp()) for creds in filter(approved, app["credentials"]): if creds["expiresAt"] == -1 or now < creds["expiresAt"]: - approved_product_names = [p["apiproduct"] for p in filter(approved, creds["apiProducts"])] + approved_product_names = [ + p["apiproduct"] for p in filter(approved, creds["apiProducts"]) + ] if product_name in approved_product_names: return creds @log_method -def get_app_credentials_for_product(apigee_app_base_url, apigee_edge_session, app, product_name, _test_app_id): +def get_app_credentials_for_product( + apigee_app_base_url, apigee_edge_session, app, product_name, _test_app_id +): matching_creds = get_matching_creds(app, product_name) if matching_creds is not None: return matching_creds @@ -382,7 +420,9 @@ def get_app_credentials_for_product(apigee_app_base_url, apigee_edge_session, ap # If app already exists we do not want to modify its # subscriptions. if not _test_app_id == "": - raise ValueError(f"App with id {_test_app_id} does not have expected credentials") + raise ValueError( + f"App with id {_test_app_id} does not have expected credentials" + ) # Use the apigee edge api to add another set of credentials # https://apidocs.apigee.com/docs/developer-apps/1/routes/organizations/%7Borg_name%7D/developers/%7Bdeveloper_email%7D/apps/%7Bapp_name%7D/put @@ -390,7 +430,9 @@ def get_app_credentials_for_product(apigee_app_base_url, apigee_edge_session, ap app_url = apigee_app_base_url + "/" + app["name"] resp = apigee_edge_session.put(app_url, json=app) if resp.status_code != 200: - raise ValueError(f"Unexpected response from {app_url}: {resp.status_code}, {resp.text}") + raise ValueError( + f"Unexpected response from {app_url}: {resp.status_code}, {resp.text}" + ) global _TEST_APP _TEST_APP = resp.json() @@ -466,7 +508,9 @@ def _create_test_app( # Retrieving pre-existing app if not _test_app_id == "": - get_resp = _apigee_edge_session.get(_apigee_app_base_url_no_dev + "/" + _test_app_id) + get_resp = _apigee_edge_session.get( + _apigee_app_base_url_no_dev + "/" + _test_app_id + ) err_msg = f"Could not GET TestApp: {_test_app_id}.\tReason: {get_resp.text}" assert get_resp.status_code == 200, err_msg yield get_resp.json() @@ -477,12 +521,18 @@ def _create_test_app( "attributes": [{"name": "jwks-resource-url", "value": jwt_public_key_url}], } create_resp = _apigee_edge_session.post(_apigee_app_base_url, json=app) - err_msg = f"Could not CREATE TestApp: `{app['name']}`.\tReason: {create_resp.text}" + err_msg = ( + f"Could not CREATE TestApp: `{app['name']}`.\tReason: {create_resp.text}" + ) assert create_resp.status_code == 201, err_msg yield create_resp.json() - delete_resp = _apigee_edge_session.delete(_apigee_app_base_url + "/" + app["name"]) - err_msg = f"Could not DELETE TestApp: `{app['name']}`.\tReason: {delete_resp.text}" + delete_resp = _apigee_edge_session.delete( + _apigee_app_base_url + "/" + app["name"] + ) + err_msg = ( + f"Could not DELETE TestApp: `{app['name']}`.\tReason: {delete_resp.text}" + ) assert delete_resp.status_code == 200, err_msg global _TEST_APP _TEST_APP = None @@ -510,7 +560,9 @@ def _create_function_scoped_test_app( # Retrieving pre-existing app if not _test_app_id == "": - get_resp = _apigee_edge_session.get(_apigee_app_base_url_no_dev + "/" + _test_app_id) + get_resp = _apigee_edge_session.get( + _apigee_app_base_url_no_dev + "/" + _test_app_id + ) err_msg = f"Could not GET TestApp: {_test_app_id}.\tReason: {get_resp.text}" assert get_resp.status_code == 200, err_msg yield get_resp.json() @@ -521,12 +573,18 @@ def _create_function_scoped_test_app( "attributes": [{"name": "jwks-resource-url", "value": jwt_public_key_url}], } create_resp = _apigee_edge_session.post(_apigee_app_base_url, json=app) - err_msg = f"Could not CREATE TestApp: `{app['name']}`.\tReason: {create_resp.text}" + err_msg = ( + f"Could not CREATE TestApp: `{app['name']}`.\tReason: {create_resp.text}" + ) assert create_resp.status_code == 201, err_msg yield create_resp.json() - delete_resp = _apigee_edge_session.delete(_apigee_app_base_url + "/" + app["name"]) - err_msg = f"Could not DELETE TestApp: `{app['name']}`.\tReason: {delete_resp.text}" + delete_resp = _apigee_edge_session.delete( + _apigee_app_base_url + "/" + app["name"] + ) + err_msg = ( + f"Could not DELETE TestApp: `{app['name']}`.\tReason: {delete_resp.text}" + ) assert delete_resp.status_code == 200, err_msg global _TEST_APP _TEST_APP = None diff --git a/src/pytest_nhsd_apim/identity_service.py b/src/pytest_nhsd_apim/identity_service.py index 2f6f8ce..22dcda6 100644 --- a/src/pytest_nhsd_apim/identity_service.py +++ b/src/pytest_nhsd_apim/identity_service.py @@ -20,6 +20,10 @@ HttpUrlString = Annotated[HttpUrl, AfterValidator(lambda v: str(v).rstrip("/"))] +CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" + +PREFIX_HTTPS = "https://" + #### Config models class KeycloakConfig(BaseModel): @@ -47,7 +51,7 @@ class KeycloakConfig(BaseModel): @property def keycloak_url(self): - prefix = "https://" + prefix = PREFIX_HTTPS host = "identity.ptl.api.platform.nhs.uk" path = f"/realms/{self.realm}/protocol/openid-connect" return f"{prefix}{host}{path}" @@ -65,7 +69,7 @@ class AuthorizationCodeConfig(BaseModel): """Config needed to authenticate using authorization_code flow in the identity service""" def _identity_service_base_url(env): - prefix = "https://" + prefix = PREFIX_HTTPS host = "api.service.nhs.uk" path = "/oauth2-mock" if env != "prod": @@ -102,7 +106,7 @@ class ClientCredentialsConfig(BaseModel): """Config needed to authenticate using client_credentials flow in the identity service""" def _identity_service_base_url(env): - prefix = "https://" + prefix = PREFIX_HTTPS host = "api.service.nhs.uk" path = "/oauth2-mock" # lets just support mock auth v2... if env != "prod": @@ -136,7 +140,9 @@ def encode_jwt(self): "exp": int(time()) + 300, # 5 minutes in the future } additional_headers = {"kid": self.jwt_kid} - client_assertion = jwt.encode(claims, self.jwt_private_key, algorithm="RS512", headers=additional_headers) + client_assertion = jwt.encode( + claims, self.jwt_private_key, algorithm="RS512", headers=additional_headers + ) return client_assertion @@ -154,7 +160,9 @@ class NHSLoginConfig(BaseModel): """Config needed to authenticate using NHS Login""" def __init__(self, **kwargs): - openid_config = requests.get("https://auth.aos.signin.nhs.uk/.well-known/openid-configuration").json() + openid_config = requests.get( + "https://auth.aos.signin.nhs.uk/.well-known/openid-configuration" + ).json() self.nhs_login_base_url = openid_config["issuer"] well_known_jwks: list = requests.get(openid_config["jwks_uri"]).json() @@ -164,7 +172,9 @@ def __init__(self, **kwargs): super().__init__(**kwargs) - callback_url: HttpUrlString = "https://nhsd-apim-testing-int-ns.herokuapp.com/nhslogin/callback" + callback_url: HttpUrlString = ( + "https://nhsd-apim-testing-int-ns.herokuapp.com/nhslogin/callback" + ) nhs_login_base_url: HttpUrlString client_id: str = "APIM-1" jwt_private_key: str @@ -183,7 +193,9 @@ def encode_jwt(self): "exp": int(time()) + 300, # 5 minutes in the future } additional_headers = {"kid": self.jwt_kid} - client_assertion = jwt.encode(claims, self.jwt_private_key, algorithm=self.alg, headers=additional_headers) + client_assertion = jwt.encode( + claims, self.jwt_private_key, algorithm=self.alg, headers=additional_headers + ) return client_assertion @@ -216,7 +228,7 @@ def get_token(self): login_session = requests.session() data = { "grant_type": "client_credentials", - "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + "client_assertion_type": CLIENT_ASSERTION_TYPE, "client_assertion": self.config.encode_jwt(), } # 1. Do the post call to the identity service @@ -260,7 +272,9 @@ def _get_authorize_endpoint_response( }, ) if resp.status_code != 200: - raise RuntimeError(f"{auth_url} request returned {resp.status_code}: {resp.text}") + raise RuntimeError( + f"{auth_url} request returned {resp.status_code}: {resp.text}" + ) return resp @staticmethod @@ -294,7 +308,9 @@ def _log_in_identity_service_provider( form_submission_data, ): form_submit_url = authorize_form.action or authorize_response.request.url - resp = session.request(authorize_form.method, form_submit_url, data=form_submission_data) + resp = session.request( + authorize_form.method, form_submit_url, data=form_submission_data + ) # TODO: Investigate why when using the fixtures it returns 404 and when # using with external credentials returns 200 # if resp.status_code != 200: @@ -305,7 +321,9 @@ def _log_in_identity_service_provider( @staticmethod def _get_auth_code_from_mock_auth(response_identity_service_login): - qs = urlparse(response_identity_service_login.history[-1].headers["Location"]).query + qs = urlparse( + response_identity_service_login.history[-1].headers["Location"] + ).query auth_code = parse_qs(qs)["code"] if isinstance(auth_code, list): # in case there's multiple, this was a bug at one stage @@ -327,12 +345,16 @@ def get_token(self): self.config.scope, ) - authorize_form = self._get_authorization_form(authorize_response.content.decode()) + authorize_form = self._get_authorization_form( + authorize_response.content.decode() + ) # 2. Parse the login page. For keycloak this presents an # HTML form, which must be filled in with valid data. The tester # can submits their login data with the `login_form` field. - form_submission_data = self._get_authorize_form_submission_data(authorize_form, self.config.login_form) + form_submission_data = self._get_authorize_form_submission_data( + authorize_form, self.config.login_form + ) # form_submission_data["username"] = 656005750104 # # And here we inject a valid mock username for keycloak. @@ -456,7 +478,7 @@ def get_token(self): data = { "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange", "subject_token_type": "urn:ietf:params:oauth:token-type:id_token", - "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + "client_assertion_type": CLIENT_ASSERTION_TYPE, "subject_token": self.config.id_token, "client_assertion": self.config.encode_jwt(), } @@ -476,10 +498,10 @@ class KeycloakSignedJWTAuthenticator(Authenticator): def __init__(self, config=KeycloakSignedJWTConfig) -> None: self.config = config - raise NotImplemented(f"TODO") + raise NotImplementedError(f"TODO") def get_token(self): - raise NotImplemented(f"TODO") + raise NotImplementedError(f"TODO") class NHSLoginSandpitAuthenticator(Authenticator): @@ -487,10 +509,10 @@ class NHSLoginSandpitAuthenticator(Authenticator): def __init__(self, config=NHSLoginConfig) -> None: self.config = config - raise NotImplemented(f"TODO") + raise NotImplementedError(f"TODO") def get_token(self): - raise NotImplemented(f"TODO") + raise NotImplementedError(f"TODO") class NHSLoginAosAuthenticator(Authenticator): @@ -505,7 +527,7 @@ def __init__(self, config=NHSLoginConfig): # will need to implement SPA web scraping to do this # assuming NHS login don't provide a no-HTML API def _get_authorize_code(self): - raise NotImplemented(f"TODO") + raise NotImplementedError(f"TODO") def get_token(self): code = self.config.authorize_code or self._get_authorize_code() @@ -514,7 +536,7 @@ def get_token(self): "grant_type": "authorization_code", "code": code, "redirect_uri": self.config.callback_url, - "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + "client_assertion_type": CLIENT_ASSERTION_TYPE, "client_assertion": self.config.encode_jwt(), } # 1. Do the post call to the identity service @@ -532,10 +554,10 @@ class NHSLoginProdAuthenticator(Authenticator): def __init__(self, config=NHSLoginConfig) -> None: self.config = config - raise NotImplemented(f"TODO") + raise NotImplementedError(f"TODO") def get_token(self): - raise NotImplemented(f"TODO") + raise NotImplementedError(f"TODO") class BananaAuthenticator(Authenticator): # Placeholder @@ -543,7 +565,7 @@ class BananaAuthenticator(Authenticator): # Placeholder def __init__(self, config=BananaAuthenticatorConfig) -> None: self.config = config - raise NotImplemented(f"TODO") + raise NotImplementedError(f"TODO") def get_token(self): - raise NotImplemented(f"TODO") + raise NotImplementedError(f"TODO") diff --git a/src/pytest_nhsd_apim/log.py b/src/pytest_nhsd_apim/log.py index a1b6606..7bcd4b4 100644 --- a/src/pytest_nhsd_apim/log.py +++ b/src/pytest_nhsd_apim/log.py @@ -6,6 +6,7 @@ https://docs.pytest.org/en/7.1.x/how-to/logging.html """ + import typing import logging import inspect @@ -13,7 +14,7 @@ import uuid import json import os -from datetime import datetime +from datetime import datetime, timezone logging.METHOD = 5 logging.addLevelName(logging.METHOD, "METHOD") @@ -42,7 +43,7 @@ def log_method(f): def pre_log(f, *args, **kwargs): log_line = { - "timestamp": datetime.utcnow().isoformat(), + "timestamp": datetime.now(timezone.utc).isoformat(), "function_name": f.__name__, "id": str(uuid.uuid4()), # use this to match function entry/exit "type": "generator" if inspect.isgeneratorfunction(f) else "function", @@ -60,7 +61,7 @@ def log_and_reraise(log_line, e): def post_log(log_line, **extra): log_line["location"] = "exit" - log_line["timestamp"] = datetime.utcnow().isoformat() + log_line["timestamp"] = datetime.now(timezone.utc).isoformat() log_line.update(extra) log.method(log_line) From 4bc093e941e314ed79f02b15c8c3f678ece4cb2b Mon Sep 17 00:00:00 2001 From: Kumara Raja Date: Mon, 11 May 2026 12:57:22 +0100 Subject: [PATCH 2/5] replace fstring to normal string --- src/pytest_nhsd_apim/identity_service.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pytest_nhsd_apim/identity_service.py b/src/pytest_nhsd_apim/identity_service.py index 22dcda6..e38b18e 100644 --- a/src/pytest_nhsd_apim/identity_service.py +++ b/src/pytest_nhsd_apim/identity_service.py @@ -498,10 +498,10 @@ class KeycloakSignedJWTAuthenticator(Authenticator): def __init__(self, config=KeycloakSignedJWTConfig) -> None: self.config = config - raise NotImplementedError(f"TODO") + raise NotImplementedError("TODO") def get_token(self): - raise NotImplementedError(f"TODO") + raise NotImplementedError("TODO") class NHSLoginSandpitAuthenticator(Authenticator): @@ -509,10 +509,10 @@ class NHSLoginSandpitAuthenticator(Authenticator): def __init__(self, config=NHSLoginConfig) -> None: self.config = config - raise NotImplementedError(f"TODO") + raise NotImplementedError("TODO") def get_token(self): - raise NotImplementedError(f"TODO") + raise NotImplementedError("TODO") class NHSLoginAosAuthenticator(Authenticator): @@ -527,7 +527,7 @@ def __init__(self, config=NHSLoginConfig): # will need to implement SPA web scraping to do this # assuming NHS login don't provide a no-HTML API def _get_authorize_code(self): - raise NotImplementedError(f"TODO") + raise NotImplementedError("TODO") def get_token(self): code = self.config.authorize_code or self._get_authorize_code() @@ -554,10 +554,10 @@ class NHSLoginProdAuthenticator(Authenticator): def __init__(self, config=NHSLoginConfig) -> None: self.config = config - raise NotImplementedError(f"TODO") + raise NotImplementedError("TODO") def get_token(self): - raise NotImplementedError(f"TODO") + raise NotImplementedError("TODO") class BananaAuthenticator(Authenticator): # Placeholder @@ -565,7 +565,7 @@ class BananaAuthenticator(Authenticator): # Placeholder def __init__(self, config=BananaAuthenticatorConfig) -> None: self.config = config - raise NotImplementedError(f"TODO") + raise NotImplementedError("TODO") def get_token(self): - raise NotImplementedError(f"TODO") + raise NotImplementedError("TODO") From 5767563cb87c36b35bf992934b8aa72c9f117d3d Mon Sep 17 00:00:00 2001 From: Kumara Raja Date: Tue, 12 May 2026 13:49:00 +0100 Subject: [PATCH 3/5] bump version to 6.0.11 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a6b521f..489a6c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pytest-nhsd-apim" -version = "6.0.10" +version = "6.0.11" description = "Pytest plugin accessing NHSDigital's APIM proxies" authors = ["Adrian Ciobanita ", "Alex Carrie ", "Lucas Fantini "] maintainers = ["Alex Carrie ", "Alex Hawdon Date: Tue, 12 May 2026 13:55:35 +0100 Subject: [PATCH 4/5] removed unused local variable --- src/pytest_nhsd_apim/apigee_edge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pytest_nhsd_apim/apigee_edge.py b/src/pytest_nhsd_apim/apigee_edge.py index 5336540..859987c 100644 --- a/src/pytest_nhsd_apim/apigee_edge.py +++ b/src/pytest_nhsd_apim/apigee_edge.py @@ -382,7 +382,7 @@ def unsubscribe(): app_name = app["name"] for cred in app["credentials"]: key = cred["consumerKey"] - resp = _apigee_edge_session.delete( + _apigee_edge_session.delete( _apigee_app_base_url + f"/{app_name}/keys/{key}" ) app = nhsd_apim_test_app(force_refresh=True) From 6e7325111392579778a057b28a5b85da6b99ac40 Mon Sep 17 00:00:00 2001 From: Kumara Raja Date: Tue, 12 May 2026 16:12:50 +0100 Subject: [PATCH 5/5] comment duplicate block --- src/pytest_nhsd_apim/apigee_edge.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pytest_nhsd_apim/apigee_edge.py b/src/pytest_nhsd_apim/apigee_edge.py index 859987c..04bbf97 100644 --- a/src/pytest_nhsd_apim/apigee_edge.py +++ b/src/pytest_nhsd_apim/apigee_edge.py @@ -163,13 +163,13 @@ def _proxy_products(_apigee_edge_session, nhsd_apim_proxy_name, nhsd_apim_config # Refresh the list and try again... _APIGEE_PRODUCTS = get_all_products(_apigee_edge_session, nhsd_apim_config) - proxy_products = [ - product - for product in _APIGEE_PRODUCTS - if nhsd_apim_proxy_name in product["proxies"] - ] - if len(proxy_products) == 0: - raise ValueError(f"No products grant access to proxy {nhsd_apim_proxy_name}") + # proxy_products = [ + # product + # for product in _APIGEE_PRODUCTS + # if nhsd_apim_proxy_name in product["proxies"] + # ] + # if len(proxy_products) == 0: + # raise ValueError(f"No products grant access to proxy {nhsd_apim_proxy_name}") return proxy_products