pytest-nhsd-apim 3.3.15__py3-none-any.whl → 3.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pytest-nhsd-apim might be problematic. Click here for more details.

@@ -476,7 +476,7 @@ class DeveloperAppsAPI:
476
476
  resp = self.client.delete(url=url)
477
477
  if resp.status_code != 200:
478
478
  raise Exception(
479
- f"GET request to {resp.url} failed with status_code: {resp.status_code}, Reason: {resp.reason} and Content: {resp.text}"
479
+ f"DELETE request to {resp.url} failed with status_code: {resp.status_code}, Reason: {resp.reason} and Content: {resp.text}"
480
480
  )
481
481
  return resp.json()
482
482
 
@@ -1241,11 +1241,218 @@ class UserRolesAPI:
1241
1241
 
1242
1242
 
1243
1243
  class AppKeysAPI:
1244
+ """
1245
+ Manage consumer credentials for apps associated with individual developers.
1246
+
1247
+ Credential pairs consisting of consumer key and consumer secret are provisioned
1248
+ by Apigee Edge to apps for specific API products. Apigee Edge maintains the
1249
+ relationship between consumer keys and API products, enabling API products to be
1250
+ added to and removed from consumer keys. A single consumer key can be used to
1251
+ access multiple API products. Keys may be manually or automatically approved for
1252
+ API products--how they are issued depends on the API product configuration. A key
1253
+ must approved and approved for an API product to be capable of accessing any of
1254
+ the URIs defined in the API product.
1255
+ """
1256
+
1244
1257
  def __init__(self, client: RestClient) -> None:
1245
1258
  self.client = client
