Skip to content

Commit 783bfa6

Browse files
authored
Merge pull request #407 from NHSDigital/release/2024-11-13
Release/2024-11-13
2 parents d87e45f + a83fbc4 commit 783bfa6

File tree

67 files changed

+2021
-1699
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+2021
-1699
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 2024-11-13
4+
- [PI-617] Stream bulk LDIF into blocks of party key
5+
- [PI-527] Swagger fix
6+
- [PI-592] GSI Upgrade
7+
- [PI-590] Read/write by alias
8+
39
## 2024-11-08
410
- [PI-508] Search DeviceReferenceData
511
- [PI-578] Create MHS Device

README.md

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@
1010
3. [AWS SSO Setup](#aws-sso-setup)
1111
4. [Other helpful commands](#other-helpful-commands)
1212
2. [Tests](#tests)
13-
3. [pytest tests](#pytest-tests)
14-
4. [End-to-End feature tests](#end-to-end-feature-tests)
15-
5. [Generate the Feature Test Postman collection](#generate-the-feature-test-postman-collection)
16-
6. [Workflow](#workflow)
17-
7. [Swagger](#swagger)
18-
8. [ETL](#etl)
13+
1. [pytest tests](#pytest-tests)
14+
2. [End-to-End feature tests](#end-to-end-feature-tests)
15+
3. [Generate the Feature Test Postman collection](#generate-the-feature-test-postman-collection)
16+
3. [Data modelling](#data-modelling)
17+
1. [Domain models](#domain-models)
18+
2. [Database models](#database-models)
19+
3. [Response models](#response-models)
20+
4. [Request models](#request-models)
21+
4. [Workflow](#workflow)
22+
5. [Swagger](#swagger)
23+
6. [ETL](#etl)
1924

2025
---
2126

@@ -273,6 +278,53 @@ in the environment (but these are filled out already if generated with the integ
273278

274279
💡 **The feature tests are only guaranteed to work out-of-the-box with an empty database**
275280

281+
## Data modelling
282+
283+
Modelling in Connecting Party Manager is split into four partially-decoupled components:
284+
285+
- Domain models: The conceptual entities of Connecting Party Manager, without any reference to database indexing, and request / response syntax.
286+
- Database models: A wrapper on the domain models that we persist to database (DynamoDB), indicating write-integrity (primary keys) and a read/search interface (indexing).
287+
- Response models: Deviations from the domain model (error handling, search result wrapping, private field exclusion, etc)
288+
- Request models: API-specific models indicating the expected request bodies of create/update/search operations.
289+
290+
### Domain models
291+
292+
TBC
293+
294+
### Database models
295+
296+
#### Write-integrity (primary keys)
297+
298+
The Partition Key (`pk`) that we use for all objects\* in the database is a unique combination of prefix (aligned with the object type, e.g. `D#` for `Device`) and identifier (generally a UUID). The Sort Key (`sk`) that we use is always exactly equal to the Partition Key. This is opposed to having fully denormalised objects so that attributes are nested under their own `sk`. The reason for doing this is to limit multiple read operations for a given object, and also save I/O in our ETL process by reducing the number of database transactions required per object.
299+
300+
Objects can additionally be indexed by any keys (see [Domain models](#domain-models)) that they have. For every key in an domain object,
301+
a copy is made in the database with the index being that key, rather
302+
than the object's identifier. Such copies are referred to as non-root
303+
objects, whereas the "original" (indexed by identifier) is referred to
304+
as the root object.
305+
306+
\* In the case of `Device` tags, which sit outside of the standard database model, `pk` is equal to a query string and `sk` is equal to `pk` of the object that is referred to. A tag-indexed `Device` is otherwise a copy of the root `Device`.
307+
308+
#### Read/search interface
309+
310+
We have implemented a Global Secondary Index (GSI) for attributes named `pk_read` and `sk_read`. The pattern that is place is as follows:
311+
312+
- `pk_read`: A concatenation of parent `pk` values (joined with `#`, e.g. `PT#<product_team_id>#P<product_id>`)
313+
- `sk_read`: Equal to the `pk` of the object itself.
314+
315+
We refer to this as an "ownership" model, as it allows for reads to be
316+
executed in a way that mirrors the API read operations (GET `grandparent/parent/child`), whilst also giving us the ability to return all objects owned by the object indicated in the `pk_read` - which is a common operation for us.
317+
318+
A `read` and `search` is available on all `Repository` patterns (almost) for free (the base `_read` and `_search` require a shallow wrapper, but most of the work is done for you).
319+
320+
### Response models
321+
322+
TBC
323+
324+
### Request models
325+
326+
TBC
327+
276328
## Workflow
277329

278330
In order to create new branches, use the commands listed below. Note that the commands will throw an error if

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2024.11.08
1+
2024.11.13

changelog/2024-11-13.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- [PI-617] Stream bulk LDIF into blocks of party key
2+
- [PI-527] Swagger fix
3+
- [PI-592] GSI Upgrade
4+
- [PI-590] Read/write by alias

infrastructure/swagger/08_components--schemas--other.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ components:
33
schemas:
44
HeaderVersion:
55
type: string
6-
pattern: "^[1-9][0-9]+$"
6+
pattern: "^[1-9][0-9]?(\\.[0-9])?$"
77

88
HeaderRequestId:
99
type: string

infrastructure/terraform/per_workspace/main.tf

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,15 @@ module "table" {
4343
attributes = [
4444
{ name = "pk", type = "S" },
4545
{ name = "sk", type = "S" },
46-
{ name = "pk_1", type = "S" },
47-
{ name = "sk_1", type = "S" },
48-
{ name = "pk_2", type = "S" },
49-
{ name = "sk_2", type = "S" }
46+
{ name = "pk_read", type = "S" },
47+
{ name = "sk_read", type = "S" },
5048
]
5149

5250
global_secondary_indexes = [
5351
{
54-
name = "idx_gsi_1"
55-
hash_key = "pk_1"
56-
range_key = "sk_1"
57-
projection_type = "ALL"
58-
},
59-
{
60-
name = "idx_gsi_2"
61-
hash_key = "pk_2"
62-
range_key = "sk_2"
52+
name = "idx_gsi_read"
53+
hash_key = "pk_read"
54+
range_key = "sk_read"
6355
projection_type = "ALL"
6456
}
6557
]

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "connecting-party-manager"
3-
version = "2024.11.08"
3+
version = "2024.11.13"
44
description = "Repository for the Connecting Party Manager API and related services"
55
authors = ["NHS England"]
66
license = "LICENSE.md"

src/api/createCpmProductForEpr/tests/test_index.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def test_index():
6666
table_name=TABLE_NAME, dynamodb_client=index.cache["DYNAMODB_CLIENT"]
6767
)
6868
read_product = repo.read(
69-
product_team_id=product_team.id, product_id=created_product["id"]
69+
product_team_id=product_team.id, id=created_product["id"]
7070
).state()
7171

7272
assert created_product == read_product

src/api/createDevice/tests/test_index.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,19 @@ def test_index() -> None:
8383
assert device.name == DEVICE_NAME
8484
assert device.ods_code == ODS_CODE
8585
assert device.created_on.date() == datetime.today().date()
86-
assert device.updated_on is None
87-
assert device.deleted_on is None
86+
assert not device.updated_on
87+
assert not device.deleted_on
8888

8989
# Retrieve the created resource
9090
repo = DeviceRepository(
9191
table_name=TABLE_NAME, dynamodb_client=index.cache["DYNAMODB_CLIENT"]
9292
)
9393

94-
created_device = repo.read(device.id)
94+
created_device = repo.read(
95+
product_team_id=device.product_team_id,
96+
product_id=device.product_id,
97+
id=device.id,
98+
)
9599
assert created_device == device
96100

97101

src/api/createDeviceMessageHandlingSystem/tests/test_index.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def test_index() -> None:
136136
assert device.ods_code == ODS_CODE
137137
assert device.created_on.date() == datetime.today().date()
138138
assert device.updated_on.date() == datetime.today().date()
139-
assert device.deleted_on is None
139+
assert not device.deleted_on
140140

141141
questionnaire_responses = device.questionnaire_responses["spine_mhs/1"]
142142
assert len(questionnaire_responses) == 1
@@ -147,7 +147,11 @@ def test_index() -> None:
147147
repo = DeviceRepository(
148148
table_name=TABLE_NAME, dynamodb_client=index.cache["DYNAMODB_CLIENT"]
149149
)
150-
created_device = repo.read(device.id)
150+
created_device = repo.read(
151+
product_team_id=device.product_team_id,
152+
product_id=device.product_id,
153+
id=device.id,
154+
)
151155

152156
# Check party_key is added to tags in the created device
153157
expected_party_key = (str(ProductKeyType.PARTY_KEY), "abc1234-987654")

0 commit comments

Comments
 (0)