diff --git a/.gitignore b/.gitignore index 44dfdf0..19b2b43 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,5 @@ labs-in-progress/ .coverage tests/python/htmlcov/ -shared/bicep/modules/**/*.json \ No newline at end of file +shared/bicep/modules/**/*.json +main.json \ No newline at end of file diff --git a/infrastructure/afd-apim/create.ipynb b/infrastructure/afd-apim/create.ipynb index 1030b70..0998fdd 100644 --- a/infrastructure/afd-apim/create.ipynb +++ b/infrastructure/afd-apim/create.ipynb @@ -57,9 +57,9 @@ "if use_ACA:\n", " utils.print_info('ACA APIs will be created.')\n", "\n", - " aca_backend_1_policy_xml = utils.read_policy_xml(ACA_BACKEND_1_XML_POLICY_PATH)\n", - " aca_backend_2_policy_xml = utils.read_policy_xml(ACA_BACKEND_2_XML_POLICY_PATH)\n", - " aca_backend_pool_policy_xml = utils.read_policy_xml(ACA_BACKEND_POOL_XML_POLICY_PATH)\n", + " aca_backend_1_policy_xml = utils.read_policy_xml(BACKEND_XML_POLICY_PATH).format(backend_id = 'aca-backend-1')\n", + " aca_backend_2_policy_xml = utils.read_policy_xml(BACKEND_XML_POLICY_PATH).format(backend_id = 'aca-backend-2')\n", + " aca_backend_pool_policy_xml = utils.read_policy_xml(BACKEND_XML_POLICY_PATH).format(backend_id = 'aca-backend-pool')\n", "\n", " # Hello World (ACA Backend 1)\n", " api_hwaca_1_get = GET_APIOperation('This is a GET for Hello World on ACA Backend 1')\n", diff --git a/infrastructure/afd-apim/main.bicep b/infrastructure/afd-apim/main.bicep index 4918a19..ad4fc96 100644 --- a/infrastructure/afd-apim/main.bicep +++ b/infrastructure/afd-apim/main.bicep @@ -226,7 +226,7 @@ module backendPoolModule '../../shared/bicep/modules/apim/v1/backend-pool.bicep' // 8. APIM APIs module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [for api in apis: if(length(apis) > 0) { - name: '${api.name}-${resourceSuffix}' + name: 'api-${api.name}' params: { apimName: apimName appInsightsInstrumentationKey: appInsightsInstrumentationKey diff --git a/infrastructure/apim-aca/create.ipynb b/infrastructure/apim-aca/create.ipynb index 6ee828a..1ede262 100644 --- a/infrastructure/apim-aca/create.ipynb +++ b/infrastructure/apim-aca/create.ipynb @@ -46,9 +46,9 @@ "\n", "# Policies\n", "hello_world_policy_xml = utils.read_policy_xml(HELLO_WORLD_XML_POLICY_PATH)\n", - "aca_backend_1_policy_xml = utils.read_policy_xml(ACA_BACKEND_1_XML_POLICY_PATH)\n", - "aca_backend_2_policy_xml = utils.read_policy_xml(ACA_BACKEND_2_XML_POLICY_PATH)\n", - "aca_backend_pool_policy_xml = utils.read_policy_xml(ACA_BACKEND_POOL_XML_POLICY_PATH)\n", + "aca_backend_1_policy_xml = utils.read_policy_xml(BACKEND_XML_POLICY_PATH).format(backend_id = 'aca-backend-1')\n", + "aca_backend_2_policy_xml = utils.read_policy_xml(BACKEND_XML_POLICY_PATH).format(backend_id = 'aca-backend-2')\n", + "aca_backend_pool_policy_xml = utils.read_policy_xml(BACKEND_XML_POLICY_PATH).format(backend_id = 'aca-backend-pool')\n", "\n", "# Hello World (Root)\n", "api_hwroot_get = GET_APIOperation('This is a GET for Hello World in the root', hello_world_policy_xml)\n", diff --git a/infrastructure/apim-aca/main.bicep b/infrastructure/apim-aca/main.bicep index 406b57e..469da37 100644 --- a/infrastructure/apim-aca/main.bicep +++ b/infrastructure/apim-aca/main.bicep @@ -134,7 +134,7 @@ module backendPoolModule '../../shared/bicep/modules/apim/v1/backend-pool.bicep' // 7. APIM APIs module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [ for api in apis: if (length(apis) > 0) { - name: '${api.name}-${resourceSuffix}' + name: 'api-${api.name}' params: { apimName: apimName appInsightsInstrumentationKey: appInsightsInstrumentationKey diff --git a/infrastructure/simple-apim/main.bicep b/infrastructure/simple-apim/main.bicep index b2af5e2..048d3bd 100644 --- a/infrastructure/simple-apim/main.bicep +++ b/infrastructure/simple-apim/main.bicep @@ -49,7 +49,7 @@ module apimModule '../../shared/bicep/modules/apim/v1/apim.bicep' = { // 4. APIM APIs module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [for api in apis: if(length(apis) > 0) { - name: '${api.name}-${resourceSuffix}' + name: 'api-${api.name}' params: { apimName: apimName appInsightsInstrumentationKey: appInsightsInstrumentationKey diff --git a/samples/authX-pro/create.ipynb b/samples/authX-pro/create.ipynb index 635779f..f069ac8 100644 --- a/samples/authX-pro/create.ipynb +++ b/samples/authX-pro/create.ipynb @@ -126,8 +126,9 @@ " hr_member_role_id = '{{HRMemberRoleId}}'\n", ")\n", "\n", + "hr_product_name = 'hr'\n", "products: List[Product] = [\n", - " Product('hr', 'Human Resources', 'Product for Human Resources APIs providing access to employee data, organizational structure, benefits information, and HR management services. Includes JWT-based authentication for HR members.', 'published', False, False, hr_product_xml)\n", + " Product(hr_product_name, 'Human Resources', 'Product for Human Resources APIs providing access to employee data, organizational structure, benefits information, and HR management services. Includes JWT-based authentication for HR members.', 'published', False, False, hr_product_xml)\n", "]\n", "\n", "# 6) Define the APIs and their operations and policies\n", @@ -136,13 +137,13 @@ "hremployees_api_path = f'/{api_prefix}employees'\n", "hremployees_get = GET_APIOperation('Gets the employees', utils.read_policy_xml('./hr_get.xml'))\n", "hremployees_post = POST_APIOperation('Creates a new employee', utils.read_policy_xml('./hr_post.xml'))\n", - "hremployees = API(f'{api_prefix}Employees', 'Employees Pro', hremployees_api_path, 'This is a Human Resources API for employee information', utils.read_policy_xml(DEFAULT_XML_POLICY_PATH), operations = [hremployees_get, hremployees_post], tags = tags, productNames = ['hr'])\n", + "hremployees = API(f'{api_prefix}Employees', 'Employees Pro', hremployees_api_path, 'This is a Human Resources API for employee information', utils.read_policy_xml(DEFAULT_XML_POLICY_PATH), operations = [hremployees_get, hremployees_post], tags = tags, productNames = [hr_product_name])\n", "\n", "# Benefits (HR)\n", "hrbenefits_api_path = f'/{api_prefix}benefits'\n", "hrbenefits_get = GET_APIOperation('Gets employee benefits', utils.read_policy_xml('./hr_get.xml'))\n", "hrbenefits_post = POST_APIOperation('Creates employee benefits', utils.read_policy_xml('./hr_post.xml'))\n", - "hrbenefits = API(f'{api_prefix}Benefits', 'Benefits Pro', hrbenefits_api_path, 'This is a Human Resources API for employee benefits', utils.read_policy_xml(DEFAULT_XML_POLICY_PATH), operations = [hrbenefits_get, hrbenefits_post], tags = tags, productNames = ['hr'])\n", + "hrbenefits = API(f'{api_prefix}Benefits', 'Benefits Pro', hrbenefits_api_path, 'This is a Human Resources API for employee benefits', utils.read_policy_xml(DEFAULT_XML_POLICY_PATH), operations = [hrbenefits_get, hrbenefits_post], tags = tags, productNames = [hr_product_name])\n", "\n", "# APIs Array\n", "apis: List[API] = [hremployees, hrbenefits]\n", @@ -171,7 +172,8 @@ "bicep_parameters = {\n", " 'apis': {'value': [api.to_dict() for api in apis]},\n", " 'namedValues': {'value': [nv.to_dict() for nv in nvs]},\n", - " 'policyFragments': {'value': [pf.to_dict() for pf in pfs]}\n", + " 'policyFragments': {'value': [pf.to_dict() for pf in pfs]},\n", + " 'products': {'value': [product.to_dict() for product in products]}\n", "}\n", "\n", "# 2) Infrastructure must be in place before samples can be layered on top\n", diff --git a/samples/authX-pro/main.bicep b/samples/authX-pro/main.bicep index d5f3377..322675d 100644 --- a/samples/authX-pro/main.bicep +++ b/samples/authX-pro/main.bicep @@ -78,7 +78,8 @@ module productHr '../../shared/bicep/modules/apim/v1/product.bicep' = [for produ ] }] -// APIM APIs +// APIM APIs (deployed after products are ready to avoid race conditions) +@batchSize(1) // Deploy APIs sequentially to avoid race conditions module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [for api in apis: { name: 'api-${api.name}' params:{ @@ -90,7 +91,8 @@ module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [for api in a } dependsOn: [ namedValue // ensure all named values are created before APIs - productHr // ensure products are created before APIs that reference them + policyFragment // ensure all policy fragments are created before APIs + productHr // ensure all products are fully created before APIs ] }] diff --git a/samples/authX/hr_product.xml b/samples/authX/hr_product.xml deleted file mode 100644 index e1be748..0000000 --- a/samples/authX/hr_product.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - {jwt_signing_key} - - - - {hr_member_role_id} - - - - - - - - - - - - - - \ No newline at end of file diff --git a/samples/authX/main.bicep b/samples/authX/main.bicep index 8c516cf..f2651ad 100644 --- a/samples/authX/main.bicep +++ b/samples/authX/main.bicep @@ -34,7 +34,7 @@ resource apimService 'Microsoft.ApiManagement/service@2024-06-01-preview' existi // APIM Named Values module namedValue '../../shared/bicep/modules/apim/v1/named-value.bicep' = [for nv in namedValues: if (!empty(namedValues)) { - name: nv.name + name: 'nv-${nv.name}' params: { apimName: apimName namedValueName: nv.name @@ -45,7 +45,7 @@ module namedValue '../../shared/bicep/modules/apim/v1/named-value.bicep' = [for // APIM APIs module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [for api in apis: if(!empty(apis)) { - name: '${api.name}-${resourceSuffix}' + name: 'api-${api.name}' params: { apimName: apimName appInsightsInstrumentationKey: appInsightsInstrumentationKey diff --git a/samples/general/create.ipynb b/samples/general/create.ipynb index d5fdf03..7ce247c 100644 --- a/samples/general/create.ipynb +++ b/samples/general/create.ipynb @@ -46,7 +46,7 @@ "# 1) User-defined parameters (change these as needed)\n", "rg_location = 'eastus2'\n", "index = 1\n", - "deployment = INFRASTRUCTURE.AFD_APIM_PE\n", + "deployment = INFRASTRUCTURE.SIMPLE_APIM\n", "tags = ['general']\n", "\n", "# 2) Service-defined parameters (please do not change these)\n", diff --git a/samples/general/main.bicep b/samples/general/main.bicep index 1e222a8..4a51c99 100644 --- a/samples/general/main.bicep +++ b/samples/general/main.bicep @@ -32,7 +32,7 @@ resource apimService 'Microsoft.ApiManagement/service@2024-06-01-preview' existi // APIM APIs module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [for api in apis: if(!empty(apis)) { - name: '${api.name}-${resourceSuffix}' + name: 'api-${api.name}' params: { apimName: apimName appInsightsInstrumentationKey: appInsightsInstrumentationKey diff --git a/samples/load-balancing/main.bicep b/samples/load-balancing/main.bicep index 599b29c..1900ddf 100644 --- a/samples/load-balancing/main.bicep +++ b/samples/load-balancing/main.bicep @@ -197,7 +197,7 @@ module backendPoolModule4 '../../shared/bicep/modules/apim/v1/backend-pool.bicep // APIM APIs module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [ for api in apis: if (!empty(apis)) { - name: '${api.name}-${resourceSuffix}' + name: 'api-${api.name}' params: { apimName: apimName appInsightsInstrumentationKey: appInsightsInstrumentationKey diff --git a/shared/apim-policies/aca-backend-2.xml b/shared/apim-policies/aca-backend-2.xml deleted file mode 100644 index 4c86d0b..0000000 --- a/shared/apim-policies/aca-backend-2.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/shared/apim-policies/aca-backend-pool.xml b/shared/apim-policies/aca-backend-pool.xml deleted file mode 100644 index 49a4e9f..0000000 --- a/shared/apim-policies/aca-backend-pool.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/shared/apim-policies/aca-backend-1.xml b/shared/apim-policies/backend.xml similarity index 83% rename from shared/apim-policies/aca-backend-1.xml rename to shared/apim-policies/backend.xml index 03e81ec..1da050d 100644 --- a/shared/apim-policies/aca-backend-1.xml +++ b/shared/apim-policies/backend.xml @@ -4,7 +4,7 @@ - + diff --git a/shared/apim-policies/fragments/pf-authz-match-all.xml b/shared/apim-policies/fragments/pf-authz-match-all.xml new file mode 100644 index 0000000..5711225 --- /dev/null +++ b/shared/apim-policies/fragments/pf-authz-match-all.xml @@ -0,0 +1,26 @@ + + + + + ("authz_roles", "").ToString().Split(','); + var jwtRoles = jwt.Claims.GetValueOrDefault("roles", new string[0]); + + // Check if ALL required role GUIDs are present in the JWT + return !requiredRoleGuids.All(requiredRole => jwtRoles.Contains(requiredRole.Trim())); + }"> + + + Access denied - not all required roles found + + + + + diff --git a/shared/apim-policies/fragments/pf-authz-match-any.xml b/shared/apim-policies/fragments/pf-authz-match-any.xml index de4899c..79a0a64 100644 --- a/shared/apim-policies/fragments/pf-authz-match-any.xml +++ b/shared/apim-policies/fragments/pf-authz-match-any.xml @@ -3,8 +3,7 @@ - "jwt": The parsed JWT token which should already be set by the pf-authx fragment. - "authz_roles": A csv of allowed role GUIDs to check against. Any match will grant access. - - This fragment only blocks access (returns 403) when no roles match. If roles match, processing -continues normally. + - This fragment only blocks access (returns 403) when no roles match. If roles match, processing continues normally. --> diff --git a/shared/bicep/modules/apim/v1/api.bicep b/shared/bicep/modules/apim/v1/api.bicep index b2a92e3..da6980d 100644 --- a/shared/bicep/modules/apim/v1/api.bicep +++ b/shared/bicep/modules/apim/v1/api.bicep @@ -149,11 +149,24 @@ resource apiDiagnostics 'Microsoft.ApiManagement/service/apis/diagnostics@2024-0 } } } +// Product associations are handled directly in this module with proper dependency management // to prevent race conditions while keeping the architecture simple +// Reference existing products for association (with explicit dependency timing) +resource apimProducts 'Microsoft.ApiManagement/service/products@2024-06-01-preview' existing = [for productName in productNames: { + name: productName + parent: apimService +}] + +// Create product-API associations with proper dependency management // https://learn.microsoft.com/azure/templates/microsoft.apimanagement/service/products/apis -resource apiProductAssociation 'Microsoft.ApiManagement/service/products/apis@2024-05-01' = [for productName in productNames: { - name: '${apimName}/${productName}/${api.name}' +resource apiProductAssociation 'Microsoft.ApiManagement/service/products/apis@2024-06-01-preview' = [for (productName, index) in productNames: { + name: apimApi.name + parent: apimProducts[index] dependsOn: [ - apimApi + apimProducts[index] // Ensure the specific product exists and is ready + apiPolicy // Ensure API policy is applied if present + apiOperation // Ensure all operations are created + apiOperationPolicy // Ensure all operation policies are applied + apiDiagnostics // Ensure diagnostics are configured if present ] }] @@ -174,10 +187,10 @@ output apiDisplayName string = apimApi.properties.displayName @description('The path of the created API.') output apiPath string = apimApi.properties.path -// @description('Array of product names this API is associated with.') -// output associatedProducts string[] = productNames +@description('Array of product names this API is associated with.') +output associatedProducts array = productNames -// @description('Number of products this API is associated with.') -// output productAssociationCount int = length(productNames) +@description('Number of products this API is associated with.') +output productAssociationCount int = length(productNames) //output subscriptionPrimaryKey string = apimSubscription.listSecrets().primaryKey diff --git a/shared/python/apimtypes.py b/shared/python/apimtypes.py index 8850acd..e608856 100644 --- a/shared/python/apimtypes.py +++ b/shared/python/apimtypes.py @@ -16,9 +16,7 @@ DEFAULT_XML_POLICY_PATH = f'{SHARED_XML_POLICY_BASE_PATH}/default.xml' HELLO_WORLD_XML_POLICY_PATH = f'{SHARED_XML_POLICY_BASE_PATH}/hello-world.xml' REQUEST_HEADERS_XML_POLICY_PATH = f'{SHARED_XML_POLICY_BASE_PATH}/request-headers.xml' -ACA_BACKEND_1_XML_POLICY_PATH = f'{SHARED_XML_POLICY_BASE_PATH}/aca-backend-1.xml' -ACA_BACKEND_2_XML_POLICY_PATH = f'{SHARED_XML_POLICY_BASE_PATH}/aca-backend-2.xml' -ACA_BACKEND_POOL_XML_POLICY_PATH = f'{SHARED_XML_POLICY_BASE_PATH}/aca-backend-pool.xml' +BACKEND_XML_POLICY_PATH = f'{SHARED_XML_POLICY_BASE_PATH}/backend.xml' SUBSCRIPTION_KEY_PARAMETER_NAME = 'api_key' SLEEP_TIME_BETWEEN_REQUESTS_MS = 50 diff --git a/tests/python/test_apimrequests.py b/tests/python/test_apimrequests.py index 60f5bda..f427004 100644 --- a/tests/python/test_apimrequests.py +++ b/tests/python/test_apimrequests.py @@ -1,8 +1,8 @@ import pytest import requests from unittest.mock import patch, MagicMock -from shared.python.apimrequests import ApimRequests -from shared.python.apimtypes import HTTP_VERB, SUBSCRIPTION_KEY_PARAMETER_NAME +from apimrequests import ApimRequests +from apimtypes import SUBSCRIPTION_KEY_PARAMETER_NAME # Sample values for tests default_url = "https://example.com/apim/" @@ -35,10 +35,10 @@ def test_init_no_key(): assert apim.headers["Accept"] == "application/json" @pytest.mark.http -@patch("shared.python.apimrequests.requests.request") -@patch("shared.python.apimrequests.utils.print_message") -@patch("shared.python.apimrequests.utils.print_info") -@patch("shared.python.apimrequests.utils.print_error") +@patch("apimrequests.requests.request") +@patch("apimrequests.utils.print_message") +@patch("apimrequests.utils.print_info") +@patch("apimrequests.utils.print_error") def test_single_get_success(mock_print_error, mock_print_info, mock_print_message, mock_request, apim): mock_response = MagicMock() mock_response.status_code = 200 @@ -55,10 +55,10 @@ def test_single_get_success(mock_print_error, mock_print_info, mock_print_messag mock_print_error.assert_not_called() @pytest.mark.http -@patch("shared.python.apimrequests.requests.request") -@patch("shared.python.apimrequests.utils.print_message") -@patch("shared.python.apimrequests.utils.print_info") -@patch("shared.python.apimrequests.utils.print_error") +@patch("apimrequests.requests.request") +@patch("apimrequests.utils.print_message") +@patch("apimrequests.utils.print_info") +@patch("apimrequests.utils.print_error") def test_single_get_error(mock_print_error, mock_print_info, mock_print_message, mock_request, apim): mock_request.side_effect = requests.exceptions.RequestException("fail") result = apim.singleGet(default_path, printResponse=True) @@ -66,10 +66,10 @@ def test_single_get_error(mock_print_error, mock_print_info, mock_print_message, mock_print_error.assert_called_once() @pytest.mark.http -@patch("shared.python.apimrequests.requests.request") -@patch("shared.python.apimrequests.utils.print_message") -@patch("shared.python.apimrequests.utils.print_info") -@patch("shared.python.apimrequests.utils.print_error") +@patch("apimrequests.requests.request") +@patch("apimrequests.utils.print_message") +@patch("apimrequests.utils.print_info") +@patch("apimrequests.utils.print_error") def test_single_post_success(mock_print_error, mock_print_info, mock_print_message, mock_request, apim): mock_response = MagicMock() mock_response.status_code = 201 @@ -86,9 +86,9 @@ def test_single_post_success(mock_print_error, mock_print_info, mock_print_messa mock_print_error.assert_not_called() @pytest.mark.http -@patch("shared.python.apimrequests.requests.Session") -@patch("shared.python.apimrequests.utils.print_message") -@patch("shared.python.apimrequests.utils.print_info") +@patch("apimrequests.requests.Session") +@patch("apimrequests.utils.print_message") +@patch("apimrequests.utils.print_info") def test_multi_get_success(mock_print_info, mock_print_message, mock_session, apim): mock_sess = MagicMock() mock_response = MagicMock() @@ -110,9 +110,9 @@ def test_multi_get_success(mock_print_info, mock_print_message, mock_session, ap mock_print_code.assert_called() @pytest.mark.http -@patch("shared.python.apimrequests.requests.Session") -@patch("shared.python.apimrequests.utils.print_message") -@patch("shared.python.apimrequests.utils.print_info") +@patch("apimrequests.requests.Session") +@patch("apimrequests.utils.print_message") +@patch("apimrequests.utils.print_info") def test_multi_get_error(mock_print_info, mock_print_message, mock_session, apim): mock_sess = MagicMock() mock_sess.request.side_effect = requests.exceptions.RequestException("fail") @@ -134,8 +134,8 @@ def make_apim(): @pytest.mark.http def test_single_post_error(): apim = make_apim() - with patch("shared.python.apimrequests.requests.request") as mock_request, \ - patch("shared.python.apimrequests.utils.print_error") as mock_print_error: + with patch("apimrequests.requests.request") as mock_request, \ + patch("apimrequests.utils.print_error") as mock_print_error: import requests mock_request.side_effect = requests.RequestException("fail") result = apim.singlePost(path, data={"foo": "bar"}, printResponse=True) @@ -145,7 +145,7 @@ def test_single_post_error(): @pytest.mark.http def test_multi_get_non_json(): apim = make_apim() - with patch("shared.python.apimrequests.requests.Session") as mock_session: + with patch("apimrequests.requests.Session") as mock_session: mock_sess = MagicMock() mock_response = MagicMock() mock_response.status_code = 200 @@ -161,7 +161,7 @@ def test_multi_get_non_json(): @pytest.mark.http def test_request_header_merging(): apim = make_apim() - with patch("shared.python.apimrequests.requests.request") as mock_request: + with patch("apimrequests.requests.request") as mock_request: mock_response = MagicMock() mock_response.status_code = 200 mock_response.headers = {"Content-Type": "application/json"} @@ -189,7 +189,7 @@ def test_print_response_code_edge(): class DummyResponse: status_code = 302 reason = "Found" - with patch("shared.python.apimrequests.utils.print_val") as mock_print_val: + with patch("apimrequests.utils.print_val") as mock_print_val: apim._print_response_code(DummyResponse()) mock_print_val.assert_called_with("Response status", "302") diff --git a/tests/python/test_apimtypes.py b/tests/python/test_apimtypes.py index ffa0a74..63e7bb7 100644 --- a/tests/python/test_apimtypes.py +++ b/tests/python/test_apimtypes.py @@ -1,8 +1,9 @@ """ Unit tests for apimtypes.py. """ + import pytest -from shared.python import apimtypes +import apimtypes # ------------------------------ @@ -279,14 +280,6 @@ def test_api_missing_fields(): policyXml = EXAMPLE_POLICY_XML ) - # with pytest.raises(TypeError): - # apimtypes.API( - # name = EXAMPLE_NAME, - # displayName = EXAMPLE_DISPLAY_NAME, - # path = EXAMPLE_PATH, - # description = EXAMPLE_DESCRIPTION - # ) - # ------------------------------ # ENUMS diff --git a/tests/python/test_utils.py b/tests/python/test_utils.py index b94ed4e..1226c67 100644 --- a/tests/python/test_utils.py +++ b/tests/python/test_utils.py @@ -2,9 +2,8 @@ from apimtypes import INFRASTRUCTURE import os import builtins -from io import StringIO -from unittest.mock import patch, MagicMock, mock_open -from shared.python import utils +from unittest.mock import MagicMock, mock_open +import utils from apimtypes import INFRASTRUCTURE # ------------------------------