1246
- raise NotImplementedError(
1247
- f"Ugh! this is awkward, this API is not available yet...feel free to give us a shout or to open a PR https://github.com/NHSDigital/pytest-nhsd-apim/blob/0cf274850a8fe61e17f214380496ba09fd6cc973/src/pytest_nhsd_apim/apigee_apis.py#L1142"
1248
- )
1259
+
1260
+ def create_app_key(self, email: str, app_name: str, body: dict) -> "dict":
1261
+ """
1262
+ Creates a custom consumer key and secret for a developer app.
1263
+ This is particularly useful if you want to migrate existing consumer
1264
+ keys/secrets to Edge from another system.
1265
+
1266
+ After creating the consumer key and secret, associate the key with an
1267
+ API product, as described in Add API Product to Key.
1268
+
1269
+ Consumer keys and secrets can contain letters, numbers, underscores,
1270
+ and hyphens. No other special characters are allowed.
1271
+
1272
+ Note: Be aware of the following size limits on API keys. By staying
1273
+ within these limits, you help avoid service disruptions.
1274
+
1275
+ - Consumer key (API key) size: 2 KB
1276
+
1277
+ - Consumer secret size: 2 KB
1278
+
1279
+ If a consumer key and secret already exist, you can either keep them
1280
+ or delete them, as described in Delete Key for a Developer App.
1281
+
1282
+ In addition, you can use this API if you have existing API keys and
1283
+ secrets that you want to copy into Edge from another system. For more
1284
+ information, see Import existing consumer keys and secrets.
1285
+ """
1286
+
1287
+ resource = f"/developers/{email}/apps/{app_name}/keys/create"
1288
+ url = f"{self.client.base_url}{resource}"
1289
+ resp = self.client.post(url=url, json=body)
1290
+ if resp.status_code != 201:
1291
+ raise Exception(
1292
+ f"POST request to {resp.url} failed with status_code: {resp.status_code}, Reason: {resp.reason} and Content: {resp.text}"
1293
+ )
1294
+ return resp.json()
1295
+
1296
+ def delete_app_key(self, email: str, app_name: str, app_key: str) -> None:
1297
+ """
1298
+ Deletes a consumer key that belongs to an app, and removes all API products
1299
+ associated with the app. Once deleted, the consumer key cannot be used
1300
+ to access any APIs.
1301
+
1302
+ After you delete a consumer key, you may want to:
1303
+
1304
+ - Create a new consumer key and secret for the developer app, and
1305
+ subsequently add an API product to the key.
1306
+
1307
+ - Delete the developer app, if it is no longer required.
1308
+ """
1309
+
1310
+ resource = f"/developers/{email}/apps/{app_name}/keys/{app_key}"
1311
+ url = f"{self.client.base_url}{resource}"
1312
+ resp = self.client.delete(url=url)
1313
+ if resp.status_code != 200:
1314
+ raise Exception(
1315
+ f"DELETE request to {resp.url} failed with status_code: {resp.status_code}, Reason: {resp.reason} and Content: {resp.text}"
1316
+ )
1317
+ return resp.json()
1318
+
1319
+ def get_app_key(self, email: str, app_name: str, key: str, **query_params) -> "list[str]":
1320
+ """
1321
+ Gets details for a consumer key for a developer app, including the key
1322
+ and secret value, associated API products, and other information.
1323
+ """
1324
+
1325
+ params = query_params
1326
+ resource = f"/developers/{email}/apps/{app_name}/keys/{key}"
1327
+ url = f"{self.client.base_url}{resource}"
1328
+ resp = self.client.get(url=url, params=params)
1329
+ if resp.status_code != 200:
1330
+ raise Exception(
1331
+ f"GET request to {resp.url} failed with status_code: {resp.status_code}, Reason: {resp.reason} and Content: {resp.text}"
1332
+ )
1333
+ return resp.json()
1334
+
1335
+ def post_app_key(self, email: str, app_name: str, key: str, body: dict, **query_params) -> "dict":
1336
+ """
1337
+ Enables you to perform one of the following tasks:
1338
+
1339
+ - Add an API product to a developer app key, enabling the app that holds
1340
+ the key to access the API resources bundled in the API product. You can
1341
+ also use this API to add attributes to the key. You must include all existing
1342
+ attributes, whether or not you are updating them, as well as any new attributes
1343
+ that you are adding. After adding the API product, you can use the same key
1344
+ to access all API products associated with the app.
1345
+
1346
+ - Approve or revoke a specific consumer key for an app. Call the API with the
1347
+ action query parameter set to approve or revoke (with no request body) and
1348
+ set the Content-type header to application/octet-stream. If successful, the HTTP
1349
+ status code for success is: 204 No Content
1350
+
1351
+ - You can approve a consumer key that is currently revoked or pending. Once
1352
+ approved, the app can use the consumer key to access APIs. Revoking a consumer
1353
+ key renders it unusable for the app to use to access an API.
1354
+
1355
+ - Note: Any access tokens associated with a revoked app key will remain active.
1356
+ However, Apigee Edge checks the status of the app key and if set to revoked it
1357
+ will not allow API calls to go through.
1358
+ """
1359
+
1360
+ resource = f"/developers/{email}/apps/{app_name}/keys/{key}"
1361
+ url = f"{self.client.base_url}{resource}"
1362
+ resp = self.client.post(url=url, json=body, params=query_params)
1363
+ if resp.status_code != 200 and resp.status_code != 204:
1364
+ raise Exception(
1365
+ f"POST request to {resp.url} failed with status_code: {resp.status_code}, Reason: {resp.reason} and Content: {resp.text}"
1366
+ )
1367
+ if resp.status_code == 204:
1368
+ return resp
1369
+ else:
1370
+ return resp.json()
1371
+
1372
+ def put_app_key(self, email: str, app_name: str, key: str, body: dict) -> "dict":
1373
+ """
1374
+ Updates the allowed OAuth scopes associated with an app.
1375
+
1376
+ Note: Specify the complete list of scopes to apply. The specified list replaces
1377
+ the existing scopes on the app. Therefore, to add a scope, you must specify all
1378
+ of the existing scopes along with the added scope.
1379
+
1380
+ This API does not change the list of scopes in the API product(s) included in
1381
+ the app; rather, it sets allowed list of scopes in the scopes element under the
1382
+ apiProducts element in the attributes of the app.
1383
+
1384
+ Important: The specified scopes must already exist on the API product(s)
1385
+ associated with the app. You can't arbitrarily add a scope that does not already
1386
+ exist in an API product. For example, if the app has one API product with these
1387
+ scopes: READ, WRITE. You can't use this API to add a new scope, such as DELETE
1388
+ (unless the app has another product with that scope). If you do this, you'll get
1389
+ a 400 Bad Request error. For example:
1390
+
1391
+ {
1392
+ "code": "keymanagement.service.InvalidScopes",
1393
+ "message": "Invalid scopes. Scopes must be contained in [READ, WRITE]",
1394
+ "contexts": []
1395
+
1396
+ }
1397
+
1398
+ It would be allowed to remove one or both of the existing scopes, and later add
1399
+ one or both back.
1400
+ """
1401
+
1402
+ resource = f"/developers/{email}/apps/{app_name}/keys/{key}"
1403
+ url = f"{self.client.base_url}{resource}"
1404
+ resp = self.client.put(url=url, json=body)
1405
+ if resp.status_code != 200:
1406
+ raise Exception(
1407
+ f"PUT request to {resp.url} failed with status_code: {resp.status_code}, Reason: {resp.reason} and Content: {resp.text}"
1408
+ )
1409
+ return resp.json()
1410
+
1411
+ def delete_product_app_key_association(self, email: str, app_name: str, app_key: str, apiproduct_name: str) -> None:
1412
+ """
1413
+ Removes an API product from an app's consumer key, and thereby renders the app
1414
+ unable to access the API resources defined in that API product.
1415
+
1416
+ Note that the consumer key itself still exists after this call. Only the
1417
+ association of the key with the API product is removed.
1418
+ """
1419
+
1420
+ resource = f"/developers/{email}/apps/{app_name}/keys/{app_key}/apiproducts/{apiproduct_name}"
1421
+ url = f"{self.client.base_url}{resource}"
1422
+ resp = self.client.delete(url=url)
1423
+ if resp.status_code != 200:
1424
+ raise Exception(
1425
+ f"DELETE request to {resp.url} failed with status_code: {resp.status_code}, Reason: {resp.reason} and Content: {resp.text}"
1426
+ )
1427
+ return resp.json()
1428
+
1429
+ def post_product_app_key_association(self, email: str, app_name: str, key: str, apiproduct_name: str, **query_params) -> "dict":
1430
+ """
1431
+ Approves or revokes an API product for an API key. Call the API with the action
1432
+ query parameter set to approve or revoke (with no request body) and set the
1433
+ Content-type header to application/octet-stream. If successful, the HTTP status
1434
+ code for success is: 204 No Content
1435
+
1436
+ To consume API resources defined in an API product, an app's consumer key must
1437
+ be approved and it must also be approved for that specific API product.
1438
+
1439
+ Notes:
1440
+
1441
+ - The API product must already be associated with the app.
1442
+
1443
+ - Any access tokens associated with a revoked app key will remain active. However,
1444
+ Apigee Edge checks the status of the app key and if set to revoked it will not
1445
+ allow API calls to go through.
1446
+ """
1447
+
1448
+ resource = f"/developers/{email}/apps/{app_name}/keys/{key}/apiproducts/{apiproduct_name}"
1449
+ url = f"{self.client.base_url}{resource}"
1450
+ resp = self.client.post(url=url, params=query_params)
1451
+ if resp.status_code != 204:
1452
+ raise Exception(
1453
+ f"POST request to {resp.url} failed with status_code: {resp.status_code}, Reason: {resp.reason} and Content: {resp.text}"
1454
+ )
1455
+ return resp
1249
1456
 
