Skip to content
This repository was archived by the owner on Oct 10, 2025. It is now read-only.

Commit ef66199

Browse files
authored
Merge pull request #7 from aws-solutions/release-candidate/v1.0.1
Release candidate/v1.0.1
2 parents fcac514 + 124079c commit ef66199

File tree

18 files changed

+293
-93
lines changed

18 files changed

+293
-93
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [1.0.1] - 2021-10-01
8+
### Added
9+
- The solution now exports the Amazon SNS Topic ARN as `SNSTopicArn`.
10+
11+
### Changed
12+
- The SNS message format will change based on the protocol used. For Amazon SQS and Email-JSON endpoints, a JSON payload
13+
will be sent. The message sent to subscribed Email endpoints is unchanged.
14+
- The Amazon CloudWatch dashboard deployed by the solution will be replaced with a dashboard containing the stack's
15+
region name.
16+
717
## [1.0.0] - 2021-09-23
818
### Added
919
- All files, initial version

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ After running the command, you can deploy the template:
377377
378378
## Collection of operational metrics
379379
This solution collects anonymous operational metrics to help AWS improve the quality of features of the solution.
380-
For more information, including how to disable this capability, please see the [implementation guide](https://aws.amazon.com/solutions/implementations/maintaining-personalized-experiences-with-ml).
380+
For more information, including how to disable this capability, please see the [implementation guide](https://docs.aws.amazon.com/solutions/latest/maintaining-personalized-experiences-with-ml/collection-of-operational-metrics.html).
381381

382382
***
383383

source/aws_lambda/s3_event/handler.py

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for #
1111
# the specific language governing permissions and limitations under the License. #
1212
# ######################################################################################################################
13-
13+
import json
1414
import os
15-
from typing import List
1615

1716
from aws_lambda_powertools import Logger, Tracer, Metrics
1817
from aws_lambda_powertools.metrics import MetricUnit
@@ -43,21 +42,50 @@ def solution_name() -> str:
4342
return os.environ["SOLUTION_NAME"]
4443

4544

46-
def send_configuration_error(errors: List[str]):
45+
def send_configuration_error(configuration: Configuration):
46+
errors = configuration.errors
4747
sns = get_service_client("sns")
48+
dataset_group = configuration.dataset_group
49+
4850
subject = f"{solution_name()} Notifications"
4951

50-
message = "There were errors detected when reading a personalization job configuration file:\n\n"
51-
for error in errors:
52-
logger.error(f"Personalization job configuration error: {error}")
53-
message += f" - {error}\n"
54-
message += "\nPlease correct these errors and upload the configuration again."
52+
def build_default_message():
53+
f"The personalization workflow for {configuration.dataset_group} completed with errors."
54+
55+
def build_json_message():
56+
return json.dumps(
57+
{
58+
"datasetGroup": dataset_group,
59+
"status": "UPDATE FAILED",
60+
"summary": "There were errors detected when reading a personalization job configuration file",
61+
"description": [error for error in errors],
62+
}
63+
)
64+
65+
def build_long_message():
66+
message = "There were errors detected when reading a personalization job configuration file:\n\n"
67+
for error in errors:
68+
logger.error(f"Personalization job configuration error: {error}")
69+
message += f" - {error}\n"
70+
message += "\nPlease correct these errors and upload the configuration again."
71+
return message
5572

73+
logger.error("publishing configuration error to SQS")
5674
sns.publish(
5775
TopicArn=topic_arn(),
58-
Message=message,
76+
Message=json.dumps(
77+
{
78+
"default": build_default_message(),
79+
"sms": build_default_message(),
80+
"email": build_long_message(),
81+
"email-json": build_json_message(),
82+
"sqs": build_json_message(),
83+
}
84+
),
85+
MessageStructure="json",
5986
Subject=subject,
6087
)
88+
logger.error("published configuration error to SQS")
6189

6290

6391
@metrics.log_metrics
@@ -86,7 +114,7 @@ def lambda_handler(event, context):
86114
configuration = Configuration()
87115
configuration.load(config_text)
88116
if configuration.errors:
89-
send_configuration_error(configuration.errors)
117+
send_configuration_error(configuration)
90118
metrics.add_metric(
91119
"ConfigurationsProcessedFailures", unit=MetricUnit.Count, value=1
92120
)
@@ -98,7 +126,7 @@ def lambda_handler(event, context):
98126
metrics.add_metric(
99127
"ConfigurationsProcessedFailures", unit=MetricUnit.Count, value=1
100128
)
101-
send_configuration_error(configuration.errors)
129+
send_configuration_error(configuration)
102130
else:
103131
config = configuration.config_dict
104132
config = set_bucket(config, bucket, key)

source/aws_lambda/shared/personalize_service.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@ class Configuration:
609609
def __init__(self):
610610
self._configuration_errors = []
611611
self.config_dict = {}
612+
self.dataset_group = "UNKNOWN"
612613

613614
def load(self, content: Union[Path, str]):
614615
if isinstance(content, Path):
@@ -670,6 +671,8 @@ def _validate_dataset_group(self, path="datasetGroup.serviceConfig"):
670671
)
671672
else:
672673
self._validate_resource(DatasetGroup(), dataset_group)
674+
if isinstance(dataset_group, dict):
675+
self.dataset_group = dataset_group.get("name", self.dataset_group)
673676

674677
def _validate_event_tracker(self, path="eventTracker.serviceConfig"):
675678
event_tracker = jmespath.search(path, self.config_dict)

source/aws_lambda/shared/scheduler/base.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,10 @@ def list(self) -> Generator[str, None, None]:
139139
:return: Generator[str] of the schedules (by name)
140140
"""
141141
done = False
142-
scan_kwargs = {"ProjectionExpression": TASK_PK}
142+
scan_kwargs = {
143+
"ProjectionExpression": "#name",
144+
"ExpressionAttributeNames": {"#name": TASK_PK},
145+
}
143146
start_key = None
144147
discovered = set()
145148
while not done:

source/aws_lambda/shared/sfn_middleware.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,11 @@ def check_status( # NOSONAR - allow higher complexity
212212
expected_value = expected_value.lower()
213213

214214
# some parameters don't require checking:
215-
if self.resource == "datasetImportJob" and expected_key == "jobName":
215+
if self.resource == "datasetImportJob" and expected_key in {
216+
"jobName",
217+
"dataSource",
218+
"roleArn",
219+
}:
216220
continue
217221
if self.resource == "batchInferenceJob" and expected_key in {
218222
"jobName",

source/aws_lambda/sns_notification/handler.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,26 @@ def __init__(self, event: Dict, context: LambdaContext):
6767
metrics.add_metric("JobSuccess", unit=MetricUnit.Count, value=1)
6868
self.message = self._build_success_message()
6969

70+
self.default = self._build_default_message()
71+
self.sms = self._build_sms_message()
72+
self.json = self._build_json_message()
73+
74+
def _build_json_message(self):
75+
return json.dumps(
76+
{
77+
"datasetGroup": self.dataset_group,
78+
"status": "UPDATE FAILED" if self.error else "UPDATE COMPLETE",
79+
"summary": self._build_default_message(),
80+
"description": self.message,
81+
}
82+
)
83+
84+
def _build_default_message(self) -> str:
85+
return f"The personalization workflow for {self.dataset_group} completed {'with errors' if self.error else 'successfully'}"
86+
87+
def _build_sms_message(self) -> str:
88+
return self._build_default_message()
89+
7090
def _build_error_message(self) -> str:
7191
"""
7292
Build the error message
@@ -116,12 +136,21 @@ def lambda_handler(event, context):
116136
:return: None
117137
"""
118138
sns = get_service_client("sns")
119-
message = MessageBuilder(event, context).message
139+
message_builder = MessageBuilder(event, context)
120140
subject = f"{solution_name()} Notifications"
121141

122142
logger.info("publishing message for event", extra={"event": event})
123143
sns.publish(
124144
TopicArn=topic_arn(),
125-
Message=message,
145+
Message=json.dumps(
146+
{
147+
"default": message_builder.default,
148+
"sms": message_builder.sms,
149+
"email": message_builder.message,
150+
"email-json": message_builder.json,
151+
"sqs": message_builder.json,
152+
}
153+
),
154+
MessageStructure="json",
126155
Subject=subject,
127156
)

source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,13 @@ class CDKSolution:
2929
"""
3030

3131
def __init__(self, cdk_json_path: Path, qualifier="hnb659fds"):
32+
self.qualifier = qualifier
3233
self.context = SolutionContext(cdk_json_path=cdk_json_path)
33-
self.synthesizer = SolutionStackSubstitions(qualifier=qualifier)
34+
self.synthesizer = SolutionStackSubstitions(qualifier=self.qualifier)
35+
36+
def reset(self) -> None:
37+
"""
38+
Get a new synthesizer for this CDKSolution - useful for testing
39+
:return: None
40+
"""
41+
self.synthesizer = SolutionStackSubstitions(qualifier=self.qualifier)

source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/interfaces.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,12 @@ def metadata(self) -> dict:
7777
return self._metadata
7878

7979
def _get_metadata(self) -> dict:
80-
parameter_groups = list(
81-
set([parameter.group for parameter in self._parameters])
82-
)
80+
pgs = set()
81+
parameter_groups = [
82+
p.group
83+
for p in self._parameters
84+
if p.group not in pgs and not pgs.add(p.group)
85+
]
8386
metadata = {
8487
"AWS::CloudFormation::Interface": {
8588
"ParameterGroups": [

source/cdk_solution_helper_py/requirements-dev.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
aws-cdk.core>=1.120.0
2-
aws-cdk.aws_lambda>=1.120.0
1+
aws-cdk.core>=1.123.0
2+
aws-cdk.aws_lambda>=1.123.0
33
black
44
boto3>=1.17.49
55
requests>=2.24.0

0 commit comments

Comments
 (0)