Skip to content

Commit 03e8389

Browse files
Update: [AEA-6075] - Changes supporting spine codebase integration (#46)
1 parent 945ffa0 commit 03e8389

File tree

12 files changed

+179
-103
lines changed

12 files changed

+179
-103
lines changed

.devcontainer/devcontainer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
"ms-python.flake8",
2929
"eamodio.gitlens",
3030
"github.vscode-pull-request-github",
31-
"dbaeumer.vscode-eslint",
3231
"lfm.vscode-makefile-term",
3332
"streetsidesoftware.code-spell-checker",
3433
"timonwong.shellcheck",
35-
"github.vscode-github-actions"
34+
"github.vscode-github-actions",
35+
"tamasfe.even-better-toml"
3636
],
3737
"settings": {
3838
"python.defaultInterpreterPath": "/home/vscode/.asdf/shims/python",

src/eps_spine_shared/common/dynamodb_client.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ def __init__(
7070

7171
resource_args = {"service_name": SERVICE_NAME, "region_name": REGION_NAME}
7272
if aws_endpoint_url:
73-
log_object.write_log("DDB0003", None, {"awsEndpointUrl": aws_endpoint_url})
73+
self.log_object.write_log("DDB0003", None, {"awsEndpointUrl": aws_endpoint_url})
7474
resource_args["endpoint_url"] = aws_endpoint_url
7575
else:
76-
log_object.write_log("DDB0004", None)
76+
self.log_object.write_log("DDB0004", None)
7777

7878
self.resource = session.resource(**resource_args)
7979
self.table = self.resource.Table(table_name)
@@ -82,7 +82,7 @@ def __init__(
8282
self.deserialiser = TypeDeserializer()
8383
self.serialiser = TypeSerializer()
8484
except Exception as ex:
85-
log_object.write_log("DDB0000", sys.exc_info(), {"error": str(ex)})
85+
self.log_object.write_log("DDB0000", sys.exc_info(), {"error": str(ex)})
8686
raise ex
8787

8888
self.log_object.write_log("DDB0001", None, {"tableName": table_name})

src/eps_spine_shared/common/dynamodb_datastore.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -80,29 +80,27 @@ class EpsDynamoDbDataStore:
8080
DEFAULT_EXPIRY_DAYS = 56
8181
MAX_NEXT_ACTIVITY_DATE = "99991231"
8282

83-
def __init__(
84-
self,
85-
log_object,
86-
aws_endpoint_url,
87-
table_name,
88-
role_arn=None,
89-
role_session_name=None,
90-
sts_endpoint_url=None,
91-
):
83+
def __init__(self, log_object, system_config):
9284
"""
9385
Instantiate the DynamoDB client.
9486
"""
9587
self.log_object = EpsLogger(log_object)
9688
self.client = EpsDynamoDbClient(
9789
log_object,
98-
aws_endpoint_url,
99-
table_name,
100-
role_arn,
101-
role_session_name,
102-
sts_endpoint_url,
90+
system_config["ddb aws endpoint url"],
91+
system_config["datastore table name"],
92+
system_config["datastore role arn"],
93+
system_config["process name"],
94+
system_config["sts endpoint url"],
10395
)
10496
self.indexes = EpsDynamoDbIndex(log_object, self.client)
10597

98+
def testConnection(self):
99+
"""
100+
Placeholder test connection, returns constant value
101+
"""
102+
return True
103+
106104
def base64_decode_document_content(self, internal_id, document):
107105
"""
108106
base64 decode document content in order to store as binary type in DynamoDB.
@@ -595,7 +593,7 @@ def _fetch_next_sequence_number(self, internal_id, key, max_sequence_number, rea
595593
self.client.insert_items(internal_id, [item], is_update, False)
596594
break
597595
except EpsDataStoreError as e:
598-
if e.errorTopic == EpsDataStoreError.CONDITIONAL_UPDATE_FAILURE and tries < 25:
596+
if e.error_topic == EpsDataStoreError.CONDITIONAL_UPDATE_FAILURE and tries < 25:
599597
sequence_number = item[Attribute.SEQUENCE_NUMBER.name]
600598
item[Attribute.SEQUENCE_NUMBER.name] = (
601599
sequence_number + 1 if sequence_number < max_sequence_number else 1
@@ -759,13 +757,13 @@ def delete_record(self, internal_id, record_key):
759757

760758
@timer
761759
def return_pids_due_for_next_activity(
762-
self, _internal_id, next_activity_start, next_activity_end
760+
self, _internal_id, next_activity_start, next_activity_end, shard=None
763761
):
764762
"""
765763
Returns all the epsRecord keys for prescriptions whose nextActivity is the same as that provided,
766764
and whose next activity date is within the date range provided.
767765
"""
768-
return self.indexes.query_next_activity_date(next_activity_start, next_activity_end)
766+
return self.indexes.query_next_activity_date(next_activity_start, next_activity_end, shard)
769767

770768
@timer
771769
def return_prescription_ids_for_nom_pharm(self, _internal_id, nominated_pharmacy_index_term):

src/eps_spine_shared/common/dynamodb_index.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ def query_claim_id(self, claim_id):
314314

315315
return [item[Key.PK.name] for item in items]
316316

317-
def query_next_activity_date(self, range_start, range_end):
317+
def query_next_activity_date(self, range_start, range_end, shard=None):
318318
"""
319319
Yields the epsRecord keys which match the supplied nextActivity and date range for the nextActivity index.
320320
@@ -332,6 +332,10 @@ def query_next_activity_date(self, range_start, range_end):
332332
if not valid:
333333
return []
334334

335+
if shard or shard == "":
336+
yield from self._query_next_activity_date_shard(next_activity, sk_expression, shard)
337+
return
338+
335339
shards = [None] + list(range(1, NEXT_ACTIVITY_DATE_PARTITIONS + 1))
336340

337341
for shard in shards:
@@ -342,7 +346,9 @@ def _query_next_activity_date_shard(self, next_activity, sk_expression, shard):
342346
Return a generator for the epsRecord keys which match the supplied nextActivity and date range
343347
for a given pk shard.
344348
"""
345-
expected_next_activity = next_activity if shard is None else f"{next_activity}.{shard}"
349+
expected_next_activity = (
350+
next_activity if shard is None or shard == "" else f"{next_activity}.{shard}"
351+
)
346352
pk_expression = BotoKey(Attribute.NEXT_ACTIVITY.name).eq(expected_next_activity)
347353

348354
return self.client.query_index_yield(

src/eps_spine_shared/common/prescription/line_item.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def expire(self, parent_prescription):
8989
if current_status not in LineItemStatus.EXPIRY_IMMUTABLE_STATES:
9090
new_status = LineItemStatus.EXPIRY_LOOKUP[current_status]
9191
self.update_status(new_status)
92-
parent_prescription.logObject.write_log(
92+
parent_prescription.log_object.write_log(
9393
"EPS0072b",
9494
None,
9595
{

src/eps_spine_shared/common/prescription/record.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ def _handle_missing_issue(self, issue_number):
404404
{"internalID": self.internal_id, "prescriptionID": self.id, "issue": issue_number},
405405
)
406406
# Re-raise this as SpineBusinessError with equivalent errorCode from ErrorBase1722.
407-
raise EpsBusinessError(EpsErrorBase.PRESCRIPTION_NOT_FOUND)
407+
raise EpsBusinessError(EpsErrorBase.MISSING_ISSUE)
408408

409409
@property
410410
def id(self):
@@ -2621,7 +2621,7 @@ def release_next_instance(
26212621
next_issue_number_str = self._find_next_future_issue_number(current_issue_number_str)
26222622
if next_issue_number_str is None:
26232623
# give up if there is no next issue
2624-
self.pendingInstanceChange = None
2624+
self.pending_instance_change = None
26252625
return
26262626

26272627
# update the issue
@@ -2677,7 +2677,7 @@ def release_next_instance(
26772677
)
26782678

26792679
# mark so that we know to update the prescription's current issue number
2680-
self.pendingInstanceChange = next_issue_number_str
2680+
self.pending_instance_change = next_issue_number_str
26812681

26822682
def add_release_document_ref(self, rel_req_document_ref):
26832683
"""
@@ -2969,7 +2969,7 @@ def fetch_release_response_parameters(self):
29692969
for line_item in self.current_issue.line_items:
29702970
line_item_ref = "lineItem" + str(line_item.order)
29712971
item_status = (
2972-
line_item.previousStatus
2972+
line_item.previous_status
29732973
if line_item.status == LineItemStatus.WITH_DISPENSER
29742974
else line_item.status
29752975
)
@@ -3044,6 +3044,7 @@ def force_current_instance_increment(self):
30443044
new_current_issue_number = False
30453045
for i in range(self.current_issue_number, self.max_repeats + 1):
30463046
try:
3047+
self.prescription_record[fields.FIELD_INSTANCES][str(i)]
30473048
new_current_issue_number = i
30483049
break
30493050
except KeyError:

src/eps_spine_shared/errors.py

Lines changed: 131 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,69 +2,135 @@
22

33
from botocore.exceptions import NoCredentialsError
44

5+
# Try to import spine error classes. If successful, we are on spine and should use wrapper classes.
6+
on_spine = False
7+
try:
8+
# from spinecore.prescriptions.common.errors.errorbaseprescriptionsearch \
9+
# import ErrorBasePrescSearch # pyright: ignore[reportMissingImports]
10+
from spinecore.common.aws.awscommon import ( # pyright: ignore[reportMissingImports]
11+
NoCredentialsErrorWithRetry,
12+
)
13+
from spinecore.common.errors import ( # pyright: ignore[reportMissingImports]
14+
SpineBusinessError,
15+
SpineSystemError,
16+
)
517

6-
class EpsNoCredentialsErrorWithRetry(NoCredentialsError):
7-
"""
8-
Extends NoCredentialsError to provide information about retry attempts.
9-
To be caught in Spine application code and re-raised as NoCredentialsErrorWithRetry.
10-
"""
11-
12-
fmt = "Unable to locate credentials after {attempts} attempts"
13-
14-
15-
class EpsSystemError(Exception):
16-
"""
17-
Exception to be raised if an unexpected system error occurs.
18-
To be caught in Spine application code and re-raised as SpineSystemError.
19-
"""
20-
21-
MESSAGE_FAILURE = "messageFailure"
22-
DEVELOPMENT_FAILURE = "developmentFailure"
23-
SYSTEM_FAILURE = "systemFailure"
24-
IMMEDIATE_REQUEUE = "immediateRequeue"
25-
RETRY_EXPIRED = "retryExpired"
26-
PUBLISHER_HANDLES_REQUEUE = "publisherHandlesRequeue"
27-
UNRELIABLE_MESSAGE = "unreliableMessage"
28-
29-
def __init__(self, errorTopic, *args): # noqa: B042
30-
"""
31-
errorTopic is the topic to be used when writing the WDO to the error exchange
32-
"""
33-
super(EpsSystemError, self).__init__(*args)
34-
self.errorTopic = errorTopic
35-
36-
37-
class EpsBusinessError(Exception):
38-
"""
39-
Exception to be raised by a message worker if an expected error condition is hit,
40-
one that is expected to cause a HL7 error response with a set errorCode.
41-
To be caught in Spine application code and re-raised as SpineBusinessError.
42-
"""
43-
44-
def __init__(self, errorCode, suppInfo=None, messageId=None): # noqa: B042
45-
super(EpsBusinessError, self).__init__()
46-
self.errorCode = errorCode
47-
self.supplementaryInformation = suppInfo
48-
self.messageId = messageId
49-
50-
def __str__(self):
51-
if self.supplementaryInformation:
52-
return "{} {}".format(self.errorCode, self.supplementaryInformation)
53-
return str(self.errorCode)
54-
55-
56-
class EpsErrorBase(Enum):
57-
"""
58-
To be used in Spine application code to remap to ErrorBases.
59-
"""
60-
61-
INVALID_LINE_STATE_TRANSITION = 1
62-
ITEM_NOT_FOUND = 2
63-
MAX_REPEAT_MISMATCH = 3
64-
NOT_CANCELLED_EXPIRED = 4
65-
NOT_CANCELLED_CANCELLED = 5
66-
NOT_CANCELLED_NOT_DISPENSED = 6
67-
NOT_CANCELLED_DISPENSED = 7
68-
NOT_CANCELLED_WITH_DISPENSER = 8
69-
NOT_CANCELLED_WITH_DISPENSER_ACTIVE = 9
70-
PRESCRIPTION_NOT_FOUND = 10
18+
# from spinecore.prescriptions.common.errors.errorbase1634 \
19+
# import ErrorBase1634 # pyright: ignore[reportMissingImports]
20+
from spinecore.prescriptions.common.errors.errorbase1719 import ( # pyright: ignore[reportMissingImports]
21+
ErrorBase1719,
22+
)
23+
from spinecore.prescriptions.common.errors.errorbase1722 import ( # pyright: ignore[reportMissingImports]
24+
ErrorBase1722,
25+
)
26+
27+
on_spine = True
28+
except ImportError:
29+
pass
30+
31+
32+
if on_spine:
33+
34+
class EpsNoCredentialsErrorWithRetry:
35+
"""
36+
Wrapper for NoCredentialsErrorWithRetry
37+
"""
38+
39+
def __init__(self, *args):
40+
raise NoCredentialsErrorWithRetry(*args)
41+
42+
class EpsSystemError:
43+
"""
44+
Wrapper for SpineSystemError
45+
"""
46+
47+
def __init__(self, *args):
48+
raise SpineSystemError(*args)
49+
50+
class EpsBusinessError:
51+
"""
52+
Wrapper for SpineBusinessError
53+
"""
54+
55+
def __init__(self, *args):
56+
raise SpineBusinessError(*args)
57+
58+
class EpsErrorBase:
59+
"""
60+
Wrapper for ErrorBases
61+
"""
62+
63+
MISSING_ISSUE = ErrorBase1722.PRESCRIPTION_NOT_FOUND
64+
ITEM_NOT_FOUND = ErrorBase1722.ITEM_NOT_FOUND
65+
INVALID_LINE_STATE_TRANSITION = ErrorBase1722.INVALID_LINE_STATE_TRANSITION
66+
MAX_REPEAT_MISMATCH = ErrorBase1722.MAX_REPEAT_MISMATCH
67+
NOT_CANCELLED_EXPIRED = ErrorBase1719.NOT_CANCELLED_EXPIRED
68+
NOT_CANCELLED_CANCELLED = ErrorBase1719.NOT_CANCELLED_CANCELLED
69+
NOT_CANCELLED_NOT_DISPENSED = ErrorBase1719.NOT_CANCELLED_NOT_DISPENSED
70+
NOT_CANCELLED_DISPENSED = ErrorBase1719.NOT_CANCELLED_DISPENSED
71+
NOT_CANCELLED_WITH_DISPENSER = ErrorBase1719.NOT_CANCELLED_WITH_DISPENSER
72+
NOT_CANCELLED_WITH_DISPENSER_ACTIVE = ErrorBase1719.NOT_CANCELLED_WITH_DISPENSER_ACTIVE
73+
PRESCRIPTION_NOT_FOUND = ErrorBase1719.PRESCRIPTION_NOT_FOUND
74+
75+
else:
76+
77+
class EpsNoCredentialsErrorWithRetry(NoCredentialsError):
78+
"""
79+
Extends NoCredentialsError to provide information about retry attempts.
80+
"""
81+
82+
fmt = "Unable to locate credentials after {attempts} attempts"
83+
84+
class EpsSystemError(Exception):
85+
"""
86+
Exception to be raised if an unexpected system error occurs.
87+
"""
88+
89+
MESSAGE_FAILURE = "messageFailure"
90+
DEVELOPMENT_FAILURE = "developmentFailure"
91+
SYSTEM_FAILURE = "systemFailure"
92+
IMMEDIATE_REQUEUE = "immediateRequeue"
93+
RETRY_EXPIRED = "retryExpired"
94+
PUBLISHER_HANDLES_REQUEUE = "publisherHandlesRequeue"
95+
UNRELIABLE_MESSAGE = "unreliableMessage"
96+
97+
def __init__(self, error_topic, *args): # noqa: B042
98+
"""
99+
error_topic is the topic to be used when writing the WDO to the error exchange
100+
"""
101+
super(EpsSystemError, self).__init__(*args)
102+
self.error_topic = error_topic
103+
104+
class EpsBusinessError(Exception):
105+
"""
106+
Exception to be raised by a message worker if an expected error condition is hit,
107+
one that is expected to cause a HL7 error response with a set errorCode.
108+
"""
109+
110+
def __init__(self, error_code, supp_info=None, message_id=None): # noqa: B042
111+
super(EpsBusinessError, self).__init__()
112+
self.error_code = error_code
113+
self.supplementary_information = supp_info
114+
self.message_id = message_id
115+
116+
def __str__(self):
117+
if self.supplementary_information:
118+
return "{} {}".format(self.error_code, self.supplementary_information)
119+
return str(self.error_code)
120+
121+
class EpsErrorBase(Enum):
122+
"""
123+
To be used in Spine application code to remap to ErrorBases.
124+
"""
125+
126+
INVALID_LINE_STATE_TRANSITION = 1
127+
ITEM_NOT_FOUND = 2
128+
MAX_REPEAT_MISMATCH = 3
129+
NOT_CANCELLED_EXPIRED = 4
130+
NOT_CANCELLED_CANCELLED = 5
131+
NOT_CANCELLED_NOT_DISPENSED = 6
132+
NOT_CANCELLED_DISPENSED = 7
133+
NOT_CANCELLED_WITH_DISPENSER = 8
134+
NOT_CANCELLED_WITH_DISPENSER_ACTIVE = 9
135+
PRESCRIPTION_NOT_FOUND = 10
136+
MISSING_ISSUE = 11

tests/mock_logger.py renamed to src/eps_spine_shared/testing/mock_logger.py

File renamed without changes.

0 commit comments

Comments
 (0)