1250
1457
 
1251
1458
  class UsersAPI:
@@ -18,6 +18,7 @@ from .log import log, log_method
18
18
  from .apigee_apis import (
19
19
  ApigeeNonProdCredentials,
20
20
  ApigeeClient,
21
+ AppKeysAPI,
21
22
  DebugSessionsAPI,
22
23
  AccessTokensAPI,
23
24
  ApiProductsAPI,
@@ -583,3 +584,13 @@ def developer_apps_api():
583
584
  config = ApigeeNonProdCredentials()
584
585
  client = ApigeeClient(config=config)
585
586
  return DeveloperAppsAPI(client=client)
587
+
588
+ @pytest.fixture(scope="session")
589
+ @log_method
590
+ def developer_app_keys_api():
591
+ """
592
+ Authenitcated wrapper for Apigee's developer app keys API
593
+ """
594
+ config = ApigeeNonProdCredentials()
595
+ client = ApigeeClient(config=config)
596
+ return AppKeysAPI(client=client)
@@ -48,7 +48,8 @@ from .apigee_edge import (
48
48
  trace,
49
49
  products_api,
50
50
  access_token_api,
51
- developer_apps_api
51
+ developer_apps_api,
52
+ developer_app_keys_api
52
53
  )
53
54
  from .auth_journey import (
54
55
  _jwt_keys,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytest-nhsd-apim
3
- Version: 3.3.15
3
+ Version: 3.4.1
4
4
  Summary: Pytest plugin accessing NHSDigital's APIM proxies
5
5
  Home-page: https://github.com/NHSDigital/pytest-nhsd-apim
6
6
  Author: Adrian Ciobanita
@@ -11,7 +11,7 @@ License: MIT
11
11
  Classifier: Framework :: Pytest
12
12
  Requires-Python: >=3.8
13
13
  Description-Content-Type: text/markdown
14
- Requires-Dist: Authlib (==0.15.5)
14
+ Requires-Dist: Authlib (==1.3.1)
15
15
  Requires-Dist: cryptography (==42.0.0)
16
16
  Requires-Dist: lxml (==4.9.1)
17
17
  Requires-Dist: pycryptodome (==3.20.0)
@@ -99,6 +99,7 @@ The APIs we offer at the moment are:
99
99
  | ApiProductsAPI | [here](/src/pytest_nhsd_apim/apigee_apis.py#L575) |[Overview](https://apidocs.apigee.com/docs/api-products/1/overview)|
100
100
  | DebugSessionsAPI | [here](/src/pytest_nhsd_apim/apigee_apis.py#L844) |[Overview](https://apidocs.apigee.com/docs/debug-sessions/1/overview)|
101
101
  | AccessTokensAPI | [here](/src/pytest_nhsd_apim/apigee_apis.py#L983) |[Overview](https://apidocs.apigee.com/docs/oauth-20-access-tokens/1/overview)|
102
+ | AppKeysAPI | [here](/src/pytest_nhsd_apim/apigee_apis.py#L1243) |[Overview](https://apidocs.apigee.com/docs/developer-app-keys/1/overview)|
102
103
 
103
104
  For a more detailed implementation of the available APIs please refer to the tests [here](/tests/test_apigee_apis.py).
104
105
  We will keep adding APIs with time, if you are looking for a particular APIs not listed above please feel free to open a pull request and send it to us.
@@ -0,0 +1,16 @@
1
+ pytest_nhsd_apim/__init__.py,sha256=CRwQfUDrbXIqvJX6OfTR4jGdhlU9kjGmBAptJasRg7E,72
2
+ pytest_nhsd_apim/apigee_apis.py,sha256=tcAo254Mvx__qAX7Focb-6855eTLWL2TUsna1vT6Eow,67284
3
+ pytest_nhsd_apim/apigee_edge.py,sha256=NQrW8PeA_-X4zt2torTmBOaFIzRKjh6pT1ksolNjpmA,19065
4
+ pytest_nhsd_apim/auth_journey.py,sha256=UovbLXhokUnikrMOaXIhjV8t5aRrcxinAbS96nfZWcY,5154
5
+ pytest_nhsd_apim/config.py,sha256=ScKfV8iURqDXX2ajgGsRDcVn9RZy2DxLoEU2QQt9lmA,4246
6
+ pytest_nhsd_apim/identity_service.py,sha256=HrXfSm0dIFg51LBng_nf2iIGlOcwzV_J1vA4agm_Ne4,19766
7
+ pytest_nhsd_apim/log.py,sha256=8gYHqzlQM546FB2XvFmLTk1YfZRNeNhIwLmOy0GScr8,2685
8
+ pytest_nhsd_apim/nhsd_apim_authorization.py,sha256=TE_6YnoM7kpRReiZP6-Z52rRs9bUuax1mpXqkbzg8zQ,3505
9
+ pytest_nhsd_apim/pytest_nhsd_apim.py,sha256=ZCItUqcM23CCmcRyGU8bEwI3vJnNcGdoOlbSfvYILR8,5242
10
+ pytest_nhsd_apim/secrets.py,sha256=yIYwOZwpliIomtqSJGIYRbAE2HYvLvQU4W2kOa9TnXo,2354
11
+ pytest_nhsd_apim/token_cache.py,sha256=6L08taTlSyRsx2NCb0LSGsHdWx_wmqwlFtcF7pZMhUg,3540
12
+ pytest_nhsd_apim-3.4.1.dist-info/METADATA,sha256=XbxWjUsREhPEjML0PHDDJIG2v2YNQER3-kmxmYmxNps,4660
13
+ pytest_nhsd_apim-3.4.1.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
14
+ pytest_nhsd_apim-3.4.1.dist-info/entry_points.txt,sha256=XWicT3meTpqLXnZcXNoAd2IfXspFPlNgMgLBMy4nqwQ,57
15
+ pytest_nhsd_apim-3.4.1.dist-info/top_level.txt,sha256=ZK5GZP-g_K8gNfm4a58T9JCRb0i1X267ngvNelCGgfQ,17
16
+ pytest_nhsd_apim-3.4.1.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- pytest_nhsd_apim/__init__.py,sha256=CRwQfUDrbXIqvJX6OfTR4jGdhlU9kjGmBAptJasRg7E,72
2
- pytest_nhsd_apim/apigee_apis.py,sha256=nVarxYSD1TIjCduBQyW8LIODyPk76xpELXD2lhyWLHc,57427
3
- pytest_nhsd_apim/apigee_edge.py,sha256=sIITiwkyKoNVDJ9OmnxV3mipF6LJR0w2qpmvT5Mwr1M,18777
4
- pytest_nhsd_apim/auth_journey.py,sha256=UovbLXhokUnikrMOaXIhjV8t5aRrcxinAbS96nfZWcY,5154
5
- pytest_nhsd_apim/config.py,sha256=ScKfV8iURqDXX2ajgGsRDcVn9RZy2DxLoEU2QQt9lmA,4246
6
- pytest_nhsd_apim/identity_service.py,sha256=HrXfSm0dIFg51LBng_nf2iIGlOcwzV_J1vA4agm_Ne4,19766
7
- pytest_nhsd_apim/log.py,sha256=8gYHqzlQM546FB2XvFmLTk1YfZRNeNhIwLmOy0GScr8,2685
8
- pytest_nhsd_apim/nhsd_apim_authorization.py,sha256=TE_6YnoM7kpRReiZP6-Z52rRs9bUuax1mpXqkbzg8zQ,3505
9
- pytest_nhsd_apim/pytest_nhsd_apim.py,sha256=_snJGTUVsHA70FGrKNNXjx9yNc_UjO1Ffhw0SLQtYUs,5214
10
- pytest_nhsd_apim/secrets.py,sha256=yIYwOZwpliIomtqSJGIYRbAE2HYvLvQU4W2kOa9TnXo,2354
11
- pytest_nhsd_apim/token_cache.py,sha256=6L08taTlSyRsx2NCb0LSGsHdWx_wmqwlFtcF7pZMhUg,3540
12
- pytest_nhsd_apim-3.3.15.dist-info/METADATA,sha256=y8LIwibo_Ulh9ouBI3RFahR7U7k1xIHnd9h7v_DvDI8,4518
13
- pytest_nhsd_apim-3.3.15.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
14
- pytest_nhsd_apim-3.3.15.dist-info/entry_points.txt,sha256=XWicT3meTpqLXnZcXNoAd2IfXspFPlNgMgLBMy4nqwQ,57
15
- pytest_nhsd_apim-3.3.15.dist-info/top_level.txt,sha256=ZK5GZP-g_K8gNfm4a58T9JCRb0i1X267ngvNelCGgfQ,17
16
- pytest_nhsd_apim-3.3.15.dist-info/RECORD,,