From 23bdc81b2940a8ffe22d4351956eebceaeb6d716 Mon Sep 17 00:00:00 2001 From: Rohan Mehta Date: Wed, 22 Mar 2023 11:27:59 -0400 Subject: [PATCH 01/16] initial commit for python hexagonal arch sample --- .../async-lambda-dynamodb/template.yaml | 165 ---------------- .../THIRD-PARTY-LICENSES | 176 ++++++++++++++++++ .../adapters/CurrencyExchangeDB.py | 25 +++ .../adapters/HandleStockRequest.py | 16 ++ .../adapters/StocksDB.py | 15 ++ .../adapters/__init__.py | 0 .../hexagonal-architectures/app.py | 27 +++ .../domains/__init__.py | 0 .../hexagonal-architectures/domains/stock.py | 24 +++ .../ports/CurrenciesService.py | 8 + .../ports/HttpHandler.py | 8 + .../ports/StocksService.py | 8 + .../hexagonal-architectures/ports/__init__.py | 0 .../hexagonal-architectures/requirements.txt | 4 + .../hexagonal-architectures/template.yaml | 61 ++++++ .../hexagonal-architectures/testevent.json | 69 +++++++ 16 files changed, 441 insertions(+), 165 deletions(-) create mode 100644 python-test-samples/hexagonal-architectures/THIRD-PARTY-LICENSES create mode 100644 python-test-samples/hexagonal-architectures/adapters/CurrencyExchangeDB.py create mode 100644 python-test-samples/hexagonal-architectures/adapters/HandleStockRequest.py create mode 100644 python-test-samples/hexagonal-architectures/adapters/StocksDB.py create mode 100644 python-test-samples/hexagonal-architectures/adapters/__init__.py create mode 100644 python-test-samples/hexagonal-architectures/app.py create mode 100644 python-test-samples/hexagonal-architectures/domains/__init__.py create mode 100644 python-test-samples/hexagonal-architectures/domains/stock.py create mode 100644 python-test-samples/hexagonal-architectures/ports/CurrenciesService.py create mode 100644 python-test-samples/hexagonal-architectures/ports/HttpHandler.py create mode 100644 python-test-samples/hexagonal-architectures/ports/StocksService.py create mode 100644 python-test-samples/hexagonal-architectures/ports/__init__.py create mode 100644 python-test-samples/hexagonal-architectures/requirements.txt create mode 100644 python-test-samples/hexagonal-architectures/template.yaml create mode 100644 python-test-samples/hexagonal-architectures/testevent.json diff --git a/python-test-samples/async-architectures/async-lambda-dynamodb/template.yaml b/python-test-samples/async-architectures/async-lambda-dynamodb/template.yaml index 37dbdf83..e69de29b 100644 --- a/python-test-samples/async-architectures/async-lambda-dynamodb/template.yaml +++ b/python-test-samples/async-architectures/async-lambda-dynamodb/template.yaml @@ -1,165 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: > - This template deploys a code sample for testing an asynchronous architecture using Python. - -Parameters: - DeployTestResources: - Description: The parameter instructs the template whether or not to deploy test resources to your environment. - Default: "True" - Type: String - AllowedValues: - - "True" - - "False" - ConstraintDescription: Allowed values are True and False - -Conditions: - CreateTestResources: !Equals [!Ref DeployTestResources, "True"] - -Globals: # https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-template-anatomy-globals.html - Function: - Timeout: 15 - MemorySize: 256 - Runtime: python3.9 - Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html - # Embed Lambda Powertools as a shared Layer - # See: https://awslabs.github.io/aws-lambda-powertools-python/latest/#lambda-layer - Layers: # - - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPython:9 - Environment: - Variables: - DESTINATION_BUCKET: - !Sub "async-destination-${AWS::StackName}-${AWS::AccountId}" - RESULTS_TABLE: - !Sub "async-results-${AWS::StackName}-${AWS::AccountId}" - # Powertools env vars: https://awslabs.github.io/aws-lambda-powertools-python/#environment-variables - LOG_LEVEL: INFO - POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 - POWERTOOLS_LOGGER_LOG_EVENT: true - POWERTOOLS_METRICS_NAMESPACE: MyServerlessApplication - POWERTOOLS_SERVICE_NAME: async - -Resources: - SourceBucket: - Type: AWS::S3::Bucket - UpdateReplacePolicy: Delete - Properties: - BucketName: - !Sub "async-source-${AWS::StackName}-${AWS::AccountId}" - BucketEncryption: - ServerSideEncryptionConfiguration: - - ServerSideEncryptionByDefault: - SSEAlgorithm: AES256 - PublicAccessBlockConfiguration: - BlockPublicAcls: true - BlockPublicPolicy: true - IgnorePublicAcls: true - RestrictPublicBuckets: true - - DestinationBucket: - Type: AWS::S3::Bucket - UpdateReplacePolicy: Delete - Properties: - BucketName: - !Sub "async-destination-${AWS::StackName}-${AWS::AccountId}" - BucketEncryption: - ServerSideEncryptionConfiguration: - - ServerSideEncryptionByDefault: - SSEAlgorithm: AES256 - PublicAccessBlockConfiguration: - BlockPublicAcls: true - BlockPublicPolicy: true - IgnorePublicAcls: true - RestrictPublicBuckets: true - - ToUppercaseTextTransformer: - Type: AWS::Serverless::Function - Properties: - FunctionName: - !Sub "ToUppercaseTextTransformer-${AWS::StackName}-${AWS::AccountId}" - CodeUri: src/ - Handler: app.handler - Policies: - - S3ReadPolicy: - BucketName: - !Sub "async-source-${AWS::StackName}-${AWS::AccountId}" - - S3CrudPolicy: - BucketName: - !Sub "async-destination-${AWS::StackName}-${AWS::AccountId}" - Events: - FileUpload: - Type: S3 - Properties: - Bucket: !Ref SourceBucket - Events: s3:ObjectCreated:* - Filter: - S3Key: - Rules: - - Name: suffix - Value: '.txt' - - AsyncTransformTestResultsTable: - Type: AWS::DynamoDB::Table - Condition: CreateTestResources - Properties: - TableName: - !Sub "async-results-${AWS::StackName}-${AWS::AccountId}" - AttributeDefinitions: - - AttributeName: id - AttributeType: S - KeySchema: - - AttributeName: id - KeyType: HASH - ProvisionedThroughput: - ReadCapacityUnits: 2 - WriteCapacityUnits: 2 - - DestinationBucketListener: - Type: AWS::Serverless::Function - Condition: CreateTestResources - Properties: - FunctionName: - !Sub "DestinationBucketListener-${AWS::StackName}-${AWS::AccountId}" - CodeUri: tests/integration - Handler: event_listener_lambda.handler - Policies: - - DynamoDBWritePolicy: - TableName: !Ref AsyncTransformTestResultsTable - - S3ReadPolicy: - BucketName: - !Sub "async-destination-${AWS::StackName}-${AWS::AccountId}" - Events: - FileUpload: - Type: S3 - Properties: - Bucket: !Ref DestinationBucket - Events: s3:ObjectCreated:* - Filter: - S3Key: - Rules: - - Name: suffix - Value: '.txt' - - -Outputs: - - SourceBucketName: - Description: "Source bucket for asynchronous testing sample" - Value: !Ref SourceBucket - - DestinationBucketName: - Description: "Destination bucket for asynchronous testing sample" - Value: !Ref DestinationBucket - - DestinationBucketListenerName: - Condition: CreateTestResources - Description: "Lambda Function to listen for test results" - Value: !Ref DestinationBucketListener - - AsyncTransformTestResultsTable: - Condition: CreateTestResources - Description: "DynamoDB table to persist test results" - Value: !Ref AsyncTransformTestResultsTable - - - diff --git a/python-test-samples/hexagonal-architectures/THIRD-PARTY-LICENSES b/python-test-samples/hexagonal-architectures/THIRD-PARTY-LICENSES new file mode 100644 index 00000000..3712eac8 --- /dev/null +++ b/python-test-samples/hexagonal-architectures/THIRD-PARTY-LICENSES @@ -0,0 +1,176 @@ +** Tensorflow - https://github.com/tensorflow/tensorflow/ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/python-test-samples/hexagonal-architectures/adapters/CurrencyExchangeDB.py b/python-test-samples/hexagonal-architectures/adapters/CurrencyExchangeDB.py new file mode 100644 index 00000000..5cfdfcec --- /dev/null +++ b/python-test-samples/hexagonal-architectures/adapters/CurrencyExchangeDB.py @@ -0,0 +1,25 @@ +from os import environ +import json +import boto3 + +def getCurrencies(currencies): + try: + dynamodb_table_name = environ["CURRENCIES_DB_TABLE"] + dynamodb_resource = boto3.resource('dynamodb') + dynamodb_table = dynamodb_resource.Table(dynamodb_table_name) + print(f"Using DynamoDB Table {dynamodb_table_name}.") + rates = {} + for currency in currencies: + dynamodb_response = dynamodb_table.get_item(Key={"CURRENCY": f"{currency}"}) + print(dynamodb_response) + #stockData = HttpHandler.retrieveStock(stockID) + response = { + "rates":{ + "USD": 1.21, + "CAD": 1.31, + "AUD": 1.41 + } + } + return response + except Exception as e: + print(e) \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/adapters/HandleStockRequest.py b/python-test-samples/hexagonal-architectures/adapters/HandleStockRequest.py new file mode 100644 index 00000000..f0a9a401 --- /dev/null +++ b/python-test-samples/hexagonal-architectures/adapters/HandleStockRequest.py @@ -0,0 +1,16 @@ +from ports import HttpHandler +import json + +def getStocksRequest(stockID): + try: + stockData = HttpHandler.retrieveStock(stockID) + print("Stock Data", stockData) + response = { + 'statusCode': 200, + 'body': json.dumps({ + "message": stockData, + }) + } + except Exception as e: + print(e) + return response \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/adapters/StocksDB.py b/python-test-samples/hexagonal-architectures/adapters/StocksDB.py new file mode 100644 index 00000000..52984ba0 --- /dev/null +++ b/python-test-samples/hexagonal-architectures/adapters/StocksDB.py @@ -0,0 +1,15 @@ +from os import environ +import json +import boto3 + +def getStockValue(stockID): + try: + dynamodb_table_name = environ["STOCKS_DB_TABLE"] + dynamodb_resource = boto3.resource('dynamodb') + dynamodb_table = dynamodb_resource.Table(dynamodb_table_name) + print(f"Using DynamoDB Table {dynamodb_table_name}.") + dynamodb_response = dynamodb_table.get_item(Key={"STOCK_ID": f"{stockID}"}) + dynamodb_response = {"STOCK_ID": "1", "VALUE": 3} + return dynamodb_response + except Exception as e: + print(e) \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/adapters/__init__.py b/python-test-samples/hexagonal-architectures/adapters/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python-test-samples/hexagonal-architectures/app.py b/python-test-samples/hexagonal-architectures/app.py new file mode 100644 index 00000000..a3347737 --- /dev/null +++ b/python-test-samples/hexagonal-architectures/app.py @@ -0,0 +1,27 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 + +# Lambda Handler for the Python apigw-lambda-dynamodb example +# This handler accepts an id that represents a persons name, and creates a "Hello {Name}!" message. +# The id and name associated with the name is stored in a DynamoDB Table. +# Additionally, when a message is created, the lambda logs the "Hello {Name}!" message to DynamoDB with a timestamp. +# The DynamoDB Table used is passed as an environment variable "DYNAMODB_TABLE_NAME" + +from os import environ +from datetime import datetime +from adapters import HandleStockRequest + +from aws_lambda_powertools.utilities.data_classes import APIGatewayProxyEvent +from aws_lambda_powertools.utilities.typing import LambdaContext +from aws_lambda_powertools.utilities.validation import validator + +def lambda_handler(event: APIGatewayProxyEvent, context: LambdaContext) -> dict: + try: + print("test") + print(event) + stockID = event["pathParameters"]["stock"] + print("Test") + response = HandleStockRequest.getStocksRequest(stockID) + return response + except Exception as e: + print(e) \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/domains/__init__.py b/python-test-samples/hexagonal-architectures/domains/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python-test-samples/hexagonal-architectures/domains/stock.py b/python-test-samples/hexagonal-architectures/domains/stock.py new file mode 100644 index 00000000..f6341c2a --- /dev/null +++ b/python-test-samples/hexagonal-architectures/domains/stock.py @@ -0,0 +1,24 @@ +from ports import CurrenciesService + +from ports import StocksService + +currencies = ["USD", "CAD", "AUD"] + +def retrieveStockValues(stockID): + try: + stockValue = StocksService.getStockData(stockID) + currencyList = CurrenciesService.getCurrenciesData(currencies) + print(stockValue) + print(currencyList) + stockWithCurrencies = { + "stock": stockValue["STOCK_ID"], + "values": { + "EUR": stockValue["VALUE"] + } + } + for currency in currencyList["rates"]: + stockWithCurrencies["values"][currency] = round(stockValue["VALUE"] * currencyList["rates"][currency], 3) + + return stockWithCurrencies + except Exception as e: + print(e) \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/ports/CurrenciesService.py b/python-test-samples/hexagonal-architectures/ports/CurrenciesService.py new file mode 100644 index 00000000..d375da62 --- /dev/null +++ b/python-test-samples/hexagonal-architectures/ports/CurrenciesService.py @@ -0,0 +1,8 @@ +from adapters import CurrencyExchangeDB + +def getCurrenciesData(currencies): + try: + data = CurrencyExchangeDB.getCurrencies(currencies) + return data + except Exception as e: + print(e) \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/ports/HttpHandler.py b/python-test-samples/hexagonal-architectures/ports/HttpHandler.py new file mode 100644 index 00000000..ae7796bd --- /dev/null +++ b/python-test-samples/hexagonal-architectures/ports/HttpHandler.py @@ -0,0 +1,8 @@ +from domains import stock + +def retrieveStock(stockID): + try: + stockWithCurrencies = stock.retrieveStockValues(stockID) + return stockWithCurrencies + except Exception as e: + print(e) \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/ports/StocksService.py b/python-test-samples/hexagonal-architectures/ports/StocksService.py new file mode 100644 index 00000000..effce15a --- /dev/null +++ b/python-test-samples/hexagonal-architectures/ports/StocksService.py @@ -0,0 +1,8 @@ +from adapters import StocksDB + +def getStockData(stockID): + try: + data = StocksDB.getStockValue(stockID) + return data + except Exception as e: + print(e) \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/ports/__init__.py b/python-test-samples/hexagonal-architectures/ports/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python-test-samples/hexagonal-architectures/requirements.txt b/python-test-samples/hexagonal-architectures/requirements.txt new file mode 100644 index 00000000..3df75db3 --- /dev/null +++ b/python-test-samples/hexagonal-architectures/requirements.txt @@ -0,0 +1,4 @@ +aws-xray-sdk +aws_lambda_powertools +fastjsonschema +boto3 \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/template.yaml b/python-test-samples/hexagonal-architectures/template.yaml new file mode 100644 index 00000000..d6800c8c --- /dev/null +++ b/python-test-samples/hexagonal-architectures/template.yaml @@ -0,0 +1,61 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: hexagonal-arch-sample + +Globals: + Function: + Timeout: 20 + +Resources: + StocksConverterFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./ + Handler: app.lambda_handler + Runtime: python3.9 + MemorySize: 256 + Policies: + - DynamoDBReadPolicy: + TableName: !Ref StocksTable + - DynamoDBReadPolicy: + TableName: !Ref CurrenciesTable + Environment: + Variables: + STOCKS_DB_TABLE: !Ref StocksTable + CURRENCIES_DB_TABLE: !Ref CurrenciesTable + Events: + StocksConverter: + Type: HttpApi + Properties: + ApiId: !Ref StocksGateway + Path: /stock/{StockID} + Method: get + StocksTable: + Type: AWS::DynamoDB::Table + Properties: + AttributeDefinitions: + - AttributeName: STOCK_ID + AttributeType: S + KeySchema: + - AttributeName: STOCK_ID + KeyType: HASH + BillingMode: PAY_PER_REQUEST + CurrenciesTable: + Type: AWS::DynamoDB::Table + Properties: + AttributeDefinitions: + - AttributeName: CURRENCY + AttributeType: S + KeySchema: + - AttributeName: CURRENCY + KeyType: HASH + BillingMode: PAY_PER_REQUEST + StocksGateway: + Type: AWS::Serverless::HttpApi + Properties: + CorsConfiguration: + AllowMethods: + - GET + - POST + AllowOrigins: + - "*" \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/testevent.json b/python-test-samples/hexagonal-architectures/testevent.json new file mode 100644 index 00000000..2463d39d --- /dev/null +++ b/python-test-samples/hexagonal-architectures/testevent.json @@ -0,0 +1,69 @@ +{ + "version": "2.0", + "routeKey": "$default", + "rawPath": "/path/to/resource", + "rawQueryString": "parameter1=value1¶meter1=value2¶meter2=value", + "cookies": [ + "cookie1", + "cookie2" + ], + "headers": { + "Header1": "value1", + "Header2": "value1,value2" + }, + "queryStringParameters": { + "parameter1": "value1,value2", + "parameter2": "value" + }, + "requestContext": { + "accountId": "123456789012", + "apiId": "api-id", + "authentication": { + "clientCert": { + "clientCertPem": "CERT_CONTENT", + "subjectDN": "www.example.com", + "issuerDN": "Example issuer", + "serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1", + "validity": { + "notBefore": "May 28 12:30:02 2019 GMT", + "notAfter": "Aug 5 09:36:04 2021 GMT" + } + } + }, + "authorizer": { + "jwt": { + "claims": { + "claim1": "value1", + "claim2": "value2" + }, + "scopes": [ + "scope1", + "scope2" + ] + } + }, + "domainName": "id.execute-api.us-east-1.amazonaws.com", + "domainPrefix": "id", + "http": { + "method": "POST", + "path": "/path/to/resource", + "protocol": "HTTP/1.1", + "sourceIp": "192.168.0.1/32", + "userAgent": "agent" + }, + "requestId": "id", + "routeKey": "$default", + "stage": "$default", + "time": "12/Mar/2020:19:03:58 +0000", + "timeEpoch": 1583348638390 + }, + "body": "eyJ0ZXN0IjoiYm9keSJ9", + "pathParameters": { + "stockID": "1" + }, + "isBase64Encoded": true, + "stageVariables": { + "stageVariable1": "value1", + "stageVariable2": "value2" + } +} From 958ae97cd5ae779308dc8a3437a427d94861386a Mon Sep 17 00:00:00 2001 From: Rohan Mehta Date: Mon, 10 Apr 2023 15:18:57 -0400 Subject: [PATCH 02/16] Unit tests passing, 97% coverage --- .../hexagonal-architectures/app.py | 27 ---- .../hexagonal-architectures/domains/stock.py | 24 ---- .../{adapters => src}/__init__.py | 0 .../{ => src}/adapters/CurrencyExchangeDB.py | 13 +- .../{ => src}/adapters/HandleStockRequest.py | 5 +- .../{ => src}/adapters/StocksDB.py | 9 +- .../{domains => src/adapters}/__init__.py | 0 .../hexagonal-architectures/src/app.py | 25 ++++ .../{ports => src/domains}/__init__.py | 0 .../src/domains/stock.py | 29 ++++ .../{ => src}/ports/CurrenciesService.py | 2 +- .../{ => src}/ports/HttpHandler.py | 5 +- .../{ => src}/ports/StocksService.py | 5 +- .../src/ports/__init__.py | 0 .../hexagonal-architectures/template.yaml | 2 +- .../tests/events/testevent-failure.json | 69 +++++++++ .../{ => tests/events}/testevent.json | 0 .../tests/unit/__init__.py | 0 .../tests/unit/mock_test.py | 133 ++++++++++++++++++ 19 files changed, 277 insertions(+), 71 deletions(-) delete mode 100644 python-test-samples/hexagonal-architectures/app.py delete mode 100644 python-test-samples/hexagonal-architectures/domains/stock.py rename python-test-samples/hexagonal-architectures/{adapters => src}/__init__.py (100%) rename python-test-samples/hexagonal-architectures/{ => src}/adapters/CurrencyExchangeDB.py (65%) rename python-test-samples/hexagonal-architectures/{ => src}/adapters/HandleStockRequest.py (82%) rename python-test-samples/hexagonal-architectures/{ => src}/adapters/StocksDB.py (68%) rename python-test-samples/hexagonal-architectures/{domains => src/adapters}/__init__.py (100%) create mode 100644 python-test-samples/hexagonal-architectures/src/app.py rename python-test-samples/hexagonal-architectures/{ports => src/domains}/__init__.py (100%) create mode 100644 python-test-samples/hexagonal-architectures/src/domains/stock.py rename python-test-samples/hexagonal-architectures/{ => src}/ports/CurrenciesService.py (79%) rename python-test-samples/hexagonal-architectures/{ => src}/ports/HttpHandler.py (73%) rename python-test-samples/hexagonal-architectures/{ => src}/ports/StocksService.py (67%) create mode 100644 python-test-samples/hexagonal-architectures/src/ports/__init__.py create mode 100644 python-test-samples/hexagonal-architectures/tests/events/testevent-failure.json rename python-test-samples/hexagonal-architectures/{ => tests/events}/testevent.json (100%) create mode 100644 python-test-samples/hexagonal-architectures/tests/unit/__init__.py create mode 100644 python-test-samples/hexagonal-architectures/tests/unit/mock_test.py diff --git a/python-test-samples/hexagonal-architectures/app.py b/python-test-samples/hexagonal-architectures/app.py deleted file mode 100644 index a3347737..00000000 --- a/python-test-samples/hexagonal-architectures/app.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: MIT-0 - -# Lambda Handler for the Python apigw-lambda-dynamodb example -# This handler accepts an id that represents a persons name, and creates a "Hello {Name}!" message. -# The id and name associated with the name is stored in a DynamoDB Table. -# Additionally, when a message is created, the lambda logs the "Hello {Name}!" message to DynamoDB with a timestamp. -# The DynamoDB Table used is passed as an environment variable "DYNAMODB_TABLE_NAME" - -from os import environ -from datetime import datetime -from adapters import HandleStockRequest - -from aws_lambda_powertools.utilities.data_classes import APIGatewayProxyEvent -from aws_lambda_powertools.utilities.typing import LambdaContext -from aws_lambda_powertools.utilities.validation import validator - -def lambda_handler(event: APIGatewayProxyEvent, context: LambdaContext) -> dict: - try: - print("test") - print(event) - stockID = event["pathParameters"]["stock"] - print("Test") - response = HandleStockRequest.getStocksRequest(stockID) - return response - except Exception as e: - print(e) \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/domains/stock.py b/python-test-samples/hexagonal-architectures/domains/stock.py deleted file mode 100644 index f6341c2a..00000000 --- a/python-test-samples/hexagonal-architectures/domains/stock.py +++ /dev/null @@ -1,24 +0,0 @@ -from ports import CurrenciesService - -from ports import StocksService - -currencies = ["USD", "CAD", "AUD"] - -def retrieveStockValues(stockID): - try: - stockValue = StocksService.getStockData(stockID) - currencyList = CurrenciesService.getCurrenciesData(currencies) - print(stockValue) - print(currencyList) - stockWithCurrencies = { - "stock": stockValue["STOCK_ID"], - "values": { - "EUR": stockValue["VALUE"] - } - } - for currency in currencyList["rates"]: - stockWithCurrencies["values"][currency] = round(stockValue["VALUE"] * currencyList["rates"][currency], 3) - - return stockWithCurrencies - except Exception as e: - print(e) \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/adapters/__init__.py b/python-test-samples/hexagonal-architectures/src/__init__.py similarity index 100% rename from python-test-samples/hexagonal-architectures/adapters/__init__.py rename to python-test-samples/hexagonal-architectures/src/__init__.py diff --git a/python-test-samples/hexagonal-architectures/adapters/CurrencyExchangeDB.py b/python-test-samples/hexagonal-architectures/src/adapters/CurrencyExchangeDB.py similarity index 65% rename from python-test-samples/hexagonal-architectures/adapters/CurrencyExchangeDB.py rename to python-test-samples/hexagonal-architectures/src/adapters/CurrencyExchangeDB.py index 5cfdfcec..beab0a18 100644 --- a/python-test-samples/hexagonal-architectures/adapters/CurrencyExchangeDB.py +++ b/python-test-samples/hexagonal-architectures/src/adapters/CurrencyExchangeDB.py @@ -11,15 +11,8 @@ def getCurrencies(currencies): rates = {} for currency in currencies: dynamodb_response = dynamodb_table.get_item(Key={"CURRENCY": f"{currency}"}) - print(dynamodb_response) - #stockData = HttpHandler.retrieveStock(stockID) - response = { - "rates":{ - "USD": 1.21, - "CAD": 1.31, - "AUD": 1.41 - } - } - return response + rates[dynamodb_response["Item"]["CURRENCY"]] = dynamodb_response["Item"]["Rate"] + print(rates) + return rates except Exception as e: print(e) \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/adapters/HandleStockRequest.py b/python-test-samples/hexagonal-architectures/src/adapters/HandleStockRequest.py similarity index 82% rename from python-test-samples/hexagonal-architectures/adapters/HandleStockRequest.py rename to python-test-samples/hexagonal-architectures/src/adapters/HandleStockRequest.py index f0a9a401..84e23614 100644 --- a/python-test-samples/hexagonal-architectures/adapters/HandleStockRequest.py +++ b/python-test-samples/hexagonal-architectures/src/adapters/HandleStockRequest.py @@ -1,4 +1,4 @@ -from ports import HttpHandler +from src.ports import HttpHandler import json def getStocksRequest(stockID): @@ -11,6 +11,7 @@ def getStocksRequest(stockID): "message": stockData, }) } + return response except Exception as e: print(e) - return response \ No newline at end of file + raise diff --git a/python-test-samples/hexagonal-architectures/adapters/StocksDB.py b/python-test-samples/hexagonal-architectures/src/adapters/StocksDB.py similarity index 68% rename from python-test-samples/hexagonal-architectures/adapters/StocksDB.py rename to python-test-samples/hexagonal-architectures/src/adapters/StocksDB.py index 52984ba0..2b477cf1 100644 --- a/python-test-samples/hexagonal-architectures/adapters/StocksDB.py +++ b/python-test-samples/hexagonal-architectures/src/adapters/StocksDB.py @@ -1,6 +1,8 @@ from os import environ import json import boto3 +from botocore.exceptions import ClientError + def getStockValue(stockID): try: @@ -9,7 +11,10 @@ def getStockValue(stockID): dynamodb_table = dynamodb_resource.Table(dynamodb_table_name) print(f"Using DynamoDB Table {dynamodb_table_name}.") dynamodb_response = dynamodb_table.get_item(Key={"STOCK_ID": f"{stockID}"}) - dynamodb_response = {"STOCK_ID": "1", "VALUE": 3} + if "Item" not in dynamodb_response: + raise ValueError("Stock not found") + print("dynamodb response", dynamodb_response) return dynamodb_response except Exception as e: - print(e) \ No newline at end of file + print(e) + raise \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/domains/__init__.py b/python-test-samples/hexagonal-architectures/src/adapters/__init__.py similarity index 100% rename from python-test-samples/hexagonal-architectures/domains/__init__.py rename to python-test-samples/hexagonal-architectures/src/adapters/__init__.py diff --git a/python-test-samples/hexagonal-architectures/src/app.py b/python-test-samples/hexagonal-architectures/src/app.py new file mode 100644 index 00000000..e267fff6 --- /dev/null +++ b/python-test-samples/hexagonal-architectures/src/app.py @@ -0,0 +1,25 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 + +# Lambda Handler for the Python hexagonal-architectures example +# This handler accepts a stock ID and provides the price of the stock in four currencies. +# The id and price in Euros associated with the stock is stored in a DynamoDB Table. +# The currency conversion rates for Euros to USD, CAD, and AUD are stored in another DynamoDB table. + +from os import environ +from datetime import datetime +from src.adapters import HandleStockRequest + +# from aws_lambda_powertools.utilities.data_classes import APIGatewayProxyEvent +# from aws_lambda_powertools.utilities.typing import LambdaContext +# from aws_lambda_powertools.utilities.validation import validator + +def lambda_handler(event, context) -> dict: + try: + stockID = event["pathParameters"]["stockID"] + response = HandleStockRequest.getStocksRequest(stockID) + print("Response", response) + return response + except Exception as e: + print(e) + raise \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/ports/__init__.py b/python-test-samples/hexagonal-architectures/src/domains/__init__.py similarity index 100% rename from python-test-samples/hexagonal-architectures/ports/__init__.py rename to python-test-samples/hexagonal-architectures/src/domains/__init__.py diff --git a/python-test-samples/hexagonal-architectures/src/domains/stock.py b/python-test-samples/hexagonal-architectures/src/domains/stock.py new file mode 100644 index 00000000..9b5d864d --- /dev/null +++ b/python-test-samples/hexagonal-architectures/src/domains/stock.py @@ -0,0 +1,29 @@ +from decimal import Decimal +from src.ports import CurrenciesService + +from src.ports import StocksService + +currencies = ["USD", "CAD", "AUD"] + +def retrieveStockValues(stockID): + try: + stockValue = StocksService.getStockData(stockID) + currencyList = CurrenciesService.getCurrenciesData(currencies) + print("STOCK VALUE", stockValue) + print("CURRENCY LIST", currencyList) + + stockWithCurrencies = { + "stock": stockValue["Item"]["STOCK_ID"], + "values": { + "EUR": float(stockValue["Item"]["Value"]) + } + } + print(stockWithCurrencies) + for currency in currencies: + print(currencyList[currency]) + stockWithCurrencies["values"][currency] = float(Decimal(stockValue["Item"]["Value"]) * currencyList[currency]) + print(stockWithCurrencies) + return stockWithCurrencies + except ValueError as e: + print(e) + raise \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/ports/CurrenciesService.py b/python-test-samples/hexagonal-architectures/src/ports/CurrenciesService.py similarity index 79% rename from python-test-samples/hexagonal-architectures/ports/CurrenciesService.py rename to python-test-samples/hexagonal-architectures/src/ports/CurrenciesService.py index d375da62..444f197c 100644 --- a/python-test-samples/hexagonal-architectures/ports/CurrenciesService.py +++ b/python-test-samples/hexagonal-architectures/src/ports/CurrenciesService.py @@ -1,4 +1,4 @@ -from adapters import CurrencyExchangeDB +from src.adapters import CurrencyExchangeDB def getCurrenciesData(currencies): try: diff --git a/python-test-samples/hexagonal-architectures/ports/HttpHandler.py b/python-test-samples/hexagonal-architectures/src/ports/HttpHandler.py similarity index 73% rename from python-test-samples/hexagonal-architectures/ports/HttpHandler.py rename to python-test-samples/hexagonal-architectures/src/ports/HttpHandler.py index ae7796bd..ec282dbe 100644 --- a/python-test-samples/hexagonal-architectures/ports/HttpHandler.py +++ b/python-test-samples/hexagonal-architectures/src/ports/HttpHandler.py @@ -1,8 +1,9 @@ -from domains import stock +from src.domains import stock def retrieveStock(stockID): try: stockWithCurrencies = stock.retrieveStockValues(stockID) return stockWithCurrencies except Exception as e: - print(e) \ No newline at end of file + print(e) + raise \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/ports/StocksService.py b/python-test-samples/hexagonal-architectures/src/ports/StocksService.py similarity index 67% rename from python-test-samples/hexagonal-architectures/ports/StocksService.py rename to python-test-samples/hexagonal-architectures/src/ports/StocksService.py index effce15a..1bcda8a9 100644 --- a/python-test-samples/hexagonal-architectures/ports/StocksService.py +++ b/python-test-samples/hexagonal-architectures/src/ports/StocksService.py @@ -1,8 +1,9 @@ -from adapters import StocksDB +from src.adapters import StocksDB def getStockData(stockID): try: data = StocksDB.getStockValue(stockID) return data except Exception as e: - print(e) \ No newline at end of file + print(e) + raise \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/ports/__init__.py b/python-test-samples/hexagonal-architectures/src/ports/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python-test-samples/hexagonal-architectures/template.yaml b/python-test-samples/hexagonal-architectures/template.yaml index d6800c8c..0383d8ef 100644 --- a/python-test-samples/hexagonal-architectures/template.yaml +++ b/python-test-samples/hexagonal-architectures/template.yaml @@ -10,7 +10,7 @@ Resources: StocksConverterFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./ + CodeUri: src/ Handler: app.lambda_handler Runtime: python3.9 MemorySize: 256 diff --git a/python-test-samples/hexagonal-architectures/tests/events/testevent-failure.json b/python-test-samples/hexagonal-architectures/tests/events/testevent-failure.json new file mode 100644 index 00000000..d58f7ffb --- /dev/null +++ b/python-test-samples/hexagonal-architectures/tests/events/testevent-failure.json @@ -0,0 +1,69 @@ +{ + "version": "2.0", + "routeKey": "$default", + "rawPath": "/path/to/resource", + "rawQueryString": "parameter1=value1¶meter1=value2¶meter2=value", + "cookies": [ + "cookie1", + "cookie2" + ], + "headers": { + "Header1": "value1", + "Header2": "value1,value2" + }, + "queryStringParameters": { + "parameter1": "value1,value2", + "parameter2": "value" + }, + "requestContext": { + "accountId": "123456789012", + "apiId": "api-id", + "authentication": { + "clientCert": { + "clientCertPem": "CERT_CONTENT", + "subjectDN": "www.example.com", + "issuerDN": "Example issuer", + "serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1", + "validity": { + "notBefore": "May 28 12:30:02 2019 GMT", + "notAfter": "Aug 5 09:36:04 2021 GMT" + } + } + }, + "authorizer": { + "jwt": { + "claims": { + "claim1": "value1", + "claim2": "value2" + }, + "scopes": [ + "scope1", + "scope2" + ] + } + }, + "domainName": "id.execute-api.us-east-1.amazonaws.com", + "domainPrefix": "id", + "http": { + "method": "POST", + "path": "/path/to/resource", + "protocol": "HTTP/1.1", + "sourceIp": "192.168.0.1/32", + "userAgent": "agent" + }, + "requestId": "id", + "routeKey": "$default", + "stage": "$default", + "time": "12/Mar/2020:19:03:58 +0000", + "timeEpoch": 1583348638390 + }, + "body": "eyJ0ZXN0IjoiYm9keSJ9", + "pathParameters": { + "stockID": "2" + }, + "isBase64Encoded": true, + "stageVariables": { + "stageVariable1": "value1", + "stageVariable2": "value2" + } +} diff --git a/python-test-samples/hexagonal-architectures/testevent.json b/python-test-samples/hexagonal-architectures/tests/events/testevent.json similarity index 100% rename from python-test-samples/hexagonal-architectures/testevent.json rename to python-test-samples/hexagonal-architectures/tests/events/testevent.json diff --git a/python-test-samples/hexagonal-architectures/tests/unit/__init__.py b/python-test-samples/hexagonal-architectures/tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python-test-samples/hexagonal-architectures/tests/unit/mock_test.py b/python-test-samples/hexagonal-architectures/tests/unit/mock_test.py new file mode 100644 index 00000000..b5cd87ca --- /dev/null +++ b/python-test-samples/hexagonal-architectures/tests/unit/mock_test.py @@ -0,0 +1,133 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 + +from decimal import Decimal +from os import environ +import json +from datetime import datetime +from unittest import TestCase +from typing import Any, Dict +from uuid import uuid4 +import pytest +import yaml +import boto3 +from boto3.dynamodb.conditions import Key +import moto + + +# Import the handler under test +from src import app + +# Mock the DynamoDB Service during the test +@moto.mock_dynamodb + +class TestSampleLambdaWithDynamoDB(TestCase): + """ + Unit Test class for src/app.py + """ + + def setUp(self) -> None: + """ + Test Set up: + 1. Create the lambda environment variable STOCKS_DB_TABLE and CURRENCIES_DB_TABLE + 2. Build DynamoDB Tables according to the SAM template + 4. Populate DynamoDB Data into the Tables for test + """ + + # Create a name for a test table, and set the environment + self.test_stocks_table_name = "unit_test_stock_table_name" + environ["STOCKS_DB_TABLE"] = self.test_stocks_table_name + + # Create a mock table using the definition from the SAM YAML template + # This simple technique works if there are no intrinsics (like !If or !Ref) in the + # resource properties for KeySchema, AttributeDefinitions, & BillingMode. + sam_template_table_properties = self.read_sam_template()["Resources"]["StocksTable"]["Properties"] + self.mock_dynamodb = boto3.resource("dynamodb") + self.mock_dynamodb_table = self.mock_dynamodb.create_table( + TableName = self.test_stocks_table_name, + KeySchema = sam_template_table_properties["KeySchema"], + AttributeDefinitions = sam_template_table_properties["AttributeDefinitions"], + BillingMode = sam_template_table_properties["BillingMode"] + ) + + # Populate data for the tests + self.mock_dynamodb_table.put_item(Item={ + "STOCK_ID": "1", + "Value": 3}) + + # Create a name for a test table, and set the environment + self.test_currencies_table_name = "unit_test_currencies_table_name" + environ["CURRENCIES_DB_TABLE"] = self.test_currencies_table_name + + # Create a mock table using the definition from the SAM YAML template + # This simple technique works if there are no intrinsics (like !If or !Ref) in the + # resource properties for KeySchema, AttributeDefinitions, & BillingMode. + sam_template_table_properties = self.read_sam_template()["Resources"]["CurrenciesTable"]["Properties"] + self.mock_dynamodb = boto3.resource("dynamodb") + self.mock_currencies_table = self.mock_dynamodb.create_table( + TableName = self.test_currencies_table_name, + KeySchema = sam_template_table_properties["KeySchema"], + AttributeDefinitions = sam_template_table_properties["AttributeDefinitions"], + BillingMode = sam_template_table_properties["BillingMode"] + ) + + # Populate data for the tests + self.mock_currencies_table.put_item(Item={ + "CURRENCY": "USD", + "Rate": Decimal("1.31")}) + self.mock_currencies_table.put_item(Item={ + "CURRENCY": "CAD", + "Rate": Decimal("1.41")}) + self.mock_currencies_table.put_item(Item={ + "CURRENCY": "AUD", + "Rate": Decimal("1.51")}) + def tearDown(self) -> None: + """ + For teardown, remove the mocked table & environment variable + """ + self.mock_dynamodb_table.delete() + del environ['STOCKS_DB_TABLE'] + + def read_sam_template(self, sam_template_fn : str = "template.yaml" ) -> dict: + """ + Utility Function to read the SAM template for the current project + """ + with open(sam_template_fn, "r") as fp: + template =fp.read().replace("!","") # Ignoring intrinsic tags + return yaml.safe_load(template) + + def load_test_event(self, test_event_file_name: str) -> Dict[str, Any]: + """ + Load a sample event from a file + """ + with open(f"tests/events/{test_event_file_name}.json","r") as f: + event = json.load(f) + return event + + + def test_lambda_handler_happy_path(self): + """ + Happy path test where the stock ID exists in the DynamoDB Table + + Since the environment variable STOCKS_DB_TABLE and CURRENCIES_DB_TABLE + are set and DynamoDB is mocked for the entire class, this test will + implicitly use the mocked DynamoDB table we created in setUp. + """ + + test_event = self.load_test_event("testevent") + test_return = app.lambda_handler(event=test_event,context=None) + self.assertEqual( test_return["statusCode"] , 200) + self.assertEqual( test_return["body"] , '{"message": {"stock": "1", "values": {"EUR": 3.0, "USD": 3.93, "CAD": 4.23, "AUD": 4.53}}}') + + def test_lambda_handler_failure(self): + """ + Failure Test where the stock ID does not exist in the DynamoDB Table + + Since the environment variable STOCKS_DB_TABLE and CURRENCIES_DB_TABLE + are set and DynamoDB is mocked for the entire class, this test will + implicitly use the mocked DynamoDB table we created in setUp. + """ + + test_event = self.load_test_event("testevent-failure") + with pytest.raises(ValueError): + test_return = app.lambda_handler(event=test_event,context=None) \ No newline at end of file From ad468564faf2de92148e3c001776f275f6ceb401 Mon Sep 17 00:00:00 2001 From: Rohan Mehta Date: Mon, 10 Apr 2023 15:50:57 -0400 Subject: [PATCH 03/16] update readme --- .../hexagonal-architectures/README.md | 142 ++++++++++++++++++ .../hexagonal-architectures/template.yaml | 4 +- 2 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 python-test-samples/hexagonal-architectures/README.md diff --git a/python-test-samples/hexagonal-architectures/README.md b/python-test-samples/hexagonal-architectures/README.md new file mode 100644 index 00000000..d081a5d4 --- /dev/null +++ b/python-test-samples/hexagonal-architectures/README.md @@ -0,0 +1,142 @@ +[![python: 3.9](https://img.shields.io/badge/Python-3.9-green)](https://img.shields.io/badge/Python-3.9-green) +[![AWS: DynamoDB](https://img.shields.io/badge/AWS-DynamoDB-blueviolet)](https://img.shields.io/badge/AWS-DynamoDB-blueviolet) +[![test: unit](https://img.shields.io/badge/Test-Unit-blue)](https://img.shields.io/badge/Test-Unit-blue) +[![test: integration](https://img.shields.io/badge/Test-Integration-yellow)](https://img.shields.io/badge/Test-Integration-yellow) + +# Python: Hexagonal Architecture Example + +## Introduction +Hexagonal architecture is a pattern used for encapsulating domain logic and decoupling it from other implementation details, such as infrastructure or client requests. You can use these types of architectures to improve how to organize and test your Lambda functions. + +System Under Test (SUT) + +The SUT in this pattern is a Lambda function that is organized using a hexagonal architecture. You can read this blog post to learn more about these types of architectures. The example in this test pattern receives a request via API Gateway and makes calls out to other AWS cloud services like DynamoDB. + +The project uses the [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) (SAM) CLI for configuration, testing and deployment. + +--- + +## Contents +- [Python: Hexagonal Architecture Example](#python-hexagonal-architecture-example) + - [Introduction](#introduction) + - [Contents](#contents) + - [Key Files in the Project](#key-files-in-the-project) + - [Sample project description](#sample-project-description) + - [Testing Data Considerations](#testing-data-considerations) + - [Run the Unit Test](#run-the-unit-test) + - [Run the Integration Test](#run-the-integration-test) +--- + +## Key Files in the Project + - [app.py](src/app.py) - Lambda handler code to test + - [template.yaml](template.yaml) - SAM script for deployment + - [mock_test.py](tests/unit/mock_test.py) - Unit test using mocks + - [test_api_gateway.py](tests/integration/test_api_gateway.py) - Integration tests on a live stack + +[Top](#contents) + +--- + +## Sample project description + +Hexagonal Architecture: +Hexagonal architecture is also known as the ports and adapters architecture. It is an architectural pattern used for encapsulating domain logic and decoupling it from other implementation details, such as infrastructure or client requests. + +In Lambda functions, hexagonal architecture can help you implement new business requirements and improve the agility of a workload. This approach can help create separation of concerns and separate the domain logic from the infrastructure. For development teams, it can also simplify the implementation of new features and parallelize the work across different developers. + +Terms: + +1. Domain logic: Represents the task that the application should perform, abstracting any interaction with the external world. +2. Ports: Provide a way for the primary actors (on the left) to interact with the application, via the domain logic. The domain logic also uses ports for interacting with secondary actors (on the right) when needed. +3. Adapters: A design pattern for transforming one interface into another interface. They wrap the logic for interacting with a primary or secondary actor. +4. Primary actors: Users of the system such as a webhook, a UI request, or a test script. +5. Secondary actors: used by the application, these services are either a Repository (for example, a database) or a Recipient (such as a message queue). + +Application Description +The example application is a backend web service built using Amazon API Gateway, AWS Lambda, and Amazon DynamoDB. Business logic in the domain layer should be tested with unit tests. Responses from secondary actors via ports should be mocked during unit testing to speed up test execution. + +Adapter and port code can be tested in the cloud by deploying primary and secondary actors such as an API Gateway and a DynamoDB table. The test code will create an HTTP client that will send requests to the deployed API Gateway endpoint. The endpoint will invoke the primary actor, test resource configuration, IAM permissions, authorizers, internal business logic, and secondary actors of the SUT. + +This project consists of an [API Gateway](https://aws.amazon.com/api-gateway/), a single [AWS Lambda](https://aws.amazon.com/lambda) function, and 2 [Amazon DynamoDB](https://aws.amazon.com/dynamodb) tables. + +The two DynamoDB tables are meant to track Stock ID's and prices in EUR (Euros) and Euro Currency Conversion rates. + +[Top](#contents) + +--- + +## Testing Data Considerations + +Data persistence brings additional testing considerations. + +First, the data stores must be pre-populated with data to test certain functionality. In our example, we need a valid stock and valid currency conversion data to test our function. Therefore, we will add data to the data stores prior to running the tests. This data seeding operation is performed in the test setup. + +Second, the data store will be populated as a side-effect of our testing. In our example, stock and currency conversion data will be populated in our DynamoDB tables. To prevent unintended side-effects, we will clean-up data generated during the test execution. This data cleaning operation is performed in the test tear-down. + +[Top](#contents) + +--- + +## Run the Unit Test +[mock_test.py](tests/unit/mock_test.py) + +In the [unit test](tests/unit/mock_test.py), all references and calls to the DynamoDB service [are mocked on line 18](tests/unit/mock_test.py#L20). + +The unit test establishes the STOCKS_DB_TABLE and CURRENCIES_DB_TABLE environment +variables that the Lambda function uses to reference the DynamoDB tables. STOCKS_DB_TABLE and CURRENCIES_DB_TABLE are defined in the [setUp method of test class in mock_test.py](tests/unit/mock_test.py#L37-38). + +In a unit test, you must create a mocked version of the DynamoDB table. The example approach in the [setUp method of test class in mock_test.py](tests/unit/mock_test.py#L43-50) reads in the DynamoDB table schema directly the [SAM Template](template.yaml) so that the definition is maintained in one place. This simple technique works if there are no intrinsics (like !If or !Ref) in the resource properties for KeySchema, AttributeDefinitions, & BillingMode. Once the mocked table is created, test data is populated. + +With the mocked DynamoDB table created and the STOCKS_DB_TABLE and CURRENCIES_DB_TABLE set to the mocked table names, the Lambda function will use the mocked DynamoDB tables when executing. + +The [unit test tear-down](tests/unit/mock_test.py#L61-66) removes the mocked DynamoDB tables and clears the STOCKS_DB_TABLE and CURRENCIES_DB_TABLE environment variables. + +To run the unit test, execute the following +```shell +# Create and Activate a Python Virtual Environment +# One-time setup +apigw-lambda-dynamodb$ pip3 install virtualenv +apigw-lambda-dynamodb$ python3 -m venv venv +apigw-lambda-dynamodb$ source ./venv/bin/activate + +# install dependencies +apigw-lambda-dynamodb$ pip3 install -r tests/requirements.txt + +# run unit tests with mocks +apigw-lambda-dynamodb$ python3 -m pytest -s tests/unit -v +``` + +[Top](#contents) + +--- + +## Run the Integration Test + +(Coming Soon) + +[test_api_gateway.py](tests/integration/test_api_gateway.py) + +For integration tests, the full stack is deployed before testing: +```shell +apigw-lambda-dynamodb$ sam build +apigw-lambda-dynamodb$ sam deploy --guided +``` + +The [integration test](tests/integration/test_api_gateway.py) setup determines both the [API endpoint](tests/integration/test_api_gateway.py#L50-53) and the name of the [DynamoDB table](tests/integration/test_api_gateway.py#L56-58) in the stack. + +The integration test then [populates data into the DyanamoDB table](tests/integration/test_api_gateway.py#L66-70). + +The [integration test tear-down](tests/integration/test_api_gateway.py#L73-87) removes the seed data, as well as data generated during the test. + +To run the integration test, create the environment variable "AWS_SAM_STACK_NAME" with the name of the test stack, and execute the test. + +```shell +# Set the environment variables AWS_SAM_STACK_NAME and (optionally)AWS_DEFAULT_REGION +# to match the name of the stack and the region where you will test + +apigw-lambda-dynamodb$ AWS_SAM_STACK_NAME= AWS_DEFAULT_REGION= python -m pytest -s tests/integration -v +``` + +[Top](#contents) + +--- \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/template.yaml b/python-test-samples/hexagonal-architectures/template.yaml index 0383d8ef..bdb78af8 100644 --- a/python-test-samples/hexagonal-architectures/template.yaml +++ b/python-test-samples/hexagonal-architectures/template.yaml @@ -10,8 +10,8 @@ Resources: StocksConverterFunction: Type: AWS::Serverless::Function Properties: - CodeUri: src/ - Handler: app.lambda_handler + CodeUri: ./ + Handler: src.app.lambda_handler Runtime: python3.9 MemorySize: 256 Policies: From 76ea9a767528fd14db9bb71adcbddfa2c4ed1e7b Mon Sep 17 00:00:00 2001 From: Rohan Mehta Date: Mon, 10 Apr 2023 15:52:09 -0400 Subject: [PATCH 04/16] Update readme for code coverage command --- .../hexagonal-architectures/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/python-test-samples/hexagonal-architectures/README.md b/python-test-samples/hexagonal-architectures/README.md index d081a5d4..ff5ff81a 100644 --- a/python-test-samples/hexagonal-architectures/README.md +++ b/python-test-samples/hexagonal-architectures/README.md @@ -95,15 +95,15 @@ To run the unit test, execute the following ```shell # Create and Activate a Python Virtual Environment # One-time setup -apigw-lambda-dynamodb$ pip3 install virtualenv -apigw-lambda-dynamodb$ python3 -m venv venv -apigw-lambda-dynamodb$ source ./venv/bin/activate +hexagonal-architectures$ pip3 install virtualenv +hexagonal-architectures$ python3 -m venv venv +hexagonal-architectures$ source ./venv/bin/activate # install dependencies -apigw-lambda-dynamodb$ pip3 install -r tests/requirements.txt +hexagonal-architectures$ pip3 install -r tests/requirements.txt # run unit tests with mocks -apigw-lambda-dynamodb$ python3 -m pytest -s tests/unit -v +hexagonal-architectures$ python3 -m coverage run -m pytest ``` [Top](#contents) @@ -118,13 +118,13 @@ apigw-lambda-dynamodb$ python3 -m pytest -s tests/unit -v For integration tests, the full stack is deployed before testing: ```shell -apigw-lambda-dynamodb$ sam build -apigw-lambda-dynamodb$ sam deploy --guided +hexagonal-architectures$ sam build +hexagonal-architectures$ sam deploy --guided ``` The [integration test](tests/integration/test_api_gateway.py) setup determines both the [API endpoint](tests/integration/test_api_gateway.py#L50-53) and the name of the [DynamoDB table](tests/integration/test_api_gateway.py#L56-58) in the stack. -The integration test then [populates data into the DyanamoDB table](tests/integration/test_api_gateway.py#L66-70). +The integration test then [populates data into the DynamoDB table](tests/integration/test_api_gateway.py#L66-70). The [integration test tear-down](tests/integration/test_api_gateway.py#L73-87) removes the seed data, as well as data generated during the test. @@ -134,7 +134,7 @@ To run the integration test, create the environment variable "AWS_SAM_STACK_NAME # Set the environment variables AWS_SAM_STACK_NAME and (optionally)AWS_DEFAULT_REGION # to match the name of the stack and the region where you will test -apigw-lambda-dynamodb$ AWS_SAM_STACK_NAME= AWS_DEFAULT_REGION= python -m pytest -s tests/integration -v +hexagonal-architectures$ AWS_SAM_STACK_NAME= AWS_DEFAULT_REGION= python -m pytest -s tests/integration -v ``` [Top](#contents) From e4ac402a34434c0dc70ffdd9afadd27a99b66278 Mon Sep 17 00:00:00 2001 From: Rohan Mehta Date: Mon, 17 Apr 2023 13:51:36 -0400 Subject: [PATCH 05/16] Update requirements.txt --- python-test-samples/hexagonal-architectures/requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python-test-samples/hexagonal-architectures/requirements.txt b/python-test-samples/hexagonal-architectures/requirements.txt index 3df75db3..7762dee9 100644 --- a/python-test-samples/hexagonal-architectures/requirements.txt +++ b/python-test-samples/hexagonal-architectures/requirements.txt @@ -1,4 +1,6 @@ aws-xray-sdk aws_lambda_powertools fastjsonschema -boto3 \ No newline at end of file +boto3 +pytest +moto \ No newline at end of file From 82740131fa7e5bfdb2978c98a73b92eb1aed4ac7 Mon Sep 17 00:00:00 2001 From: Rohan Mehta Date: Wed, 19 Apr 2023 13:55:25 -0400 Subject: [PATCH 06/16] Add blank metadata file --- python-test-samples/hexagonal-architectures/metadata.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 python-test-samples/hexagonal-architectures/metadata.json diff --git a/python-test-samples/hexagonal-architectures/metadata.json b/python-test-samples/hexagonal-architectures/metadata.json new file mode 100644 index 00000000..e69de29b From b8f22e376c063489a8baabf80c5ad9938115a2ee Mon Sep 17 00:00:00 2001 From: Rohan Mehta Date: Wed, 19 Apr 2023 13:59:42 -0400 Subject: [PATCH 07/16] Add hexagonal architecture metadata file --- python-test-samples/hexagonal-architectures/metadata.json | 1 + 1 file changed, 1 insertion(+) diff --git a/python-test-samples/hexagonal-architectures/metadata.json b/python-test-samples/hexagonal-architectures/metadata.json index e69de29b..9e26dfee 100644 --- a/python-test-samples/hexagonal-architectures/metadata.json +++ b/python-test-samples/hexagonal-architectures/metadata.json @@ -0,0 +1 @@ +{} \ No newline at end of file From 542cb756b2fa5aa90a35b54fd04f559d2bb89042 Mon Sep 17 00:00:00 2001 From: Rohan Mehta Date: Wed, 19 Apr 2023 14:04:30 -0400 Subject: [PATCH 08/16] Fix src reference --- .../hexagonal-architectures/tests/__init__.py | 0 .../hexagonal-architectures/tests/unit/requirements.txt | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 python-test-samples/hexagonal-architectures/tests/__init__.py create mode 100644 python-test-samples/hexagonal-architectures/tests/unit/requirements.txt diff --git a/python-test-samples/hexagonal-architectures/tests/__init__.py b/python-test-samples/hexagonal-architectures/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python-test-samples/hexagonal-architectures/tests/unit/requirements.txt b/python-test-samples/hexagonal-architectures/tests/unit/requirements.txt new file mode 100644 index 00000000..7762dee9 --- /dev/null +++ b/python-test-samples/hexagonal-architectures/tests/unit/requirements.txt @@ -0,0 +1,6 @@ +aws-xray-sdk +aws_lambda_powertools +fastjsonschema +boto3 +pytest +moto \ No newline at end of file From b8b2bb586f0442e29353f51180610921caf28e94 Mon Sep 17 00:00:00 2001 From: Rohan Mehta Date: Wed, 19 Apr 2023 14:38:52 -0400 Subject: [PATCH 09/16] Update readme --- .../hexagonal-architectures/README.md | 9 ++++++--- .../img/hexagonal-architecture-diagram.png | Bin 0 -> 205813 bytes .../tests/unit/requirements.txt | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 python-test-samples/hexagonal-architectures/img/hexagonal-architecture-diagram.png diff --git a/python-test-samples/hexagonal-architectures/README.md b/python-test-samples/hexagonal-architectures/README.md index ff5ff81a..40104682 100644 --- a/python-test-samples/hexagonal-architectures/README.md +++ b/python-test-samples/hexagonal-architectures/README.md @@ -10,7 +10,7 @@ Hexagonal architecture is a pattern used for encapsulating domain logic and deco System Under Test (SUT) -The SUT in this pattern is a Lambda function that is organized using a hexagonal architecture. You can read this blog post to learn more about these types of architectures. The example in this test pattern receives a request via API Gateway and makes calls out to other AWS cloud services like DynamoDB. +The SUT in this pattern is a Lambda function that is organized using a hexagonal architecture. You can read this [blog post](https://aws.amazon.com/blogs/compute/developing-evolutionary-architecture-with-aws-lambda/) to learn more about these types of architectures. The example in this test pattern receives a request via API Gateway and makes calls out to other AWS cloud services like DynamoDB. The project uses the [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) (SAM) CLI for configuration, testing and deployment. @@ -22,6 +22,8 @@ The project uses the [AWS Serverless Application Model](https://docs.aws.amazon. - [Contents](#contents) - [Key Files in the Project](#key-files-in-the-project) - [Sample project description](#sample-project-description) + - [Terms:](#terms) + - [Application Description](#application-description) - [Testing Data Considerations](#testing-data-considerations) - [Run the Unit Test](#run-the-unit-test) - [Run the Integration Test](#run-the-integration-test) @@ -44,15 +46,16 @@ Hexagonal architecture is also known as the ports and adapters architecture. It In Lambda functions, hexagonal architecture can help you implement new business requirements and improve the agility of a workload. This approach can help create separation of concerns and separate the domain logic from the infrastructure. For development teams, it can also simplify the implementation of new features and parallelize the work across different developers. -Terms: +[Diagram](img/hexagonal-architecture-diagram.png) +### Terms: 1. Domain logic: Represents the task that the application should perform, abstracting any interaction with the external world. 2. Ports: Provide a way for the primary actors (on the left) to interact with the application, via the domain logic. The domain logic also uses ports for interacting with secondary actors (on the right) when needed. 3. Adapters: A design pattern for transforming one interface into another interface. They wrap the logic for interacting with a primary or secondary actor. 4. Primary actors: Users of the system such as a webhook, a UI request, or a test script. 5. Secondary actors: used by the application, these services are either a Repository (for example, a database) or a Recipient (such as a message queue). -Application Description +### Application Description The example application is a backend web service built using Amazon API Gateway, AWS Lambda, and Amazon DynamoDB. Business logic in the domain layer should be tested with unit tests. Responses from secondary actors via ports should be mocked during unit testing to speed up test execution. Adapter and port code can be tested in the cloud by deploying primary and secondary actors such as an API Gateway and a DynamoDB table. The test code will create an HTTP client that will send requests to the deployed API Gateway endpoint. The endpoint will invoke the primary actor, test resource configuration, IAM permissions, authorizers, internal business logic, and secondary actors of the SUT. diff --git a/python-test-samples/hexagonal-architectures/img/hexagonal-architecture-diagram.png b/python-test-samples/hexagonal-architectures/img/hexagonal-architecture-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..7d53c0a62ed90e275c82774f0c066411d70dcc66 GIT binary patch literal 205813 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 c9)(qY7bV#z-D%-d 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∓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

W~p8Dk#|oU6Z_52V$p3)uY8a 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 z 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ℑmBd_7AQRn^!Ym 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 HcOLy0prN!^ literal 0 HcmV?d00001 diff --git a/python-test-samples/hexagonal-architectures/tests/unit/requirements.txt b/python-test-samples/hexagonal-architectures/tests/unit/requirements.txt index 7762dee9..89c643bb 100644 --- a/python-test-samples/hexagonal-architectures/tests/unit/requirements.txt +++ b/python-test-samples/hexagonal-architectures/tests/unit/requirements.txt @@ -3,4 +3,5 @@ aws_lambda_powertools fastjsonschema boto3 pytest -moto \ No newline at end of file +moto +pytest-cov \ No newline at end of file From 7d052fd66c0445967030d78f2d1b17a2e9113029 Mon Sep 17 00:00:00 2001 From: Rohan Mehta Date: Wed, 19 Apr 2023 14:48:20 -0400 Subject: [PATCH 10/16] initial version of metadata file --- .../hexagonal-architectures/metadata.json | 36 ++++++++++++++++++- .../tests/integration/test_api_gateway.py | 0 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 python-test-samples/hexagonal-architectures/tests/integration/test_api_gateway.py diff --git a/python-test-samples/hexagonal-architectures/metadata.json b/python-test-samples/hexagonal-architectures/metadata.json index 9e26dfee..ce79e1e6 100644 --- a/python-test-samples/hexagonal-architectures/metadata.json +++ b/python-test-samples/hexagonal-architectures/metadata.json @@ -1 +1,35 @@ -{} \ No newline at end of file +{ + "title": "Hexagonal Architecture", + "description": "This project contains unit and integration tests for an application designed with Hexagonal Architecture.", + "content_language": "English", + "language": "Python", + "type": ["Unit", "Integration"], + "diagram": "/img/hexagonal-architecture-diagram.png", + "framework": "SAM", + "services": ["apigw", "lambda", "dynamodb"], + "git_repo_url": "https://github.com/aws-samples/serverless-test-samples", + "pattern_source": "AWS", + "pattern_detail_tabs": [ + { + "title": "Application Code", + "filepath": "/src/app.py" + }, + { + "title": "Unit Tests", + "filepath": "/tests/unit/mock_test.py" + }, + { + "title": "Integration Test", + "filepath": "/tests/integration/test_api_gateway.py" + } + ], + "authors": [ + { + "name": "Rohan Mehta", + "image": "https://media.licdn.com/dms/image/C4D03AQG1qfZlu1eemw/profile-displayphoto-shrink_800_800/0/1573532217447?e=1687392000&v=beta&t=732VxZY4sKbyP0gkofdK4KJIbV0dRFpfxqos_KR_PYQ", + "bio": "Cloud Application Architect at AWS", + "linkedin": "https://www.linkedin.com/in/rohan-mehta-dev/", + "twitter": "https://twitter.com/rohanmehta_dev" + } + ] +} \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/tests/integration/test_api_gateway.py b/python-test-samples/hexagonal-architectures/tests/integration/test_api_gateway.py new file mode 100644 index 00000000..e69de29b From 375633e3e0ac9da947179b472bcd6834de7b8326 Mon Sep 17 00:00:00 2001 From: Rohan Mehta Date: Fri, 21 Apr 2023 10:29:41 -0400 Subject: [PATCH 11/16] Loop over modified folders --- .github/workflows/metadata-validation.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/metadata-validation.yml b/.github/workflows/metadata-validation.yml index cbfe56d5..3d463c2d 100644 --- a/.github/workflows/metadata-validation.yml +++ b/.github/workflows/metadata-validation.yml @@ -44,5 +44,8 @@ jobs: - name: Validate Schema run: | - pip install -r .github/workflows/requirements.txt - python .github/workflows/metadata_json_validator.py ${{ steps.setfolders.outputs.folders }} \ No newline at end of file + for folder in ${{ steps.setfolders.outputs.folders }} + do + echo $folder + pip install -r .github/workflows/requirements.txt + python .github/workflows/metadata_json_validator.py $folder \ No newline at end of file From c330939c9159aabc37d5d6b07fc7dc5d1b34731b Mon Sep 17 00:00:00 2001 From: Rohan Mehta Date: Tue, 25 Apr 2023 02:25:20 -0400 Subject: [PATCH 12/16] Update with working integration tests --- .../hexagonal-architectures/src/app.py | 10 +- .../hexagonal-architectures/template.yaml | 18 ++- .../tests/integration/__init__.py | 0 .../tests/integration/test_api_gateway.py | 113 ++++++++++++++++++ 4 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 python-test-samples/hexagonal-architectures/tests/integration/__init__.py diff --git a/python-test-samples/hexagonal-architectures/src/app.py b/python-test-samples/hexagonal-architectures/src/app.py index e267fff6..4a597304 100644 --- a/python-test-samples/hexagonal-architectures/src/app.py +++ b/python-test-samples/hexagonal-architectures/src/app.py @@ -16,10 +16,16 @@ def lambda_handler(event, context) -> dict: try: - stockID = event["pathParameters"]["stockID"] + print(event) + stockID = event["pathParameters"]["StockID"] response = HandleStockRequest.getStocksRequest(stockID) print("Response", response) return response + except ValueError as e: + print("V", e) + return { + "statusCode": 404, + "body": "Stock not found" + } except Exception as e: - print(e) raise \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/template.yaml b/python-test-samples/hexagonal-architectures/template.yaml index bdb78af8..fc93ad2d 100644 --- a/python-test-samples/hexagonal-architectures/template.yaml +++ b/python-test-samples/hexagonal-architectures/template.yaml @@ -58,4 +58,20 @@ Resources: - GET - POST AllowOrigins: - - "*" \ No newline at end of file + - "*" +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + StockConverterApi: + Description: "API Gateway endpoint URL for Prod stage for Stock Converter function" + Value: !Sub "https://${StocksGateway}.execute-api.${AWS::Region}.amazonaws.com/stock/{stock_id}" + StocksConverterFunction: + Description: "Stock Converter Lambda Function ARN" + Value: !GetAtt StocksConverterFunction.Arn + CurrenciesTableName: + Description: "Currencies Table Name" + Value: !Ref CurrenciesTable + StocksTableName: + Description: "Stocks Table Name" + Value: !Ref StocksTable diff --git a/python-test-samples/hexagonal-architectures/tests/integration/__init__.py b/python-test-samples/hexagonal-architectures/tests/integration/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python-test-samples/hexagonal-architectures/tests/integration/test_api_gateway.py b/python-test-samples/hexagonal-architectures/tests/integration/test_api_gateway.py index e69de29b..1ade43d0 100644 --- a/python-test-samples/hexagonal-architectures/tests/integration/test_api_gateway.py +++ b/python-test-samples/hexagonal-architectures/tests/integration/test_api_gateway.py @@ -0,0 +1,113 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 + +import os +from decimal import Decimal +from unittest import TestCase +from uuid import uuid4 +from boto3.dynamodb.conditions import Key +import boto3 +import requests + +""" +Set the environment variable AWS_SAM_STACK_NAME +to match the name of the stack you will test + +AWS_SAM_STACK_NAME= python -m pytest -s tests/integration -v +""" + +class TestApiGateway(TestCase): + api_endpoint: str + + aws_region = os.environ.get("AWS_DEFAULT_REGION") or "us-east-1" + + @classmethod + def get_stack_name(cls) -> str: + stack_name = os.environ.get("AWS_SAM_STACK_NAME") + if not stack_name: + raise Exception( + "Cannot find env var AWS_SAM_STACK_NAME. \n" + "Please setup this environment variable with the stack name where we are running integration tests." + ) + + return stack_name + + def setUp(self) -> None: + """ + Based on the provided env variable AWS_SAM_STACK_NAME, + We use the cloudformation API to retrieve the StockConverterApi URL and the DynamoDB Table Name + We also seed the DynamoDB Table for the test + """ + stack_name = TestApiGateway.get_stack_name() + + client = boto3.client("cloudformation") + print(stack_name) + try: + response = client.describe_stacks(StackName=stack_name) + except Exception as e: + raise Exception( + f"Cannot find stack {stack_name}. \n" f'Please make sure stack with the name "{stack_name}" exists.' + ) from e + + # StockConverterApi + print(stack_name, response) + stack_outputs = response["Stacks"][0]["Outputs"] + api_outputs = [output for output in stack_outputs if output["OutputKey"] == "StockConverterApi"] + self.assertTrue(api_outputs, f"Cannot find output StockConverterApi in stack {stack_name}") + self.api_endpoint = api_outputs[0]["OutputValue"] + + # CurrenciesTableName + currencies_dynamodb_outputs = [output for output in stack_outputs if output["OutputKey"] == "CurrenciesTableName"] + self.assertTrue(currencies_dynamodb_outputs, f"Cannot find output DynamoDBTableName in stack {stack_name}") + self.currencies_dynamodb_table_name = currencies_dynamodb_outputs[0]["OutputValue"] + + # Seed the Currencies DynamoDB Table with Test Data + dynamodb_resource = boto3.resource("dynamodb", region_name = self.aws_region) + currencies_dynamodb_table = dynamodb_resource.Table(name=self.currencies_dynamodb_table_name) + currencies_dynamodb_table.put_item(Item={"CURRENCY": "USD", + "Rate": Decimal("1.31")}) + currencies_dynamodb_table.put_item(Item={"CURRENCY": "CAD", + "Rate": Decimal("1.41")}) + currencies_dynamodb_table.put_item(Item={"CURRENCY": "AUD", + "Rate": Decimal("1.51")}) + + # StocksTableName + stocks_dynamodb_outputs = [output for output in stack_outputs if output["OutputKey"] == "StocksTableName"] + self.assertTrue(stocks_dynamodb_outputs, f"Cannot find output DynamoDBTableName in stack {stack_name}") + self.stocks_dynamodb_table_name = stocks_dynamodb_outputs[0]["OutputValue"] + + # Seed the Currencies DynamoDB Table with Test Data + dynamodb_resource = boto3.resource("dynamodb", region_name = self.aws_region) + stocks_dynamodb_table = dynamodb_resource.Table(name=self.stocks_dynamodb_table_name) + stocks_dynamodb_table.put_item(Item={"STOCK_ID": "1", + "Value": 3}) + + + def tearDown(self) -> None: + """ + # For tear-down, remove any data injected for the tests + """ + dynamodb_resource = boto3.resource("dynamodb", region_name = self.aws_region) + currencies_dynamodb_table = dynamodb_resource.Table(name=self.currencies_dynamodb_table_name) + + for id in ["AUD", "USD", "CAD"]: + currencies_dynamodb_table.delete_item(Key={"CURRENCY":id}) + + stocks_dynamodb_table = dynamodb_resource.Table(name=self.stocks_dynamodb_table_name) + stocks_dynamodb_table.delete_item(Key={"STOCK_ID":"1"}) + + def test_api_gateway_200(self): + """ + Call the API Gateway endpoint and check the response for a 200 + """ + print(self.api_endpoint) + print("URL", self.api_endpoint.replace("{stock_id}","1")) + response = requests.get(self.api_endpoint.replace("{stock_id}","1")) + self.assertEqual(response.status_code, requests.codes.ok) + + def test_api_gateway_404(self): + """ + Call the API Gateway endpoint and check the response for a 404 (id not found) + """ + response = requests.get(self.api_endpoint.replace("{stock_id}","2")) + self.assertEqual(response.status_code, requests.codes.not_found) \ No newline at end of file From 6a9db61e28f18517922d29dcd155d57c2c315a15 Mon Sep 17 00:00:00 2001 From: Tom Romano Date: Wed, 3 May 2023 09:23:23 -0400 Subject: [PATCH 13/16] Resolve merge conflicts --- .../async-lambda-dynamodb/template.yaml | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) diff --git a/python-test-samples/async-lambda-dynamodb/template.yaml b/python-test-samples/async-lambda-dynamodb/template.yaml index e69de29b..37dbdf83 100644 --- a/python-test-samples/async-lambda-dynamodb/template.yaml +++ b/python-test-samples/async-lambda-dynamodb/template.yaml @@ -0,0 +1,165 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + This template deploys a code sample for testing an asynchronous architecture using Python. + +Parameters: + DeployTestResources: + Description: The parameter instructs the template whether or not to deploy test resources to your environment. + Default: "True" + Type: String + AllowedValues: + - "True" + - "False" + ConstraintDescription: Allowed values are True and False + +Conditions: + CreateTestResources: !Equals [!Ref DeployTestResources, "True"] + +Globals: # https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-template-anatomy-globals.html + Function: + Timeout: 15 + MemorySize: 256 + Runtime: python3.9 + Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html + # Embed Lambda Powertools as a shared Layer + # See: https://awslabs.github.io/aws-lambda-powertools-python/latest/#lambda-layer + Layers: # + - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPython:9 + Environment: + Variables: + DESTINATION_BUCKET: + !Sub "async-destination-${AWS::StackName}-${AWS::AccountId}" + RESULTS_TABLE: + !Sub "async-results-${AWS::StackName}-${AWS::AccountId}" + # Powertools env vars: https://awslabs.github.io/aws-lambda-powertools-python/#environment-variables + LOG_LEVEL: INFO + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 + POWERTOOLS_LOGGER_LOG_EVENT: true + POWERTOOLS_METRICS_NAMESPACE: MyServerlessApplication + POWERTOOLS_SERVICE_NAME: async + +Resources: + SourceBucket: + Type: AWS::S3::Bucket + UpdateReplacePolicy: Delete + Properties: + BucketName: + !Sub "async-source-${AWS::StackName}-${AWS::AccountId}" + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: AES256 + PublicAccessBlockConfiguration: + BlockPublicAcls: true + BlockPublicPolicy: true + IgnorePublicAcls: true + RestrictPublicBuckets: true + + DestinationBucket: + Type: AWS::S3::Bucket + UpdateReplacePolicy: Delete + Properties: + BucketName: + !Sub "async-destination-${AWS::StackName}-${AWS::AccountId}" + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: AES256 + PublicAccessBlockConfiguration: + BlockPublicAcls: true + BlockPublicPolicy: true + IgnorePublicAcls: true + RestrictPublicBuckets: true + + ToUppercaseTextTransformer: + Type: AWS::Serverless::Function + Properties: + FunctionName: + !Sub "ToUppercaseTextTransformer-${AWS::StackName}-${AWS::AccountId}" + CodeUri: src/ + Handler: app.handler + Policies: + - S3ReadPolicy: + BucketName: + !Sub "async-source-${AWS::StackName}-${AWS::AccountId}" + - S3CrudPolicy: + BucketName: + !Sub "async-destination-${AWS::StackName}-${AWS::AccountId}" + Events: + FileUpload: + Type: S3 + Properties: + Bucket: !Ref SourceBucket + Events: s3:ObjectCreated:* + Filter: + S3Key: + Rules: + - Name: suffix + Value: '.txt' + + AsyncTransformTestResultsTable: + Type: AWS::DynamoDB::Table + Condition: CreateTestResources + Properties: + TableName: + !Sub "async-results-${AWS::StackName}-${AWS::AccountId}" + AttributeDefinitions: + - AttributeName: id + AttributeType: S + KeySchema: + - AttributeName: id + KeyType: HASH + ProvisionedThroughput: + ReadCapacityUnits: 2 + WriteCapacityUnits: 2 + + DestinationBucketListener: + Type: AWS::Serverless::Function + Condition: CreateTestResources + Properties: + FunctionName: + !Sub "DestinationBucketListener-${AWS::StackName}-${AWS::AccountId}" + CodeUri: tests/integration + Handler: event_listener_lambda.handler + Policies: + - DynamoDBWritePolicy: + TableName: !Ref AsyncTransformTestResultsTable + - S3ReadPolicy: + BucketName: + !Sub "async-destination-${AWS::StackName}-${AWS::AccountId}" + Events: + FileUpload: + Type: S3 + Properties: + Bucket: !Ref DestinationBucket + Events: s3:ObjectCreated:* + Filter: + S3Key: + Rules: + - Name: suffix + Value: '.txt' + + +Outputs: + + SourceBucketName: + Description: "Source bucket for asynchronous testing sample" + Value: !Ref SourceBucket + + DestinationBucketName: + Description: "Destination bucket for asynchronous testing sample" + Value: !Ref DestinationBucket + + DestinationBucketListenerName: + Condition: CreateTestResources + Description: "Lambda Function to listen for test results" + Value: !Ref DestinationBucketListener + + AsyncTransformTestResultsTable: + Condition: CreateTestResources + Description: "DynamoDB table to persist test results" + Value: !Ref AsyncTransformTestResultsTable + + + From e78fd23e77c48a3c9c2a0af2d1bd2ed580c62bbe Mon Sep 17 00:00:00 2001 From: Tom Romano Date: Wed, 3 May 2023 11:45:13 -0400 Subject: [PATCH 14/16] Changes for Unit Test, Enhance error logging --- .../hexagonal-architectures/README.md | 35 ++++++++------- .../img/Hexagonal-Architecture.drawio | 40 ++++++++++++++++++ .../img/Hexagonal-Architecture.drawio.png | Bin 0 -> 51258 bytes .../src/adapters/CurrencyExchangeDB.py | 4 +- .../src/adapters/HandleStockRequest.py | 4 +- .../src/adapters/StocksDB.py | 5 +-- .../hexagonal-architectures/src/app.py | 7 ++- .../src/domains/stock.py | 7 +-- .../src/ports/CurrenciesService.py | 3 +- .../src/ports/HttpHandler.py | 4 +- .../src/ports/StocksService.py | 4 +- .../tests/events/testevent-failure.json | 2 +- .../tests/events/testevent.json | 2 +- .../tests/unit/mock_test.py | 7 ++- 14 files changed, 81 insertions(+), 43 deletions(-) create mode 100644 python-test-samples/hexagonal-architectures/img/Hexagonal-Architecture.drawio create mode 100644 python-test-samples/hexagonal-architectures/img/Hexagonal-Architecture.drawio.png diff --git a/python-test-samples/hexagonal-architectures/README.md b/python-test-samples/hexagonal-architectures/README.md index 40104682..0afde6fc 100644 --- a/python-test-samples/hexagonal-architectures/README.md +++ b/python-test-samples/hexagonal-architectures/README.md @@ -1,4 +1,5 @@ [![python: 3.9](https://img.shields.io/badge/Python-3.9-green)](https://img.shields.io/badge/Python-3.9-green) +[![AWS: API Gateway](https://img.shields.io/badge/AWS-API%20Gateway-blueviolet)](https://img.shields.io/badge/AWS-API%20Gateway-blueviolet) [![AWS: DynamoDB](https://img.shields.io/badge/AWS-DynamoDB-blueviolet)](https://img.shields.io/badge/AWS-DynamoDB-blueviolet) [![test: unit](https://img.shields.io/badge/Test-Unit-blue)](https://img.shields.io/badge/Test-Unit-blue) [![test: integration](https://img.shields.io/badge/Test-Integration-yellow)](https://img.shields.io/badge/Test-Integration-yellow) @@ -8,17 +9,22 @@ ## Introduction Hexagonal architecture is a pattern used for encapsulating domain logic and decoupling it from other implementation details, such as infrastructure or client requests. You can use these types of architectures to improve how to organize and test your Lambda functions. -System Under Test (SUT) +The project uses the [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) (SAM) CLI for configuration, testing and deployment. + +--- + +## System Under Test (SUT) The SUT in this pattern is a Lambda function that is organized using a hexagonal architecture. You can read this [blog post](https://aws.amazon.com/blogs/compute/developing-evolutionary-architecture-with-aws-lambda/) to learn more about these types of architectures. The example in this test pattern receives a request via API Gateway and makes calls out to other AWS cloud services like DynamoDB. -The project uses the [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) (SAM) CLI for configuration, testing and deployment. +![Diagram](img/hexagonal-architecture-diagram.png) --- ## Contents - [Python: Hexagonal Architecture Example](#python-hexagonal-architecture-example) - [Introduction](#introduction) + - [System Under Test (SUT)](#system-under-test-sut) - [Contents](#contents) - [Key Files in the Project](#key-files-in-the-project) - [Sample project description](#sample-project-description) @@ -46,14 +52,12 @@ Hexagonal architecture is also known as the ports and adapters architecture. It In Lambda functions, hexagonal architecture can help you implement new business requirements and improve the agility of a workload. This approach can help create separation of concerns and separate the domain logic from the infrastructure. For development teams, it can also simplify the implementation of new features and parallelize the work across different developers. -[Diagram](img/hexagonal-architecture-diagram.png) - ### Terms: -1. Domain logic: Represents the task that the application should perform, abstracting any interaction with the external world. -2. Ports: Provide a way for the primary actors (on the left) to interact with the application, via the domain logic. The domain logic also uses ports for interacting with secondary actors (on the right) when needed. -3. Adapters: A design pattern for transforming one interface into another interface. They wrap the logic for interacting with a primary or secondary actor. -4. Primary actors: Users of the system such as a webhook, a UI request, or a test script. -5. Secondary actors: used by the application, these services are either a Repository (for example, a database) or a Recipient (such as a message queue). +1. *Domain logic*: Represents the task that the application should perform, abstracting any interaction with the external world. +2. *Ports*: Provide a way for the primary actors (on the left) to interact with the application, via the domain logic. The domain logic also uses ports for interacting with secondary actors (on the right) when needed. +3. *Adapters*: A design pattern for transforming one interface into another interface. They wrap the logic for interacting with a primary or secondary actor. +4. *Primary actors*: Users of the system such as a webhook, a UI request, or a test script. +5. *Secondary actors*: used by the application, these services are either a Repository (for example, a database) or a Recipient (such as a message queue). ### Application Description The example application is a backend web service built using Amazon API Gateway, AWS Lambda, and Amazon DynamoDB. Business logic in the domain layer should be tested with unit tests. Responses from secondary actors via ports should be mocked during unit testing to speed up test execution. @@ -64,6 +68,8 @@ This project consists of an [API Gateway](https://aws.amazon.com/api-gateway/), The two DynamoDB tables are meant to track Stock ID's and prices in EUR (Euros) and Euro Currency Conversion rates. +![Hexagonal-Architecture.drawio.png](img/Hexagonal-Architecture.drawio.png) + [Top](#contents) --- @@ -83,10 +89,9 @@ Second, the data store will be populated as a side-effect of our testing. In ou ## Run the Unit Test [mock_test.py](tests/unit/mock_test.py) -In the [unit test](tests/unit/mock_test.py), all references and calls to the DynamoDB service [are mocked on line 18](tests/unit/mock_test.py#L20). +In the [unit test](tests/unit/mock_test.py), all references and calls to the DynamoDB service [are mocked on line 22](tests/unit/mock_test.py#L22). -The unit test establishes the STOCKS_DB_TABLE and CURRENCIES_DB_TABLE environment -variables that the Lambda function uses to reference the DynamoDB tables. STOCKS_DB_TABLE and CURRENCIES_DB_TABLE are defined in the [setUp method of test class in mock_test.py](tests/unit/mock_test.py#L37-38). +The unit test establishes the STOCKS_DB_TABLE and CURRENCIES_DB_TABLE environment variables that the Lambda function uses to reference the DynamoDB tables. STOCKS_DB_TABLE and CURRENCIES_DB_TABLE are defined in the [setUp method of test class in mock_test.py](tests/unit/mock_test.py#L29-81). In a unit test, you must create a mocked version of the DynamoDB table. The example approach in the [setUp method of test class in mock_test.py](tests/unit/mock_test.py#L43-50) reads in the DynamoDB table schema directly the [SAM Template](template.yaml) so that the definition is maintained in one place. This simple technique works if there are no intrinsics (like !If or !Ref) in the resource properties for KeySchema, AttributeDefinitions, & BillingMode. Once the mocked table is created, test data is populated. @@ -103,10 +108,10 @@ hexagonal-architectures$ python3 -m venv venv hexagonal-architectures$ source ./venv/bin/activate # install dependencies -hexagonal-architectures$ pip3 install -r tests/requirements.txt +hexagonal-architectures$ pip3 install -r tests/unit/requirements.txt # run unit tests with mocks -hexagonal-architectures$ python3 -m coverage run -m pytest +hexagonal-architectures$ python -m pytest -s tests/unit -v ``` [Top](#contents) @@ -115,8 +120,6 @@ hexagonal-architectures$ python3 -m coverage run -m pytest ## Run the Integration Test -(Coming Soon) - [test_api_gateway.py](tests/integration/test_api_gateway.py) For integration tests, the full stack is deployed before testing: diff --git a/python-test-samples/hexagonal-architectures/img/Hexagonal-Architecture.drawio b/python-test-samples/hexagonal-architectures/img/Hexagonal-Architecture.drawio new file mode 100644 index 00000000..3efb8f7d --- /dev/null +++ b/python-test-samples/hexagonal-architectures/img/Hexagonal-Architecture.drawio @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/python-test-samples/hexagonal-architectures/img/Hexagonal-Architecture.drawio.png b/python-test-samples/hexagonal-architectures/img/Hexagonal-Architecture.drawio.png new file mode 100644 index 0000000000000000000000000000000000000000..3536b699d88392459a0b7312c334360ddb82377d GIT binary patch literal 51258 zcmeFZ1wd6@w=N7whje#?QkxFx?v}1i=cW;mRAGaJh%^WYg0x6UqizuCkQNX@q>*m8 zYa{ylzTbQ5|L?i?JNFzU_8N1p8Dow)#xtKamQh+7ia3}Qm;s$&K|Jbn;&JoJiL6IJOZ3Ne0m^0WySvyL*n(9Zz2U9!332fW0%A5*B|UXDW?os~vx}p%E$~Ot*4h~Y zzs1JW%8$zt0#xS(aq)720FkVNC&U$~qyPf(a`A8p2!aH-goS{DKW3`H%g+T=2V}Zd zc2=H_zx4(e+Im~r|4@LHubrN|yqbr%kQcw5jgF?gpNH#@iTm1mdN~4GyVT*&zTr1} z`@7rzklNV#I$8q*;brENXXcRx%HZF8vNn!Z_MTQiJ3MYyz(6ls&;k_635*`zg5#C; zbvZ#cehR|MvUflt9uP(8+m1Sb7XIAY&o*7}D0*7CJE%i!E_G~k=SQ<35QzUrqc;9O zBq9O=KLqxkKl{1r7B0|r47eH)2pBAUF!nxcW_Q)AJ23G--2@ltT3fmNqtw9B#v8CDxX}s=Tn!IiTgleZ-r;A@ zAfBrdSF4|uuLNEWRyGj7ANOC1eqWUzdi<$PPY48P@3%$!z4&J(*E_PdE^veXsmH5+ z0Au=(-}_4&p1=0f|DWdkm26xE+2C;J!0*4jFoIuj3H-d2gPZEFLOHmZ{<_k#^>lQ#^|tlA68@L~eAIvdxcD2b zU5UIPKAyndyR7xM%DAfMZRKfidu2O+TMyU+f4AfQ-XIih0nY=>7cdG>TNf*DN8djW z)aq(8*#D{r-(vqBg8}MX+1r2EEdO>|SJU^ka`Cwu;y)M%uwO1cnU%FS1XutWKL{o{VXEqdGjbUr^s?pA-;^kujI`V9WqUcYJcszu?eJ#ppBc!foN zbm8}hX8=OfrQdS+%_#_8$^LoNf8~S!XDh<}(&ZKd0_VTA6)z*%{|DpwC*O)!)BkNN zO1oMGzyTSLw3agPThYqf*3SxXDsX4}x;fDh-- zva3=56kfLV*SmiR|9tlk*&lZU4FJK-$J@ox%~tjokb#FGJBXXNEW`!w00E`)0sj;L z3*?gl0Gy2@5YB!p6X6$-QvhPcuQEADPXH190Euq!&9TfQHZTDtd$9qrbeq!US*)Sb4esz;$+xci`iY`56~n?*K}0ci^(} z^Wx|7wEYorl>r_*HocjQy{#TLw-vW+*@|XbvJlJ2N!T$kc_|fP8 z7Wn<|T|eOb|Nr9pT}}V@m~Q|lMAWQY!8QPe@|U&!pGf~ltiD7x|92z3f{?I04?hs* zexiVXN2HhI5s(E5!Ncc&i%9SC!;t^a1bM!n2fY9CLH^R+0hs8IWBISMfSf!W`;UV{@|KI^G;Mf1<4hffV_dn`CWc77)O`MHMw@gV@a|F3fm|CfCTpTHHX@L$e{04&g@^ZOlyTpmtjT_8R-Kgxb1A^!Gk z@{^JK#X$Zc_%jp7!}WK_{uj;n3+Mm5|A#=}FBO0`ey;%fb#>MJ?}8uPy}3t@$4WIxpwnQ;Ryxn#eWsMBn zLmKbIf(+YT9=#saP*Yc$XPWV&IF$G>jwol-B7zJH?FoH{3}U_L%{I)JNTmojEhCVy zL3Yprvj_AFi1-sIzvRyVISXb%iLh)862mL3Up>j8kb%{RVu0Fj#(&k$2?c6nM!g8l z5H@oML+;+}Ilmg5i#lQi8H$41n72;R7y0|&zHys>IqF8R>064!eM66a9V2$+*`h%G zOo-n`J6ktiUYk_qRbOG7D)3t)bSc3keN^#J%FAC?T|-wf_Fa3Zuvd%)P2+K;nXGY& z!M_)f=TM)Hq*tCf8F`!f1iT^)xy88TkB?wrV2@2MU^LPQK?H5ylW6PfyY9wrhEu%z zwVU)-&^f{N{r%2I5}c2jl)*3K>QP>l0=w%oG(PD*$kH0gYUCid6;ouiNXxlWj2Ba-d7eseM?&^y(u<6Oy^nlYaK`SHq-2{j~SeL@GNH{rE^ z92(cz8Q*!~MT|(i=cJF~+GpC}d95jk6gu+IKBuR@9Uq@O3Q5wSy+=9kd*Jg|Y=#XK z5xc9X6$Lm_gQ+zczLr~Ed#~?$LP}rsWXekz*7>t8o(YgJN0Y!Z>cR|Py5Q7qiiBDR zj?TZyD_r^*h+dvOQ4uj5>}%N+HdVoXzo4YRAq@FAI0_C5SJ=rSs%;~R?k;Yq_}qCtu61YK|Ri}qF+8t z;GEJYv;0_IJ}5l1{pM({{CopoNLebC-@l_1oj#{vi6@Wl*mHU@8xL)Z@$Vjd;pQ_K zLJmeB@6bq#1$4`(zM{7@noFE6P>QoHd(4q8Abp67g%xS=P4cD743YLm+gxjUv6hggHU>fFvO4d$-*DC1_?zu(!Y|gVu_yJWuAVi;j_Cu zLo?;Ki6+m3?z{A`8Qg2wK%3QYtvO z;@bniuuYUyJpLyfw+-#hV{X-WdG`m{^rssYJ|?aBq$RBuvgz^K|Kxha{%YLI@94Wb zG+5aWLUP^TWZyRr*_?uMr14>@M<-Q0sEVwc8~(;E6IshincEhB#N)pxgD($;YJYxX zOM1B%$6v!840et$Q^aFm%F(hoV5Pw)8~0vpn}}f3h$zb$vH7QXTAeq}7m8vz_B~7b zG6{XtycO8)d1wDEtrzK$<&@hV;I*K+w~}ej6rrZ671|c=KbD)5Gv6F-uV)mK^S;kOLsUec$DT_ah-*34@-Bz{27d3B6Xxdmv^CS3 zd5f>^!ceAU1L5*eK^!5tk7_cS!PXhmf&D;LSvxtWcj^7(UNM)MN>aXW z-0!~_5o*_ijgr9Uh!xu9#@71hu?0^uYB%NQkR*!>5^KxNLn&e>=Z2!hmA)qH5M?#H zqnw}3imPSwFrdPaTW|?NgGt{*Kz(Dxg*}`lUTf#FVV335fQoW6BJ(_&hQyr*PhvY!-!w`D36K1 znTU+&eqq1)m(xLN@bd1izpBSZ_Du#IS^Tu!N+wT=<{W$7p?W*o<39(nl*${E11-?r!g*1eXlUs6{p3A*U-ye4(yFRsik7_4mS}ZAOb#2+Q8i zwM`5_0xvigONKqp?iaxV#d?S8brEgpmTu7eMjdUoGK{?c!@&;o&uh)h)- zdQ>lmpYWt4t<>3xu(}}%A%ae7pPCf5HtC0+$I=J;N*MTv!-#i~O5Q?6uk_)YpvGMN z;Q+%*J3bbTZ0>DO2bG6wYY(~|hHE4}?fRqNHI5d4E(~~A3#O$kRy?W96b;0u4#eq5 z2k=8&OhMUH6ILE;U)v`~YZ(7Xi4X*2d3YyZHBlin(oRFIPBMbg$4LmAa)1!oe1xL* zO~do5M;Gs#*mTNS8vJ%;rbuFWd{?Ltb&)}c6|>Py7STxByDw%3D#6P_-(ac7dLI5i zCMS70Io7+=LYL~zVk%2YBkibfD>|uloU3w z!Z>+N;i+fB*>EsVHOxP;|5k$5RE{m_h>r6OLK=MfXN*KRC9(Bi^k$q3Pz}MiXhH0_BkP1oYKi#tvO$h2%saM zsm!F`0|qXeZ`>+UvL&h2a|(?n7&suz&bM4wLD(?tjy~YD5&dJs$W$||pp|;k<^4YZ z*!SkkwxGN)HadpJOEd=$+iH5D^c`|?Du-XFqO_&F2oeY&^TQ6dgk|nvYdF3PCFU^?!$D)Hd`jPO z=gl+K+ETlC5&$2r+9eXBgYWDJWKfB2@WV0FH_TE=JVc~4HU!^+RP`@o9xKua!>=0d zjYv`hO5SwNV;`k70uQ6_zd;QQFXUO+vwtw_i`$hU{8K@8%sVWKxwk$b=Pz_UR>QA^%$(du>u>pdUd8?T>wz*7 zrM`;i+lV;h5gVwsjh5>h4A~gY7=nWYVDSm^kXH8}SY<&{`%F@X1NWAn`De8~_s%7H zp))C=;Xsc^kA)Ne_MUwn1W^rwy)>}O*>@g5@( z{b7tUClRAF#&^TYnArHg;rTDph~cSwEmn4r@eF!(o$BJR&{@^@YM#&8b29uS=gzD8 zk#4(vIScc-RDY;0Nj}7+{YGmz4AQ&Nnn1bRu|4~ko`vSA!cCtKc-GIa zy_*K37>3DJv^50f8rB*3F?Bo=-V64+Z{TX5uqv~^GEGV)K^o;X#(?q~R*2uJchruE z2x+zaR(apTY@}FF9-KM9LqfdOn?QzQ1E}UV(|+j&VPm7n6P!pOk{MG1aS9y0{j}h*@rWHXDpGOi*pwG$IGgGoe`&vTD5C_hH5`zmzRuZf)<8{E z3Em?*hq9~${J(4|geqUn7s^lT`g-V+l{aruM6_qz zQ!jRQvvPt+nFx(+yLg>xH8Xx=a6c@y)zvch$&{wEUhe=r1Ul%CT}?+rWZ1}xyt;q? zMZX5`%QKbo(hr~z8{@&mj*uo9rSUOjj7QbQ^GFqgt!duEF6m+h z7H#D>4*|6Gh+ZMvc^x}N=19k~E0q7)9kI=^?;$759TByY!PTo7UeoRD7`N5c(CiDD z7>>O*(gPx`tk1;6Sl+y#DU#dayBn^TKxVz_e2@Vw9*u%CeDTT{-<`o14_4kbIsGPw z)@qV6Z|RO1TZMSR^~VgL$o>ecc;CXg|@WzpG9e)a~b=-Al2pTomX)26U99#bufep^jWl@s@tY|x+{YL`8Qy=9Oe zU&bz~$Ne0m`A|?^gdi_;<1Zd!<2Y1c+neA>=wE3#Dm=22}E zC3CMmgml7DuxexR>v?oP>1&fAae1BMtUaO7?-GuBF_^;+@qVgO_IR}OoEPxG?;OhD z9@xj`wFTgTAB_{!@fcR=rkh7)Jp)Iw#WImJm@lCPs!=|ceo`@do@TghT9e5~q3078 zIGOs5YC2>a;~@L){ZlcH)0?MHmF!uS*W33i{E>7rj@=k0rTsa|&|2gi#tq7zq`zKG z3|X1oKT4Ai98PI{_hxwI>q5k2{A$4Jv0DyXLJg%N0Bq=iQ=y#A4|EN?2e?k6*o13% z5y9{8rxt=%k~kNkyn~X}6iV5HyM$q}*y_6{=fm-a)#uG!v}JO};+%Oz4<_~yOZ-sN zYM2_N^$on86I4RA-qG~OwrI+V-Wlj$ z32!BZEhn|u33O|w37hpj?N!6xN{-(PZY)$~VG#B(YRl+%5dZ$57z*|Nm~xx2hUWX* zQd2UIkPusf^-W=-mk zL&PbmXAs`Kc|98=c83g+^cz!WmcWD5x@So;$fW5kmNzhSJWDRRn4a+z>}&FU)HzfX zaK}eSX*w$%2cf~|!ed+l%*C0Se1jC23zIL(?ae#!0%H980a5f&-(+kl%kL?5k!YN8bYIy*48XPZknmS!n%_S?f%$aAuq17P1sk5@Eo4%XSo4S3kF<)`S;<+OWQmQPgtXi?=dW~Br z9wBago=<}fw6f1*&isPq3k#G-vSoD59R&am?rBMYMQ+|pV@9n7vUGmjIvG|$#EmsL-1O%;VIw>w$-`d7b*sDm_`Px^^ncC&~SW808=PP`b)|+`sRJX`u?y*XcP4RbJ?_|4MLI>+ik9EqIi5DEo zO)3~28AFzhN%#_i+b&G7BOb#rdku~#<1tGok4bhJ)0%E62ir!nRk|p*c!?1m4dU}D z*yBU=+=97BeH_X0KBv8-`Kt2zt#}ac7Q_sn1c@!1GaZe01(Ge;Hr4z|w_42U7%Q`d zj3KKze?+oAXxVK|t1SE`s$a@}4ZUUht2PTC$&Qa>-&$Sgt8-|bh2jA2?|0P5BOA6n zT$rbt!sYIh^w_8-7Qt>WxvoJM%%3WwA;5l`n1~5RRy&pD$z`pPD+zu|weRlxSfhz6 zb5>iE$+W~k^ zg~;F7UpqFQzxmmhemdtd?+H{)V^Xk9c;YJ z=i%uBG|f66X0FoLnLU8FHGvVR`|`3vwJIIHyG7fKj7&WZG||D@rzT8X%JcqJf6)%H*F-27`{M$ zsJ@Ku&QY1$%$sX0FO*qN7K5+*blFG0`Edqno80I4K}`-f6ANqaSyyr}2l?%(qV8Wn z1o10T&N5|X!&gd^2JhwX`(;lK2yPP0Q-0(TH|R z`ZSR5Yy7%UlV;WJ%pxfKVl^Jgr4!vVT()&deJSWW8*j&6bIDHBPJBU6VA$FF+0{~m zGMyWEYmcTwh{9U(Xc+l-9}XT6zo+pRopPSy7oQYRy@*vDb>GP(v^HeC$4mysR)N7! zaG%BW+9>EEJi$%P&GMcV<(XvJ>J=!kCBF44X!L?#hZ-kz@K$3dU*WZ`Bu|v2o0+p8 zO10O8RzQH|-dIWI{2IL_n=ki8)O7ENRLE~lq#tpemiM+qt!REr@0T$ zdURmYjyZyCib`lh0NA~T1)%~`F}Wkk5*g-i>=~tFJSw3KD49IYrW*8W?Wh+NrQgzq zX`YHu&n3`{0F4rTs7I!(_Yz(rK8R0-#d*m&`I)rg)rUsD0?v2r)IWMSAoAgQA0}QeanF!8c)FD5JGAs z{xe#7dJEVdMeFm90g0hXbY!EBnUo^<6gTD_f+NYnJ!5gs@F2E+!x9Ux%2O6$DY9j~ z-AIOFcJbJ}KW&LU^uaT#pvc+x&1gg(Q8FFa`)mfH%uae5*IFa3Qu$ZYw9jos2i+h$ zuV~x#hCbPHzqCDY9+X7nA@qC*RdpCA-6bz@lhov5?RiQkZ-1RC(B9+3V|lRUf!Naf z$yi|KF?heut1m@RTu{Jw1s$^3ZRpH|tRsjFL97+RXi-vjUjss1CUz7q$dyosL#)x% zZh47-k4l@sJsCd7xZD;&a!*a;L=jj_IkwzPg{UX%9>tM=4@M@4qr{axY zdo-);*>2A=(~7EgX5(v5abbz;l?o@f(*WLTK3g-du^-R6TF>+mummlPC@!er(PBmr zDiVlIM+paXg4^1DpFCb}1oz#&aTtUv>xBZU{25|zmj5`;Y8b=8Y6p|oK zVIrH6B<)H`K!_+k_$>B1VE!J%*^pc7Jp-13_!>%Jt1`lV_f%71sox^Vq)MDtxk4U`Q$zgv!>Ah$#rzJjy1Yhl(b^9&6#7H$ z?$T0TUpvq~HPW(3$#6hoK4$&Q1t_}?kGd{E%u01hcGg5Ou)hyu0LrK*ri`fvX$}jtS91&FDSa>(HR!iFO*~CCvK49 z5u3#q_}qA+e$Ng^9NB%TD&;)kMrv+P8tHk$_xK1(T--oJ`Bz_i*QuPNm4>*w4NkCz#=g1arw#cNBZ(rqNwF@1yKjUkN}o>(CQZz zCs-I-25glPZ>>C!B1Q%RM%kXtZ$6`5KFTc5nA_2 zLL`rf3{3^w$DcDqAFNzdeDH~NEwr901)_Jf?J7L91tHg8z@v8@QY|7#Q(9N0Bb^I6 zQCm*tVBM%bW&ZYHz}U-Sc8+CQ#|ZOK#cQE{QDCwSrL z`3o`;<|n3&0gm_}68#k&Fa(n$RiCGWeFz!Es@eLk6b07@^7W>ti}#7axBOH;v&Inm zH`%)ju+(oB8?=aOtPx}xCu->ThLt`vYs3;L_NcdvKN~rntiO11*=lbfc8Hwqe zieQ)1Yfdf)cyz)t9e`}XeC9p_f-Mi->X(&;;*%D;1_fPrvI$4Gd5 zO&XgVQ@q~$Fnxp%T4!;rh@QG7{{2QWWzgQ&a23-Eea36YdtJ;@5ivP2$Fc?YL(qXjix=tGPK^{c4FBHPr7I!P%x zsf`(|Sl=ATR;|^A4#xD}&Z37x@5;st{dd&W`57EcyL=t$P4@^3XB=)XM@}*m>q;akB(y4wp5%qN>OVq|deZG^KP1NhF6CTjb8?#ot&hF>d0X%Ej zbP;sz!^7n54+~^yG)akWt`s1VlAEJHO)k3=^O>x>DR!sg;?0mnHbMk_$D+jJMW0mq zj$6~ONdPM!uSKdw0I3>iF)b#v%MdzzEM5+q6!?VVuvB%^V77$IZ1k|s&Oe-(x3W&{ zsqkI1z<6Nv;OvT4k|x8uVyM*KZrQSJ8GmJl;C>!38j~$_RRamZ<6S#zURyppZ#?Zp zu)Hq4wL&1#O$OS&TiX9QW zhS^`JHDB~md**m4!k1MmeHd6)FTHtD__D5Ij$^`y$s^hZ@Sk#Tu1~gp4Mnp^*~?|a zmW?S3k%?WC1n2-64-jyaJS-=Ai39bAKJRw4H!G7@+E#+~f1%kEo_Yba?-d^WcQ)87 z)$=M+uyDO@!~L5EHfS~go1io(A{99?of06q5pU|ifyRedXoeKmz#m0;9zQuX?m~?w zPiKHG4=GR9jHh4X&+C7}pNQaOu0RAN@+kc!%Z^ku36%=lx~KS zywc+cS0r%qr#dH`vU)GG1|FE<&LP{V5D-8fib)h7C_|hJVO1);oh-W+>fTMEI>Faa9 zas)t|*Id@|ysQF|VNVDk`$Rk4GJyn!s@d6;<9T|rjwkMdJH&W+UcRPk#rkEaQRhR+ z)6T6GfwcSM7RR$zLD6q{V5t|MJbGcd@V4e%uzPBwZi^{}1%7anJkvW2wIL z$;Py2Khm^+9~S(ve!F>=cw6i5%eL98s>rqDm%L;Kh zw1VA4hj$dj2&~guVLBOfEb>NiFs9_K#S?x_M8K1IKh*9{20EL%a5DpP%J`}YU@-qjy}8~|*zHBUoE>ly(EEPq@MO3&`x%F<+L!Z%@6(idp$vFcew*fN zKBVW_RKY{ z(g;QX5)dKC*4molJ)JqvKe_NE^wROpRUXZLocBRVXpkhVhj`ZAbb>Z6-l}>vwlAsJ zuGOL;fiTz+IFBqxK1PU;QiuwgrKWI`e|5`;V_m28*h=F0cY)6h9TTFn!L$MCDh!V* z?M_IdniK6f+O|hBURs|K5S_AT3v}nLOx<_ffu|>ptX4bfN7KOMVR% zTS`t2xv8m1D^~_(;X@+Y_3PKaGzT7R%)z1v4I6zq*VflT)*aYH+~&95xN+rDyb6Y5 zv`>^V*x)N2@kg%68SA+~m4 zl+APZ4@pJl%O&VpDuJkkbn&x5Jv3gtUutw^-L7VV8IlNlIr)$&SDi<5YifS!o=+sv zb$-?4lfWcPf489q6+ckSXiZZ&fE8NF{T?sx=*{8j5nr^yBf47K`2ez^iVXF;ZXN1b z_Y^gsTCEsD?w+5bgk(#^3sBZE@^et3)m11&6MZP#kSPARBlAvlWU$(b6FCUIqvhWF zO5fq@dTb|r1#vqa9j<}MYHlWt=s*f8`d!-lZXwu_iV@!5jrhgiY`k}YZtd;KpB?Xk z{5DLz*C$6TBT2cz*TxUI*FNMT9>CB~u_i6m04o#1jEm`mY7z zM@4bxDX9%RKyw3!Vvro)VxLXvSi^H9idsTbhr1SF!Zo*I@+Z2mLKen!RLx%sP&siD zyUCC)z8lMVpWL8V8XWD(NCvL{c+2A$2{k_bK4P<|)p3)paNr%v$1Lj6K*plOV6M>0 zXlZy^YE$EF%0X&T=Y=fIn&hd_Da|c~ z-r^)4usa0E-eZcO4NpxuuXt%0ahw)JyNk$triVSs!trOKhUanFeU9Cb5U4$AHOsM$H-w{6)ICCXu%t zNY*px3S7GtzB`Rd`EVEZo5?k9T-!Qmi3M^_3GR-%wuh~k=#4E)RLmaZniz2Ft$A5? zCK%y)o$R|lxuK7lA?V7Bp|rm~6=~O-KKJ$kR%fH@xosroG1VA{VC5(9;Eq?=_>vg||6e z2-2E1Z*kAGU=j6ln$%!iH2Tp-`jxeBC4S@aIKGR$GK_L`%PV*Gr3XA$@_26zYPTLR zw_THgBsbHHi=<9jW+G^EIL2>=Cp%Uofv5*$SD{duN>=^R{;mC-1sjlyn42gF!{b=IsK-q1&xlSz ztl;rF_rzYA(w769H|>(1J(ci4UKuF`FSVkymuH}WMvC=i_f|#*E3)Nc3&wlW_&h$h zAyVDCGw{Uew%O=0Jqt2XEJ(oqenG4X43Qf@SG#!Whz@{go+o*awfD%*`U((1!|5n5 zCOx3n?6}r(b=CQnr|@o%o!C-{<*5;pmIUv+Mq(bU4h~SJ_E1JFqmIm z0iPfouS?N!x(N};DdwWHTOTq)@|KZ(r}f$ehw4+MLYtua4iVR3BD8jwMp#A8`qddYlS&$&4X_j2rIR=Y zl})5sYuM{nt=v!jUj*6H<+RIzY&xEoLT8{yJa)XWQll!=?qp8Fdagx^uYKMtA2G^I z>IBCg=#NzSLPe^Wk#}Y7)HIYZzcI0C4=bOJi>X>^M>m;H0;nHYF$?|U?sHVgKHjD) z)y{RzbA3f{jAQ>3s$`cV-JGK({%2)H>Sxx|@%Ow4@RQiJyNwumcZgH&zJv9@AE;3H z*7L;m3(~Pwm0B2jH0;GQmgFPPebE>jWKdzpz0(`E<>JpBdfsO^bP-k%W9LM z#lc?i=NIMX=CLw5C#!uCKlUtm(f+_%Or9yUWi5WgD1Oa|@8NRR1eQT@foK3OMeMD5 zuW0T=pA0{69|JAE552{LVnpgU?rU0A9%9~ZQ&i0jN|rJMQVGekHo(LTLb}p1pDGM0W^>e%6l7Rc7|iSZNqtXfIt~-A zY2|5)nTl4T6@Ai)KCU7yZ)*h-OPQ-!m+zBx1+p7ea>cEgNA8eTbfjxMdqL#rdA_8P zubRPw(aj>O_Te$}*0=1CbpF`>3_mir4#Q66=qa49DA+GyhBl%zs*hpWS z7CN62`#7I+%{;$_mmY2;?9~K~z=LU(DV0O-Papy`0CI5)Q>h&L_#D^brW|yRfv^Uh?UqLw!-XomyKK&erdu>%qr2 z=zEx;B7PfGqJA4OCUtH`pX!iMFoBcU^TV$x799I~Wz>dZu*?~%7(SEmJ&oxpF=l*= zeP)1SAof3sRt=Q(Nboe5X`wK!0oj!7jtxdg&VGpsCF{POyHt{07AkIhns&Qd;Q1ET zz4h@%CTV4)s!5`>sJI&sg4^4Jav5F`k?-qQaM@p<>tgDNFEf$TYhMoc_S$owkS>(SwsDXEb0iAL$^+41%F`WEX3 zffi&t$KRyzc5Z%y4WNCV*hzU|SR%sT>b@M*<)i%cR+=HFH8yPEqatI48$(w@S1*n= zOFx0?1ISZJsPZtWWy+?Ni?Il0lY4gay}Eg@PW{-JxLjlFJRZTEblU zBAldp=1mfuP!n4Dh$ej*8&P3uZl`aT&IkDu5d?5ja|3U!5HbZFEG=0~6kvl??$$DB zXy94qOsRE?csK^3e|VJ=Ia%nqXMG@TmL2+LZfuSLbI0l_RjhVjYtaGsfnpR0%1w(= zqlq14Ije~=5+F1~|by$RAPC1c1VR)PW0nck|b6P2@OI;4UPb19m zjG6yzIQN?+B9Dza#Yci8p6a*Cf!V1H4}uAz2J^|&%7ehsr0meSdjS}+1YK(3WGYMR z!-etky2{(JgHMJsMZ8)bB3rCRrsC!}#H`?Il}Tco#3`et7t|kUBJcO&%H4sYHaVQn z6>s8pv9$%7*nz576Kiek^A+#Z z%aU`x5@Ld%w6Gg*sQJ?&<5ho&NaiZ^9?8Dg|GH{`kMrz>rosU>gC_y4pzA&uxheQw zYX_y!2CZZ;lC;XA8qG7!CoZdt$-JWko3D>m4lWiZyz(_uZYNO^(jJglNT`RCO@lI9dnK~+;_d~8U5n!BCc>vT0s{7 zC}^3jVzuF*mIF*S78`tzwT|H0N{HRD03Q_gWX-itjL_vG)ut zj<$Qrz)uccBC>OoVu1fZy4>}8F#~A9z|pHzN>^BipT%JVca~qMBloR)1le=6ah0>d zt`cSmN>~ebqY&3SP05AIp{K?cb-=PIDk>tQqDE8ua|%oj3i+&jM8_QqYIybhobXjk zjciYmy0mc3@;!6qrVd-Y1hN~lw!M{BQ&lQ>d@D&%7%WAbsbFLr`|TMlLbKPY;4Yug z6G*a-9C$SD?(;FUq}*37$?Q@u=@BD~9-dmKXiflW3`!F04S6oxNm;Tt@>fnI@nbQj zM^F;R9FN~_2WJdxBHcKyw5u9Xim7p=AfSjQcCLjcsXtlhwrfISkyZm85f&Ns zb0B%J0`k&;{5ETfs@?@7>|Jlf8K)wc!|&;niQ{CBo~RA@(mh%6L8d z+1E=&TEhIleL9`IrOywOv+RW;G}rApSLq$rh-k7J9nU5pg9wS!l9acssb7CIK@vlo zCoI2af5wy*Wg`O}e2yq@Z5-u*JtA~-!Coe?;Z*E4S@H||h{q82!KB-`34^02v8!W5 zfvE!>?$uoiDT5nzSyQ!m?=c2MRJXN}^lJ5okH6Cl$Yuu0DHQTQqz=4jEb){#Ssz{= z^46(3$kvrRM&`6;!NQG4SU*=i>2}#pCD?g&e^7LBMG zzwczn*9V(dQj$}L6A*2x6?=gf&pfsc-9`|f9MDQA)S$Y8J5>}#u-Ar-T=owZma!`P z;}%;4Jgxi*rOd2lR44iJg>YqX&MvG8VBc4mrag@VnHFIk2T&}HNwx2f=(rMHSzljy*vS3< zSdZc-uj%a$#~p!JY1?1W2el_4yw}LYZ>t2K^AC7hbd@ks-tr{iwewbNrJ;s7liJ&x zHmImZqG6EbimU647q;zX)XAR#I2%gs=y+f>85kkhMmb)Hl}SYnn|n~bTHZ9<^+-E$ z?z1KLz8`()GVn^K`C;qZW812PLB@OI26m(2#Nc_cNq69lEg{(L(`urLfzgvK9~FN2 za*4Y+52)}0>(|Bu(KBxM5mz{x5E>^jL#h=wIn($sfHzweOj%zrzJ8V!?h0NDPnX!JJ$oeJvCL+ia_O>w>2n1H2ms9|6>i{r)O9fPeRF# zT0M{4s_7L@8g892rHuImebjJlr;sn-NWFIm9w4ZWg^6<;su_PEz_#weqebQg$+7h0bbSH znN=H~CB*kNJxY>`2>7P;5sPb&^dUHmF9hI!3gkCT*(r4Mi6z8EALJyB;0P2R`V^}0 zXYs3fw5o{4^uuPdLrS=PKP2@tKMNj?tCU>ak*j=*qE{!Kkg#S{WqH{4wD`c`f+Ree zJW-+Jwb{|bWDRo(_l5h1j|;iYq8mCD8n=$8Lzwyy9@E=((oPT5_iHr;9hhfIKat4t z-o?o3-#vZMI_@Pe7>&@3i>b5zr9^Y{zA$ropYmk2DWB=jfnH#rj44wV`Jc`^aAI9imxT) z#mo_|1mUp?E^!xDNHj|Z7;w?|t;}(6X(bWnaT~Os9(a+s%Vl5;;iFlvh!1paf%+g1 zNQkVIIT4&kzNZBE2 zQv!rqpSP>VryEQW&& z0L$EIvGnKb227ZDNY}((V4f^Cbd!;+PG#F695Lan(fdl;#NgE%0-UbYl!d>E3Z(*M zVy9#qDBbPGOysA@YBJOEVI(k1!bVoXt1!7|nJiLTRn8crX&eImMV}TyG}$YXEtIDi zBd00&U+;BJ-5he(qlM981+`rx$$rTmeI-Clyu;jBSEEo0ol}Yz>*hS2Zh46zvW`XICL^pwD>>bUDiiC0J|7u$nHR z2HHD}7{9|+RLXLMyV;raw zG0G?bk-B(E$RrIrp;x|w9L>D~YI0b#P+(f9Q{!>8fZ5XS!>1`Z6huN)kMrjl(e4H*)rq9fm+M?sO znZqV#GZ1^cze&8IoWg7e&irJ?s-I7_#!19oEk&8?;D~KD2FaLcNjNH@{QSUSV&jkz z|2_#2ym`Lq!RFiBakX$=Q44C>`fNsWhqq;y!td|;=ZYO)>K8~CCRs_*nipQ3Sz1Q; z_&M^kxFbHbVo?L-34M?ks2)DN4>?Z-mZLRc===hcw9Rs{`9d0Qad}YtqOE~sw;1Iz z`$Hd1a6etF42;pO@Y8HRALqV)bN8OcUSx7S$$K2MKIt(T2k*yk&uUiKV2d z)wQ)RfUG-AGks^`J4L2m#H`HwU9Z)F_N+C<%p_UOEbM@(Djdb{KU&g2k~qWs)V6wD z;PRrs8w)7!7i!Is4|pX`Ilq=Wnf4$rq*ym4gI*@esSThWI693w0$) z4J;4YsYBE9j%eOKKI&b*KBJj}#D;BCr{9}xPu9L~FSqf!?*IHqa1A-N!I`h5g#E@8 z!!I&1vksNleFt*#?$cj3=Bb_V)M~VrRH|^rp3#oV?@X+lL(n_?3TXOa5DGNDlLRqw-TJPml*B%k@XS-sS4#Rt>P&Wh$N1}1S#dyJ z7>cLgMh8N;Gw8u{(XxtsZ*g9Q0py5cboGsw!KW;bbVmZBd z&MA3R{_d+$GEIql2!g*4#pw<&Ph0#q)Dx#CWDB(=mmRTpGIy&&?&n}aS-hzhRqTVY z=^PdAmk6sBLRR(S`0^#^gX%L^j8DFUEQMr@w&s%9E`LuqMj&rL`0G?3ZEOT%zGS9S z%@Tq+QF$H#KpUUK8p>RiA(oey*Juj#`S%E7?OTe@7Eznf6qCJ`wd>XJx$VF{TYvZL}!8eCQRZfRNIPw;@!I_lZSsJcUCN{IfZdA^Hvt*?xe?1 zioSOHu}^;#qNpiWkCK*zKb_oEMefBEM|Ql6W9gSzk;=KdJj+|PDO<_*)8{qKHUes;8q|N80#2TJ5}_)1&E?}XXz{!TQrqr|v8Tw3s_i0`dI#p0A%Vfag6 z>q1Y&NeW{uTU%cbvR!JV=HMXM7*3R&%#)5~Hx@A+&muoOJpAnBL@0 zLVZXl8f7dE^kgX=8-=8_&|-b|9lu_lU3`@~N$r(>8}y`Zy$`mH27RBQ>Q&!6AQL_z}0Mv`&?%-?dhEYY7>G3tZ)a;u9@ z^Ji)Sm)$QX>jQnW@AXtHY%VLQ&2P_lwx%yc%*Oa+F}efkU@+ODm9|BOw#@{&w*rsnasBGL!8aED?!V$c1&>^@I#^WH9sm zs`lLaL(Q11J!%xjqzBIr$^y!z97Rk*MK-btDDBkg{BaQfMLdFG?S>+e>6y& z+l%v)pnSokDGzH`{4oC3L+Y7ig#eQRZC~vW$7>|5TK|DqNM(|ul3?yt@yZIdPAong-y@r`q z7aJrF@IejT-5iMXznZCce`(a+lP`}ZzmQnQIDyL13BJ~@6NoRI<1Fot?K3Xq%)!fdzi_wGc34u6ZQ=)y zgefJ|o3WSxb4HUS1k#r+B{`l=3LAx~1SuUzsi)9ufayOdAQkM@8vA1-g~R(Tq0G`I z8R3eP5N3PKItB;F=h7x|dMj#?pBPj{sOS{f$nVifoCI@Rweenhf696^uxK|uE^C)N$Re|F?fm{*`cYuX zRslbC+w#Zn?~m>tqR1$Qrjq4PHZo){O7;_xy1WVpg3@z!1OPhx;w`BA{;&|BUB+4b z^{Rd~e@5)$Yt-($V6p8*I>h;sV1l5z$Lx5pK&Om}pglW;o#K`za* z;yc(>%OiCpfT>H@So5GL;>LYSOG4v6@gU+#!^|0kXB~d7*N<;BsfZ90=PEscs8IXg zLeQv$37Khy4$lh$mTZmUBv(yL~Xx$MnJ zH{D{PD~)FfzxQhOE`PPN($%Gd z#O?QZZ$FC~zt|8Wr}SS*TRm~4EA8g}kI!b}3}e0YsPgHj@VQS(?fWt%P;itg0Fus&~(UB)cYQJZVAY21)vr%nF&%u}5e ziL>aO-E8)woUR$82%1R1lavC8N0@g+&FinI*?T}yd3NuRiYOPuhPQZKu_+`l23U+| zQJW1XtSBYoQHoG0CcSBP-XS>MOyp6oGY1yFBtORQ-@jkCXltvCu5u$0AhvICcLZ3I zQJ^+PtH+DaB%hjg+pBdp$y-<_LjgJT%z)OmW#>mNX+kAsqcv#*45wxh)}w$!#by5tB_2P&|z+?gVwz+-E+T17w48Emk4V z0#S<|y^w8v$;ezE0dxx7%GlR$PLo^69*X8$sbtOs!~=8Z7eV>DojctXgMWA6($5dE zKHr#pq%a*mV9ev>k}fz{>EUN;RUVHb_Fb*1vXL0gVdi!I9h3Vw{5cN;+Im=B_CiT_ zq|Q9*;qCc7syeOAM|kWrQ+qD|nK=w9FzXQCeGxc{pNnJ$&o*AHt!I9Vz-)xPUSX#@jva|&L= z+boLne@_>q9f@7VuXeK54g$K2Ii2?esO_w%rL6#Vur!(KQ>K=(>bH=Hz0Bm4fHZ94ueBY~5!)Va3tLz%6xT4j`T z6=C`fW;+9%KZguaVA}#>13#d9hi{qSY~>Y{vGqKYHpz-%0kiP-7rs+0u%)gv&D#UN zhkpP2D0Q7F)9pQ7RIp|}P6nS?#P`t_9F zZf=VGz!zY9gSfVDTbqJ1{gHt!3}*Ah8`=3Cu%*w4|KB~%XrgId$+q@M6U0}m7a#Rm8w*IG4jw=Wz+}HLs4CrI(NZT2C9%GWrWB3-)#Ah;VHcq>1RHGk>TCX8s}CQaQ1Gky$KiuTHo7y#SKu3$wa(=rU+o@6)7Y#=Zqay+(3{4_?;M8?VLn1 z(#{Os8QuNo8%?o5$OsJ-96ZFK5Z>FaYi|ISIpmi%sf|p^0}u=ITuY_23iKO`=f6|% zqfWNzq)qO(m3K-BTyU0(*XADVy!q%TA7v#o?b`)$sl=%jeSF93yx&g3x07h=zt134!+R9(vwipTObpXAg~909wDmUKBRN&+J%*`7jp( z84j3iuSzA%f9SmTygZ3xhPSvK1Pxs@IDI&8$S#&sc!@Z#bcSlFM0P$}-NxWRcQYVk zfQ8Ml_Zsq*CPlamtxUQ=l)G}VtOdCb4p`lz%MTNI>^QmR%1-SOQ(DicdatyzW&3i- zZEkWt76C2XX1~y9pxfJFzbWDw%!+2p%ZlSzdvrGIRRs0LLd>!xE>Am{si=yawtlG7 zDJDs6{m2Ap*vV{$;St2_Fn}PYHP72339zQ`e6oUk&vmmGM8g3>tfK=?cSe_D*of29 zH6=Q1Z3G?WIeshNe583oS(jEi$;P=I`WotV`&c|5YVe~%ra4XlDKJ3HShzg?T^su@ z{+nCR-89MX!~&TC=ce^CDiN_4d4NM znX+u#NG`gU-XEVERUhQmw3+_8D&8ymMQr1n00>&g^5gk#>8XmmZ%)pi#C_OJ|9 zvV=(LXKw$>?+MZ^Zc@p3;j5hEoTpyUSf6 z{)3RTi&$jXqltKA*1yo-Gcbxr-ta@g)I&Tn;k_w)EGF5@RA0l61%rs|_O?5qrNGwK zL`A=fi*AEk_1cd^kA~DFB%##Ck+vUup;_UzrkI#ggY|6Q?ZsZG^=z5$@&TogJBZWb z2O{7(T1Az8BQms;B^yv%6xi>TlrTD|6=Wy_2kP+8`&S+>Ruace4CmRZ_b%Gw3xmm) zHbwgDyqupEkpI4s{ge45yUMPr&imfWIWw9vXW47ooe$m@vwgwYr8X%VjyY7UCwhe? zsdH>mFt$xKY;oc&lmB}`TEBxOW8Om)V8wjasPiGZHoWs+@Ohf{>T={S_c{JlU~=Tm z8kE9yZf2wz-YZ05=2?Z=qgd2A#WfzW;4-TY5xy;5!s=aDmsa`_H6(J`%1nA@9o1i- z7`85brQ}m%zAhKZ9iy_go$jAfYc?XIj_4z8x)OET#J1cr5+Lf)8(S=nM~8X)6lmsf z<4GjosqAy!F(5$+_JyWBsPZlsBo=*5;ge)E%6>o-yXs(lmM7Ep2AO`)64Tl>dnwP) zNmWivil_L<_E*O(61SkK_?WAP?t&la_Q!z8EXdCtk|`1c<&6iPUhmUNYZOjrW0#hc9iUu58tBA zk>;mZzfud3=9j;w!|u|X#>(aOUO!K7-TV*&iBH(-4O2%#f(By|N1vXY;FtSXj9JdU zYjZsi>#CduBvO}^-k2*d$7yezKk1(gWZW~ub8BHO(Vt}OoS;uL>@^{wR5F*v9zI&x zehx>wXfwNCxnCegcOR;^SMKAjjq7e7BfPX>Nr|s^bssZomcAgGv-J)NT`^!K>;V6y zJM14)-`(b;x#o{|fcC~*pGmPvDrD|V3cNWfXQ-aiyvLxLPQEapASl>nFh!a>-w=a3 zVJK?29wzs`^p!}5&ugGWE4w|Atru8EiqJr43}K?(yQJV?dCdqKXp*px!JU^jkhzDy zTJkMh`=>PpbjH(0fte*RZbSQ5+L++@ultn*ZcM6PAS&F;OeSkrH(s8pCgNw&iD864 zj_XoD`z~A_VAf)!thDd_c#wzPgGpU99cp?hY}8=3?I7-36)C z`QB(encHH_68jwJ2m{X_?!%E$qNOSIglc6G>Wd8fB}KyIm>0WKmfv2rhozGIUVLsYgVihPYCUqXn0iU&%i{6;rE(dXCq8;M;gAnehh=lK;?>n^2&4 zN}(wUh5FqcQ3&TI6*9ULu<>P^0 z$gsiIWI+=x!3F_SO-ZF{eNUIz5#3vF+RIz}EA^R=WkYyk(GJ=`T0NKksCC8BB7y`G zN0l;gxi;t$)n;*VI3T(S8=~?%m)U5b3|}A5PG!Y z4{Vy4wJIReTZFphlPxL((c}?E>LxxN^{()RwtI}rstG?fttSuE*rt+n#(&H(X^Y`7 zYfjH)gdsE&EGh%|6~(!**cjEi9YimvwMp_!s_s^c@w+sxD)1u`;;30Lfh-lDnp z+?Je@ciBj1l)9K0_So=L?K>N8o2Q(gxQOj&4@3w=6Go#^VLF+5N(#z{W}0eMNcr7) zL=;W=r)YYr&PVU5{`v_5WcVQvvq#ylo!^x8GZTESSH6wwJp&>G2Eoek87IaW!h5kf zDMc6w5}X3TQ64<`6upO8&;}Oh@W=y)3}H)!G)}*hnaxudf0ljrvVOpS9H#?;&3@KK zOpV)(Nr#)j2j7oO1a!*YNarftwI);E5#8B8lZ*#Lr{0A3{w48waUTC6U{@7xw%7I&_of~4!47Rk9?oJ~ zRT#kHv?#q%($1PL7Rd@U4ol!K=K3dgkjb0QT(QxQa5Y#f@*`&|u()zjT z=^Ct_NZ}_I!Xn*?tCwp00T7o%@sraVW-3sA@6Sa6#3!6~V0-$#Jmgh-sO1kDjY$8^ z{Z%x+MjGrUc1;bJUt3D-d`4O9nKKtc=rCA1>Ig4(^g!qy2JGJ4N`>n1xGy(ls{g@) z=5y(4NS+xdXY;n+L`ekj|1;a9^NZlHlcCaHazI!)(YN@^5-$0uF-Soe-11TlZIYNZ zy;FYkD{jj`2GT=)fsrOn!ZJHOsnZE#iQx%%Ai=GL%2Gn@N#&lV0YhwxYHsZeeljvL zl=&CsB@&bu=4tJICE(dT9rU|AL~y&u9%3oskRAStI=KTmG zai-eL=~=$3^$>6r#SR!b7>UJZL$O4$b~s4VF85KHA%e>vGf16vXIake=Ir~Xi&IIe zr|1*zrF87dVhk0s%z`OwEiCiiJ`Z0#I4d@vwP7yUQuF^~&1vIcLm+Ju-9KW#xrhRN ztQZKYbf{Lzb0i3pKR#f<6>&mEfxUx#@bXWo445A~v@%Cdb7XrFu!$sXi5tBB^M~56 zPH^c3iF+RMlOHU(?!{}UxwdUBl{D<$!iIp`N5NM|J(-nK`DKe!Tj@h<5o)sJYR+Z4 zO@ogk1|o$;i)^E}DGd$E6`as#{2Y@P1N>O%#yB8Tr7AMq*7 zeh$^fiHYgn__&QlwoDOjA}9f(IPIOXpiVAk1iXZGdco)`3hJRLz|lZ&5N|qOpt`O; zJ;2#W8RT;Uu~VriFXf6py*<(d(??u6KVBpveLuPFv5lzWeV^?#X?>ly7l(4=Rw_C9 z(r+%vIjr0-)#k3Ru+gs<(R4b?k!flc@V!GGh`N%3X{&#w@41np|wK%(O=IjUu%BGc7%CJ|nhx%>Nb)pn?UVF?DtGR00Qp zZyRIR`^YdNOP)s#=VzwgxZa~#<0OUFo;f-m$vXIH08{PH%CrDKbhikRBX)wl&0Cyu zDN_g^Nex=%YZXRmVv{bJ6D`Y83H=4E7H>LBDj7GIsR5OpkCj?8tBy}8jFFhO^KP+O zdBO)=lUZL25 zry7L*{7FC#pn<6b^Q^aCHx5{A&%>j=qiDz+!oKNsp~H4v2W`>Za%_1|M8qS*&|G)- zSGpTiL}^{*(sOcs>B^Itff5>iOY7&t6!y@aI@ z(_|VrL-}ZjBhV4F`ag_C6c26Z*HC6>zS!oH`B*A?``0^_7p){RpgMUx z*msSQ zj<&;Yk)ar!?7ck752DiEcIgy}nzyNwW zV9G-SG(s*AA!la7TkWu2Yh)lVq`3c1Ex0pM0Y;#c=j=5u@P)V9d2dh1H(z9;z}Ct$ z9Syf-kW#L#w~dTnLr-Q^TI^=Cv@u*_EvxOd=nBWK41qpIUF!l@&y84<0E1Aa0%E}5 zLNg$NDet|E>8>GhUnrJf_Vc{u83f4Cu)mV*pm@0#daX>DaO4{BH#E3z%N?E8fd>3w1paS*!HIR8yVB$P~#t&|oKXDNI z;i)QXoyM$1gw!0YNF)D1^KtwX2P(Bii(Zs*)5%h*1H`N~jn0$HBGL z5a8OKM}}q3vrU{h230$)W&4#*=~~mFQD&RH;C&>R);=xV!|pqnY#Q>N5S3w&` zJ^{;;?l2=ie6I?21K)h|xo@&ZpPyFsfBBu`e#BcL5%B5VJ>W@tGZXk4I*SVX^uSr7 za94o?HDMU6U&5{S-ykJ)H{>$j+5n>e77C9@ap1LF=D zuD4l$&~>qdw^4s&)UJeH+8KrX;;-|YnVH^t{Ic>D+U&DM(Y3X`?LRlRo3EXGDQCV$ z?RwbzbbPt24`$am)|8`ifv9&+bU@iH>4;eMBjUU4=8WFADoFkvj>Q?-R9#(>mKBwy zt}aUvuwyKH&m7~Rxe43*@!@Ni5(#IHM(`A5riq1&4CZ(9yVVCzbj~^?NKk7%qJp9< zTG3L6uO!ni8(XIAor68D>&~#tJNutR+CRuO8gQNxIf1^ND3~22wi!exbiS6vMAA8w zjvOo$&sp&Iu~*2Sn_I=t$T!Ny?f2SlX4CcFuno@7d)eGPA|PH$QS%mOo!rzRkG(8L zRYW$k?H|d{kCun*ob_v5Iq8+sLrY35GX%b&l5iGmHBt(DklG4d_MG^nf90|w5b}FO zt|NN$y*e~zUFZ14moLHj-WT*Qs5dtc+Wu+QZk^8p+(u;fk7g*hCOh>-u`l;9kCLL+ zXF~mtzeglPL`s^Lx3BqmW+pluHt&rAwj~Shp+OLPZS(qKlzxdp(@VFf-kN1>`eK!A z$+Jsg2lp$zd*4muq8~QeNwGy_wJ7mII=!Qk57=-%8DWNs*LQ$pfz3 z*j^e#L*jgXL#>*-4Jh|>b=wY03_A$s>=!U((a@Zae!kdi+V4Hlv^eg&`u^6=-LF6; z5!3I<3B!qqZHxUu!kE~<+Re#Zy80c6A*!EeI zaS%DvZ2HKYGCtAVkG467nM)Cqqtp z$p})bW6hzT@>N`@=UavI&7NC0eZ#?u)!d2=iF6A@_p~XwmMXd4OxFw#PGcphq;&39 z`&Vy`eAkyKTB};bB87dxgmYRp5=z+tG^=ZI7@12=h^7r|2c67L^z1J8fIzx(D*MBX z?S+L`fCWVFkm!$G`G>R;wM#VTU2e#9Ki+zm0?Vvb7{LmWc>IfoZ$4MP6T}ZyI1vt) zy(?rP3|I!WFP@A#!~d|3IQ%(wh{Q-ieWK_b%;x0<)YM@w8p@$?E~_ORu1vzWo^L6H zJ;EmmEnw1rxGt-00Ex3yqGu&PGGVhRw@UUMmJQK(Br5cL-X*R;{&@RDa}-Z6VEGPr z{by$sI!1>*7?_2zlFrW}BT1K8hS5IF#?oSJ(HXRRM-`XL;9W=xSXelpQHI1y-sZ{W zR=>6G#h&PM{IJ5->~MxF`#I$DC=3vt;%>tt>nux*dLsSa@b+CO^d{oueb8Hw_TqJE30$2hAMjN1AMgL z(`MZLY1B?(wBEik)IPL}o}|p4Z9z-09|iM!#vuC$BGYHQT!j#AT64s;)%9KW$nntv zzS~i7&e5OA`+>l^x_I4eXj&RAJXaLd>TDVO$2jIKnWj|Op?n&t?Q}a*C$I$il~en& z`2!&)7p<+Pjp+f6c<`{^@PjnD$U?2H0WM43=~ySj!9!8ch(GJ>4)?zlUl7}n1}GI$ zYS8Sj>VJB?n69p}R*!!Tjh@@jWA3CQ$PbL11B8M2v5Z4vX>PW@LY~_Z3dFWLUgQ$jWQeX=F`gekqj(J z4@Mk)w>cF;##qSmDeCEl_z~NIdW|=euO&&k%InI37)ZC8vi#nTrx`0%2TvLg6)iw>8!ORQtXY3NQErQveAoH3u{4h?c$S z=N9){?Q3Vs?#W^qm-1J$E(b*iWYRx7954d$?)z^9EpY9hKE?49-o}RQ{nq(r`m*up z@#S9fkN)J9hcA`o&q=IzR=X^wHNdQz&)0o-#$!o2rrh>^`0#n}gH`o49+kRNsIkGV zM8pWSzj1Ma!oLh0HWqPa>~{P7-hj@hC^3sfOq|&X5N}d>NeUIfTifIfPHW1a_ zCXO#Cck9Nlte8usl>;DNH*e@mGUoyzX{4lYKV`V$W)tXQs4Aw)rh7WR>OY zWLh}|UiZk)E&D-)&i<~U@orEmWw)hJUMAp)`GVoOw0a6$`x8Y>s)b))vb~R@VHHhe zAFEE3GPqr%gIgJNg%G{oX5R|mXrHK+7p@GkU+D;%SG_n{+fe)wU-u|QB!=15=g_KWU&p0M@rO47@JH*qNeb=M84>U#Ysl(++Gi9 z`j)6anX~Kkw~+O{I9HkwzHBl0wELUyY3pes=e@<741Ofx+owRzt^+YsQ*d~4<w*=Xo zW?)%KbXWpf85CZspU+V}oWkb_k_Bz&Dz`A8KuzDthG6f!^}FS>g=9I3ZL=GqDkc0v zNmHSQTm3F+TkNb*Bl`#{8qbJDlSeiePnm zgv}W}!Bpl3-JdcZ&g_+5Jz@35vQ6yBz|iFs!0dr`Q1wEK@z?l|f2ccem_>bsov{Jl zf6sdHIEc@LYzsWe1>QiYiG-Z0aTTyss!?ZJ7LG>VD1!KW$U0e!bb=J^2Ie0`j~y^9Jynt*MtY*1xjyW$FdOx?uxZ#!%}# z`j&VK+)Pj0HM9EjdG^_*U&RbfnG_m?#bbR3ZPXdl9A2jxMaYepqi5CVNRE8uZY%dT;y$ndMgAoe|39`ZQ{qRh1`1sj1lYz8UAB zH@MV`rpv<4d0XTeny(;*&EnU%KBhFe7COArMa-+E}lK$_lmS%GH)Nv_RlT7INS8uq zPF^1$t_`fZa168Ou={yZrH3OJF$OppaAjvx+EGu%r;W|Zc#wTo{q*ZWL9tLcv_W;W z(eMsxIz#&-fQn{<6k39}g@lhF6ZIf9dvvA=`8<4`e&mnwd%_W$yz{3oN+eY za5SEgo^$SG-i>~+=`o|NovhTZC!>3)DDaN)?D^He*Q)bpdD}e?$7si8GtV*?8~!9R z!zMNzzPs3~aX3$$HncmcxTzA;1#)_wGQNAP!5$%b{DXgsO**tL`%V5zawl%(bolK< z#|JQck`j#~{p!f0^{Y3S~O5h)OqY0B(i8N?G=>Ggg z#QSXZ6J1bSBck(nf}iA(AU|zi95i)x`)fkJ=)kzoV220Z*}Y(qe0Z)Q*OHEs(fzwu zVbCj2A0HIh{)T3WgkU8Sl$nbq2}>bbDVvRTd|~=@Ke^h-Vf$B#dP-)VIQSyB%V1|@ zI4QDdjxlfTD?Rs??Di{ZOLW>=bBC=)QO|o6z*S`fE@%1HWI`IFlWQ# zC0GxNp@vM%V&y}ZwuU_;uHP$)pG9#V{QjrZh)*9Qu|bWRPv^Cd<=1@C$zVw$`mmyr ze@AVow-v*)l+QHvj!CnZu5NR+Bt`E+Z&9UTcnKBpDXROl5~?X-{;6Jr(U@83cwPz? z7$Z&dVKN@iG1bAxn!JIe{-nYF!(JLiYGQDQann*gsA%pNNqt7&KcH(r{@T@ z6D(rm*HqgWy%g=->((O?eEhWE*Bq*HvQhN){?ty)#yQx19?8H?zmooVxLZtQ^x)tS zkb9XCcXgC;&-(6yaNw-v%aG z0Fw>Hk~DlQdY@Up$f&gE`;jv5EfIE+UAq1i-uuF+N=AWq43(&r*Y@EGv@)p6z~l#{ zZU9Hl^IvBAfCsAc9^rhWSrjk#kx-Ul2RY#B3)-=LHd0lp2-%wZ{wNli;2jw;TgObd z1adSiZ+j%}#BPxh8%f*y{^rJ1jE)^Gn)D{d@5Avs>w_^fb1zib{^2_0=>--7VJz69 z0ZhU6wUL0RAqSbIIX2Xos3@3cWX8~J7Vh%{@c62B zxR_&M6lJ0eeymQRernBMZnYDLcAapkyq6FGm>sQIzl$$zGZGV*RrIdWy<)s|RP26S z^s97Qzvm@qR50BG2CFY+5ybOq}{0D&1(1P*<;r|);?W-Vp1wc$6PJE-2+(`f z7XK~5d2k-dAVU##(ES<2Yq{a3s{e(C+=aV5Oz)zDxY_{X*!W3fB&lp)#HcZbc1B2wquRu6ohhwRWfxii&0@FP759p?&+~IbNAkFG&R+S7} zL(vW*BCzPZU5CT~HDyug=*9SC@{lnASP?ZUcSU>T*HXD%CYZlh{w4)7uVTtEv%7gJ z%K*N0`2QcC?(`2}V;!wLuRm$#7i}6H?>UyhPsTXwy%7NJtfMLnZ?E7BmO=#iUACAw$N@d<9n0=QmbaEu?Z-M{ zO?GT)2Fc%!c`0r8@_$^_h>|p?(SN19CgRI2$4pA_r=N_ zv6?*U&l1CH+VD)hU%del8gKj+_!mJ7Q~+Dy5$d0cih-H(h;9`qmXDSgzpq22I4bZQ zdIm=?iKAg&G z=SAUB+Leow^_u5$tFKJG9aSvE>(M)H*);%CR~Q!vOp%XCm_AT~&ft`rNuP4o4%5h- z1}tCG-@2a1Fiv!t_(@Tp-UvqA+u@FyO%&(quN7}RBs69RUTZWp_5lZv+ z<8Pmc#o{(Sd8$p$+nluBaIr|H;J-e=J6)5#{5K@8UByqTiV2fRA%UmdJ86L*nQ^u% z@5w-c?fe?K;uq(Xtf?YTU0dileo-vE#;dzg$$U>oXME4e4VO%1vdH?c;vL-NdtB5y z8kUUfXg>$~qP*SU65J=~j?5-BpS*wuklEfXaTUlZ?Q724o=_xZ0M!IQsQsymc`i+og|%Z&=cTRXZh5X)WUAzcmG3~=_qs2*0OAM|{g?hp6@G9!vM`Sr zS~-CAdZqv!MwtE&7a+t)6UF>=f?elpxH9`I)@)u2g@ngBl7zqMycWzsz>QWV-}fjj zoz&OLR)(M$jclVM7q~d+J+Uh^qt^C`jz6~Nb?d6feoKzOwNok4y-1x3dJVNDH4k5j zI>D%p1KJ#?st^Z z-t?mwvu_%r^ZhnES6bES|KSW${dNCv2Cxd-KlGqY3$Wj&S5D({zq@3;y*!8z4|?H) z!l^hX+#TnFH#XNYIFABdX!bUp5j-dH#nQH$P@vvhlzXaRyvQVsexwM?X86smt2=wT zGf^Euk6gDkp&6MijSsn~z!6uX83QM>{^x{8{(ovLGD0bx2M6%>?QV0cw=y6&KU@|h z8l^>nB_L5#zH${YFR4kg#X0@UnMgm1`~5ITb-kh=P;ew-A#`jCY$pH+4+l4#OCIQd z760xF!FJ)$EK+hmmy2&t=P(=Ad0Y2!UJ>*qrLDm(wtDKr@MmQhJKfgiP^qpmnB;k29OY>8w3UmUPNe<&}w1xn7qMy6T{=D{#+G5 zw(ZGRCr`DvA#tRa!jWHEoGD$-0)N6NlG9B|O?49T`CaJLSv%HmFq^)VW$d6-mP0av z+ER{580Y|=oBI%v(_r#;T|J2z%W144-Xwn2W*6!HwjxhnIJ;0*BQokT*9A%?T$U~{ z>Qu-jW-&KqIVA4E87@}D(y9h0TziHErtj0xEDOKwPx^w2S9euQF!=cfoyW^D$6U$; z*L~LvoFDK3zt`YIqm zQqcZPtn~EOh~lIe&eQGFaQ9T(O$yCBvSbndNfALvBd4YK#dP}pqRrQk&5j=9Z8yt| zoB-?9fnLW~7rx1Vbb%bIcq|Jcm{CuX((7{D72alx^H)g;>Zc@!z9Z)fW+IjfLg zAek6=t^&iF7|#%u-I}6ipN^Lo8Hx)g<$O-_%C+xb)a!XZgv&;w6^{*OB5~qMFWQcR z!qR*R%Y!UfNuW84Y~_?-5uX{gepxSUciw{TI)zj3+mI?XKWP1uRBxQNcW@!Qe0o;z2gU?gJ8_%65QYk@A-Out)cRp517OG z+T?*ZXJeo1JkBHQT?tXHR^si+v(5_vNkLr7+qzN^bOi~~TiBM19IXD+Z=Tt+n}@%6 z#70545$dBa2IO#j1-&^p2~;KGrY34NDHgRZe6_}5h6{Kewol)@Im+ERKznfAgK+gC zPCh9-Avap`O1zErk!3;Ec8mR)h(UA(B)5}u zCmH+JP=*ZpNZjh)&F4kSO__SE9epd?6sUUS-wVbH3oHC?WY`6bKXN0-u284#hkzfA zF{FFQbH~uES()_tk-Lv(q~Ohwe3SM#VBtdxEt!e&diS(H>hkJ<({M{PvZE1I~Q@S&y2r0Gt zKHqX(*e90XY6*AP626MIBEh%oYAhwxltLh7| z8wx-eQf#TxJp@JchzMp)GVqYiO8oit3~lidP5xKcuVPvLyYZ92blLMide^P%Y@e4f^a zH{8`SBMNkshr;{ot}i-th-(AiwYDcC6=yA!8dmAEA^kCqTM6Dg()TYIP?1;RpACiL z6)p$~f*JGi-6-m#GBJ9ER{o$Y`w7pQj@6pi;pEsqg41sd=fRj(6Pbk{h{(X~a4;zD zElO4p@TRya%oQkF38U>xzoUD9s5_w~)GUl_PDJL)Ocz?VcAjl!1hkqmcHsFG5;l+n zvrxGtwiuxLA+1Si0pM9a8vMqLl`Q~mVw-?bai?dW77mi zl!dTM{>Dn7tnx>@p%$0b&|W}p$jTO;5#q@;dksjgOIQCKcG~T2QD&CeHQSFJ4!Sj0 zeQ3u#l@b#&pah*Am}Z~*tt4F;x3VbMOA4u{*u}bi9nqr70$Lu62h68)ZVp$rnD=n! zhP-nHI{&Xt42b->p;v9TGHft6&rbpl2IkP4_VX8`9B`i){k~R zRj>*3x2X^laUS~)+1?-ZwSyIgcQ+Y$5C@kD@8^#<$Dvv8Nvz*#PlqoC?<&$%5>Vgz zM(a)1N-e;tFS?S0&L?~z?7tuqcdnG|JJkowYM2GqP0#M$tKWX-3Q`}x`HX)BQ1Cwd zy*V?7a~PhGOHb+L1#OhQyvyOqO@%4!z$^V}`SlP1SrbC?d(pkGBq=-hR0Wj1miiXa z2Bb4st(_t&NSn&0sbfVlSxRH3{$IHaNs0)V{6?B#s#iNmWG-wM~hR2)zNaZ95b(4@@R!Jwjv z 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 literal 0 HcmV?d00001 diff --git a/python-test-samples/hexagonal-architectures/src/adapters/CurrencyExchangeDB.py b/python-test-samples/hexagonal-architectures/src/adapters/CurrencyExchangeDB.py index beab0a18..c95ec701 100644 --- a/python-test-samples/hexagonal-architectures/src/adapters/CurrencyExchangeDB.py +++ b/python-test-samples/hexagonal-architectures/src/adapters/CurrencyExchangeDB.py @@ -1,5 +1,4 @@ from os import environ -import json import boto3 def getCurrencies(currencies): @@ -15,4 +14,5 @@ def getCurrencies(currencies): print(rates) return rates except Exception as e: - print(e) \ No newline at end of file + print("getCurrencies Error:" + str(e) + " : " + str(type(e))) + raise e \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/adapters/HandleStockRequest.py b/python-test-samples/hexagonal-architectures/src/adapters/HandleStockRequest.py index 84e23614..74cda4d7 100644 --- a/python-test-samples/hexagonal-architectures/src/adapters/HandleStockRequest.py +++ b/python-test-samples/hexagonal-architectures/src/adapters/HandleStockRequest.py @@ -13,5 +13,5 @@ def getStocksRequest(stockID): } return response except Exception as e: - print(e) - raise + print("getStocksRequest Error:" + str(e) + " : " + str(type(e))) + raise e diff --git a/python-test-samples/hexagonal-architectures/src/adapters/StocksDB.py b/python-test-samples/hexagonal-architectures/src/adapters/StocksDB.py index 2b477cf1..a4abf6eb 100644 --- a/python-test-samples/hexagonal-architectures/src/adapters/StocksDB.py +++ b/python-test-samples/hexagonal-architectures/src/adapters/StocksDB.py @@ -1,5 +1,4 @@ from os import environ -import json import boto3 from botocore.exceptions import ClientError @@ -16,5 +15,5 @@ def getStockValue(stockID): print("dynamodb response", dynamodb_response) return dynamodb_response except Exception as e: - print(e) - raise \ No newline at end of file + print("getStockValue Error:" + str(e) + " : " + str(type(e))) + raise e \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/app.py b/python-test-samples/hexagonal-architectures/src/app.py index 4a597304..d74d1e9a 100644 --- a/python-test-samples/hexagonal-architectures/src/app.py +++ b/python-test-samples/hexagonal-architectures/src/app.py @@ -16,16 +16,15 @@ def lambda_handler(event, context) -> dict: try: - print(event) stockID = event["pathParameters"]["StockID"] response = HandleStockRequest.getStocksRequest(stockID) - print("Response", response) return response except ValueError as e: - print("V", e) + print("lambda_handler ValueError:" + str(e) + " : " + str(type(e))) return { "statusCode": 404, "body": "Stock not found" } except Exception as e: - raise \ No newline at end of file + print("lambda_handler Error:" + str(e) + " : " + str(type(e))) + raise e \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/domains/stock.py b/python-test-samples/hexagonal-architectures/src/domains/stock.py index 9b5d864d..47c4681e 100644 --- a/python-test-samples/hexagonal-architectures/src/domains/stock.py +++ b/python-test-samples/hexagonal-architectures/src/domains/stock.py @@ -18,12 +18,9 @@ def retrieveStockValues(stockID): "EUR": float(stockValue["Item"]["Value"]) } } - print(stockWithCurrencies) for currency in currencies: - print(currencyList[currency]) stockWithCurrencies["values"][currency] = float(Decimal(stockValue["Item"]["Value"]) * currencyList[currency]) - print(stockWithCurrencies) return stockWithCurrencies except ValueError as e: - print(e) - raise \ No newline at end of file + print("retrieveStockValues Error:" + str(e) + " : " + str(type(e))) + raise e \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/ports/CurrenciesService.py b/python-test-samples/hexagonal-architectures/src/ports/CurrenciesService.py index 444f197c..070b8fbe 100644 --- a/python-test-samples/hexagonal-architectures/src/ports/CurrenciesService.py +++ b/python-test-samples/hexagonal-architectures/src/ports/CurrenciesService.py @@ -5,4 +5,5 @@ def getCurrenciesData(currencies): data = CurrencyExchangeDB.getCurrencies(currencies) return data except Exception as e: - print(e) \ No newline at end of file + print("getCurrenciesData Error:" + str(e) + " : " + str(type(e))) + raise e \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/ports/HttpHandler.py b/python-test-samples/hexagonal-architectures/src/ports/HttpHandler.py index ec282dbe..95a3de68 100644 --- a/python-test-samples/hexagonal-architectures/src/ports/HttpHandler.py +++ b/python-test-samples/hexagonal-architectures/src/ports/HttpHandler.py @@ -5,5 +5,5 @@ def retrieveStock(stockID): stockWithCurrencies = stock.retrieveStockValues(stockID) return stockWithCurrencies except Exception as e: - print(e) - raise \ No newline at end of file + print("retrieveStock Error:" + str(e) + " : " + str(type(e))) + raise e \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/ports/StocksService.py b/python-test-samples/hexagonal-architectures/src/ports/StocksService.py index 1bcda8a9..bca821fa 100644 --- a/python-test-samples/hexagonal-architectures/src/ports/StocksService.py +++ b/python-test-samples/hexagonal-architectures/src/ports/StocksService.py @@ -5,5 +5,5 @@ def getStockData(stockID): data = StocksDB.getStockValue(stockID) return data except Exception as e: - print(e) - raise \ No newline at end of file + print("getStockData Error:" + str(e) + " : " + str(type(e))) + raise e \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/tests/events/testevent-failure.json b/python-test-samples/hexagonal-architectures/tests/events/testevent-failure.json index d58f7ffb..3c8a87ac 100644 --- a/python-test-samples/hexagonal-architectures/tests/events/testevent-failure.json +++ b/python-test-samples/hexagonal-architectures/tests/events/testevent-failure.json @@ -59,7 +59,7 @@ }, "body": "eyJ0ZXN0IjoiYm9keSJ9", "pathParameters": { - "stockID": "2" + "StockID": "40" }, "isBase64Encoded": true, "stageVariables": { diff --git a/python-test-samples/hexagonal-architectures/tests/events/testevent.json b/python-test-samples/hexagonal-architectures/tests/events/testevent.json index 2463d39d..5beffc5e 100644 --- a/python-test-samples/hexagonal-architectures/tests/events/testevent.json +++ b/python-test-samples/hexagonal-architectures/tests/events/testevent.json @@ -59,7 +59,7 @@ }, "body": "eyJ0ZXN0IjoiYm9keSJ9", "pathParameters": { - "stockID": "1" + "StockID": "1" }, "isBase64Encoded": true, "stageVariables": { diff --git a/python-test-samples/hexagonal-architectures/tests/unit/mock_test.py b/python-test-samples/hexagonal-architectures/tests/unit/mock_test.py index b5cd87ca..0778d2a5 100644 --- a/python-test-samples/hexagonal-architectures/tests/unit/mock_test.py +++ b/python-test-samples/hexagonal-architectures/tests/unit/mock_test.py @@ -4,10 +4,8 @@ from decimal import Decimal from os import environ import json -from datetime import datetime from unittest import TestCase from typing import Any, Dict -from uuid import uuid4 import pytest import yaml import boto3 @@ -129,5 +127,6 @@ def test_lambda_handler_failure(self): """ test_event = self.load_test_event("testevent-failure") - with pytest.raises(ValueError): - test_return = app.lambda_handler(event=test_event,context=None) \ No newline at end of file + test_return = app.lambda_handler(event=test_event,context=None) + self.assertEqual( test_return["statusCode"] , 404) + self.assertEqual( test_return["body"], "Stock not found") \ No newline at end of file From 92548628f8cf737a009f20331a99c266a6556127 Mon Sep 17 00:00:00 2001 From: Tom Romano Date: Wed, 3 May 2023 12:42:10 -0400 Subject: [PATCH 15/16] Resolve linting score --- .../src/adapters/HandleStockRequest.py | 17 ------ ...yExchangeDB.py => currency_exchange_db.py} | 15 ++++-- .../src/adapters/handle_stock_request.py | 23 ++++++++ .../adapters/{StocksDB.py => stocks_db.py} | 19 ++++--- .../hexagonal-architectures/src/app.py | 32 +++++------ .../src/domains/stock.py | 42 +++++++++------ .../src/ports/CurrenciesService.py | 9 ---- .../src/ports/HttpHandler.py | 9 ---- .../src/ports/StocksService.py | 9 ---- .../src/ports/currencies_service.py | 16 ++++++ .../src/ports/http_handler.py | 16 ++++++ .../src/ports/stocks_service.py | 16 ++++++ .../tests/integration/test_api_gateway.py | 54 ++++++++++--------- .../tests/unit/mock_test.py | 40 +++++++------- 14 files changed, 187 insertions(+), 130 deletions(-) delete mode 100644 python-test-samples/hexagonal-architectures/src/adapters/HandleStockRequest.py rename python-test-samples/hexagonal-architectures/src/adapters/{CurrencyExchangeDB.py => currency_exchange_db.py} (67%) create mode 100644 python-test-samples/hexagonal-architectures/src/adapters/handle_stock_request.py rename python-test-samples/hexagonal-architectures/src/adapters/{StocksDB.py => stocks_db.py} (65%) delete mode 100644 python-test-samples/hexagonal-architectures/src/ports/CurrenciesService.py delete mode 100644 python-test-samples/hexagonal-architectures/src/ports/HttpHandler.py delete mode 100644 python-test-samples/hexagonal-architectures/src/ports/StocksService.py create mode 100644 python-test-samples/hexagonal-architectures/src/ports/currencies_service.py create mode 100644 python-test-samples/hexagonal-architectures/src/ports/http_handler.py create mode 100644 python-test-samples/hexagonal-architectures/src/ports/stocks_service.py diff --git a/python-test-samples/hexagonal-architectures/src/adapters/HandleStockRequest.py b/python-test-samples/hexagonal-architectures/src/adapters/HandleStockRequest.py deleted file mode 100644 index 74cda4d7..00000000 --- a/python-test-samples/hexagonal-architectures/src/adapters/HandleStockRequest.py +++ /dev/null @@ -1,17 +0,0 @@ -from src.ports import HttpHandler -import json - -def getStocksRequest(stockID): - try: - stockData = HttpHandler.retrieveStock(stockID) - print("Stock Data", stockData) - response = { - 'statusCode': 200, - 'body': json.dumps({ - "message": stockData, - }) - } - return response - except Exception as e: - print("getStocksRequest Error:" + str(e) + " : " + str(type(e))) - raise e diff --git a/python-test-samples/hexagonal-architectures/src/adapters/CurrencyExchangeDB.py b/python-test-samples/hexagonal-architectures/src/adapters/currency_exchange_db.py similarity index 67% rename from python-test-samples/hexagonal-architectures/src/adapters/CurrencyExchangeDB.py rename to python-test-samples/hexagonal-architectures/src/adapters/currency_exchange_db.py index c95ec701..8c254775 100644 --- a/python-test-samples/hexagonal-architectures/src/adapters/CurrencyExchangeDB.py +++ b/python-test-samples/hexagonal-architectures/src/adapters/currency_exchange_db.py @@ -1,7 +1,13 @@ +""" +Currentcy Exchange DB Adapter +""" from os import environ import boto3 -def getCurrencies(currencies): +def get_currencies(currencies): + """ + get_currencies: Gets the currencies from the DynamoDB table. + """ try: dynamodb_table_name = environ["CURRENCIES_DB_TABLE"] dynamodb_resource = boto3.resource('dynamodb') @@ -13,6 +19,7 @@ def getCurrencies(currencies): rates[dynamodb_response["Item"]["CURRENCY"]] = dynamodb_response["Item"]["Rate"] print(rates) return rates - except Exception as e: - print("getCurrencies Error:" + str(e) + " : " + str(type(e))) - raise e \ No newline at end of file + except Exception as err: + print("get_currencies Error:" + str(err) + " : " + str(type(err))) + raise err + \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/adapters/handle_stock_request.py b/python-test-samples/hexagonal-architectures/src/adapters/handle_stock_request.py new file mode 100644 index 00000000..4757d80b --- /dev/null +++ b/python-test-samples/hexagonal-architectures/src/adapters/handle_stock_request.py @@ -0,0 +1,23 @@ +""" +Stock Request DB Adapter +""" +import json +from src.ports import http_handler + +def get_stocks_request(stock_id): + """ + get_stocks_request - retrieve a stock from the DB + """ + try: + stock_data = http_handler.retrieve_stock(stock_id) + print("Stock Data", stock_data) + response = { + 'statusCode': 200, + 'body': json.dumps({ + "message": stock_data, + }) + } + return response + except Exception as err: + print("get_stocks_request Error:" + str(err) + " : " + str(type(err))) + raise err diff --git a/python-test-samples/hexagonal-architectures/src/adapters/StocksDB.py b/python-test-samples/hexagonal-architectures/src/adapters/stocks_db.py similarity index 65% rename from python-test-samples/hexagonal-architectures/src/adapters/StocksDB.py rename to python-test-samples/hexagonal-architectures/src/adapters/stocks_db.py index a4abf6eb..a6dfe511 100644 --- a/python-test-samples/hexagonal-architectures/src/adapters/StocksDB.py +++ b/python-test-samples/hexagonal-architectures/src/adapters/stocks_db.py @@ -1,19 +1,24 @@ +""" +Stock Value DB Adapter +""" from os import environ import boto3 -from botocore.exceptions import ClientError - -def getStockValue(stockID): +def get_stock_value(stock_id): + """ + get_stock_value - retrieve a stock from the DB + """ try: dynamodb_table_name = environ["STOCKS_DB_TABLE"] dynamodb_resource = boto3.resource('dynamodb') dynamodb_table = dynamodb_resource.Table(dynamodb_table_name) print(f"Using DynamoDB Table {dynamodb_table_name}.") - dynamodb_response = dynamodb_table.get_item(Key={"STOCK_ID": f"{stockID}"}) + dynamodb_response = dynamodb_table.get_item(Key={"STOCK_ID": f"{stock_id}"}) if "Item" not in dynamodb_response: raise ValueError("Stock not found") print("dynamodb response", dynamodb_response) return dynamodb_response - except Exception as e: - print("getStockValue Error:" + str(e) + " : " + str(type(e))) - raise e \ No newline at end of file + except Exception as err: + print("get_stock_value Error:" + str(err) + " : " + str(type(err))) + raise err + \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/app.py b/python-test-samples/hexagonal-architectures/src/app.py index d74d1e9a..792b2f20 100644 --- a/python-test-samples/hexagonal-architectures/src/app.py +++ b/python-test-samples/hexagonal-architectures/src/app.py @@ -1,30 +1,32 @@ +""" # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 -# Lambda Handler for the Python hexagonal-architectures example -# This handler accepts a stock ID and provides the price of the stock in four currencies. -# The id and price in Euros associated with the stock is stored in a DynamoDB Table. -# The currency conversion rates for Euros to USD, CAD, and AUD are stored in another DynamoDB table. - -from os import environ -from datetime import datetime -from src.adapters import HandleStockRequest +Lambda Handler for the Python hexagonal-architectures example +This handler accepts a stock ID and provides the price of the stock in four currencies. +The id and price in Euros associated with the stock is stored in a DynamoDB Table. +The currency conversion rates for Euros to USD, CAD, and AUD are stored in another DynamoDB table. +""" +from src.adapters import handle_stock_request # from aws_lambda_powertools.utilities.data_classes import APIGatewayProxyEvent # from aws_lambda_powertools.utilities.typing import LambdaContext # from aws_lambda_powertools.utilities.validation import validator def lambda_handler(event, context) -> dict: + """ + lambda_handler: Entry Point + """ try: - stockID = event["pathParameters"]["StockID"] - response = HandleStockRequest.getStocksRequest(stockID) + stock_id = event["pathParameters"]["StockID"] + response = handle_stock_request.get_stocks_request(stock_id) return response - except ValueError as e: - print("lambda_handler ValueError:" + str(e) + " : " + str(type(e))) + except ValueError as err: + print("lambda_handler ValueError:" + str(err) + " : " + str(type(err))) return { "statusCode": 404, "body": "Stock not found" } - except Exception as e: - print("lambda_handler Error:" + str(e) + " : " + str(type(e))) - raise e \ No newline at end of file + except Exception as err: + print("lambda_handler Error:" + str(err) + " : " + str(type(err))) + raise err diff --git a/python-test-samples/hexagonal-architectures/src/domains/stock.py b/python-test-samples/hexagonal-architectures/src/domains/stock.py index 47c4681e..dac94441 100644 --- a/python-test-samples/hexagonal-architectures/src/domains/stock.py +++ b/python-test-samples/hexagonal-architectures/src/domains/stock.py @@ -1,26 +1,34 @@ +""" +Stock Domain +""" from decimal import Decimal -from src.ports import CurrenciesService - -from src.ports import StocksService +from src.ports import currencies_service +from src.ports import stocks_service currencies = ["USD", "CAD", "AUD"] -def retrieveStockValues(stockID): + +def retrieve_stock_values(stock_id): + """ + retrieve_stock_values: fetch stock in multiple currencies + """ try: - stockValue = StocksService.getStockData(stockID) - currencyList = CurrenciesService.getCurrenciesData(currencies) - print("STOCK VALUE", stockValue) - print("CURRENCY LIST", currencyList) - - stockWithCurrencies = { - "stock": stockValue["Item"]["STOCK_ID"], + stock_value = stocks_service.get_stock_data(stock_id) + currency_list = currencies_service.get_currencies_data(currencies) + print("STOCK VALUE", stock_value) + print("CURRENCY LIST", currency_list) + + stock_with_currencies = { + "stock": stock_value["Item"]["STOCK_ID"], "values": { - "EUR": float(stockValue["Item"]["Value"]) + "EUR": float(stock_value["Item"]["Value"]) } } for currency in currencies: - stockWithCurrencies["values"][currency] = float(Decimal(stockValue["Item"]["Value"]) * currencyList[currency]) - return stockWithCurrencies - except ValueError as e: - print("retrieveStockValues Error:" + str(e) + " : " + str(type(e))) - raise e \ No newline at end of file + stock_with_currencies["values"][currency] = \ + float(Decimal(stock_value["Item"]["Value"]) * currency_list[currency]) + return stock_with_currencies + except ValueError as err: + print("retrieve_stock_values Error:" + str(err) + " : " + str(type(err))) + raise err + \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/ports/CurrenciesService.py b/python-test-samples/hexagonal-architectures/src/ports/CurrenciesService.py deleted file mode 100644 index 070b8fbe..00000000 --- a/python-test-samples/hexagonal-architectures/src/ports/CurrenciesService.py +++ /dev/null @@ -1,9 +0,0 @@ -from src.adapters import CurrencyExchangeDB - -def getCurrenciesData(currencies): - try: - data = CurrencyExchangeDB.getCurrencies(currencies) - return data - except Exception as e: - print("getCurrenciesData Error:" + str(e) + " : " + str(type(e))) - raise e \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/ports/HttpHandler.py b/python-test-samples/hexagonal-architectures/src/ports/HttpHandler.py deleted file mode 100644 index 95a3de68..00000000 --- a/python-test-samples/hexagonal-architectures/src/ports/HttpHandler.py +++ /dev/null @@ -1,9 +0,0 @@ -from src.domains import stock - -def retrieveStock(stockID): - try: - stockWithCurrencies = stock.retrieveStockValues(stockID) - return stockWithCurrencies - except Exception as e: - print("retrieveStock Error:" + str(e) + " : " + str(type(e))) - raise e \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/ports/StocksService.py b/python-test-samples/hexagonal-architectures/src/ports/StocksService.py deleted file mode 100644 index bca821fa..00000000 --- a/python-test-samples/hexagonal-architectures/src/ports/StocksService.py +++ /dev/null @@ -1,9 +0,0 @@ -from src.adapters import StocksDB - -def getStockData(stockID): - try: - data = StocksDB.getStockValue(stockID) - return data - except Exception as e: - print("getStockData Error:" + str(e) + " : " + str(type(e))) - raise e \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/ports/currencies_service.py b/python-test-samples/hexagonal-architectures/src/ports/currencies_service.py new file mode 100644 index 00000000..0ab597e2 --- /dev/null +++ b/python-test-samples/hexagonal-architectures/src/ports/currencies_service.py @@ -0,0 +1,16 @@ +""" +Currency Exchange DB Port +""" +from src.adapters import currency_exchange_db + +def get_currencies_data(currencies): + """ + get_currencies_data: Retrieve Currencies + """ + try: + data = currency_exchange_db.get_currencies(currencies) + return data + except Exception as err: + print("get_currencies_data Error:" + str(err) + " : " + str(type(err))) + raise err + \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/ports/http_handler.py b/python-test-samples/hexagonal-architectures/src/ports/http_handler.py new file mode 100644 index 00000000..3685e459 --- /dev/null +++ b/python-test-samples/hexagonal-architectures/src/ports/http_handler.py @@ -0,0 +1,16 @@ +""" +Stock Port +""" +from src.domains import stock + +def retrieve_stock(stock_id): + """ + retrieve_stock: Fetch a stock + """ + try: + stock_with_currencies = stock.retrieve_stock_values(stock_id) + return stock_with_currencies + except Exception as err: + print("retrieve_stock Error:" + str(err) + " : " + str(type(err))) + raise err + \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/src/ports/stocks_service.py b/python-test-samples/hexagonal-architectures/src/ports/stocks_service.py new file mode 100644 index 00000000..1a4b229a --- /dev/null +++ b/python-test-samples/hexagonal-architectures/src/ports/stocks_service.py @@ -0,0 +1,16 @@ +""" +Stock Service Port +""" +from src.adapters import stocks_db + +def get_stock_data(stock_id): + """ + get_stock_data: Retrieve a stock from the stock db adapter + """ + try: + data = stocks_db.get_stock_value(stock_id) + return data + except Exception as err: + print("get_stock_data Error:" + str(err) + " : " + str(type(err))) + raise err + \ No newline at end of file diff --git a/python-test-samples/hexagonal-architectures/tests/integration/test_api_gateway.py b/python-test-samples/hexagonal-architectures/tests/integration/test_api_gateway.py index 1ade43d0..7aba12c1 100644 --- a/python-test-samples/hexagonal-architectures/tests/integration/test_api_gateway.py +++ b/python-test-samples/hexagonal-architectures/tests/integration/test_api_gateway.py @@ -1,31 +1,36 @@ +""" +Integration Test + +Set the environment variable AWS_SAM_STACK_NAME +to match the name of the stack you will test + +AWS_SAM_STACK_NAME= python -m pytest -s tests/integration -v +""" # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 import os from decimal import Decimal from unittest import TestCase -from uuid import uuid4 -from boto3.dynamodb.conditions import Key import boto3 import requests -""" -Set the environment variable AWS_SAM_STACK_NAME -to match the name of the stack you will test - -AWS_SAM_STACK_NAME= python -m pytest -s tests/integration -v -""" - class TestApiGateway(TestCase): - api_endpoint: str + """ + Integration test Class + """ + api_endpoint: str aws_region = os.environ.get("AWS_DEFAULT_REGION") or "us-east-1" @classmethod def get_stack_name(cls) -> str: + """ + Return the stack name + """ stack_name = os.environ.get("AWS_SAM_STACK_NAME") if not stack_name: - raise Exception( + raise LookupError( "Cannot find env var AWS_SAM_STACK_NAME. \n" "Please setup this environment variable with the stack name where we are running integration tests." ) @@ -44,10 +49,10 @@ def setUp(self) -> None: print(stack_name) try: response = client.describe_stacks(StackName=stack_name) - except Exception as e: - raise Exception( + except Exception as err: + raise LookupError( f"Cannot find stack {stack_name}. \n" f'Please make sure stack with the name "{stack_name}" exists.' - ) from e + ) from err # StockConverterApi print(stack_name, response) @@ -59,28 +64,27 @@ def setUp(self) -> None: # CurrenciesTableName currencies_dynamodb_outputs = [output for output in stack_outputs if output["OutputKey"] == "CurrenciesTableName"] self.assertTrue(currencies_dynamodb_outputs, f"Cannot find output DynamoDBTableName in stack {stack_name}") - self.currencies_dynamodb_table_name = currencies_dynamodb_outputs[0]["OutputValue"] + self.currencies_dynamodb_table_name = currencies_dynamodb_outputs[0]["OutputValue"] # Seed the Currencies DynamoDB Table with Test Data dynamodb_resource = boto3.resource("dynamodb", region_name = self.aws_region) currencies_dynamodb_table = dynamodb_resource.Table(name=self.currencies_dynamodb_table_name) - currencies_dynamodb_table.put_item(Item={"CURRENCY": "USD", + currencies_dynamodb_table.put_item(Item={"CURRENCY": "USD", "Rate": Decimal("1.31")}) - currencies_dynamodb_table.put_item(Item={"CURRENCY": "CAD", + currencies_dynamodb_table.put_item(Item={"CURRENCY": "CAD", "Rate": Decimal("1.41")}) - currencies_dynamodb_table.put_item(Item={"CURRENCY": "AUD", + currencies_dynamodb_table.put_item(Item={"CURRENCY": "AUD", "Rate": Decimal("1.51")}) # StocksTableName stocks_dynamodb_outputs = [output for output in stack_outputs if output["OutputKey"] == "StocksTableName"] self.assertTrue(stocks_dynamodb_outputs, f"Cannot find output DynamoDBTableName in stack {stack_name}") - self.stocks_dynamodb_table_name = stocks_dynamodb_outputs[0]["OutputValue"] + self.stocks_dynamodb_table_name = stocks_dynamodb_outputs[0]["OutputValue"] # Seed the Currencies DynamoDB Table with Test Data dynamodb_resource = boto3.resource("dynamodb", region_name = self.aws_region) stocks_dynamodb_table = dynamodb_resource.Table(name=self.stocks_dynamodb_table_name) - stocks_dynamodb_table.put_item(Item={"STOCK_ID": "1", - "Value": 3}) + stocks_dynamodb_table.put_item(Item={"STOCK_ID": "1","Value": 3}) def tearDown(self) -> None: @@ -90,8 +94,8 @@ def tearDown(self) -> None: dynamodb_resource = boto3.resource("dynamodb", region_name = self.aws_region) currencies_dynamodb_table = dynamodb_resource.Table(name=self.currencies_dynamodb_table_name) - for id in ["AUD", "USD", "CAD"]: - currencies_dynamodb_table.delete_item(Key={"CURRENCY":id}) + for currency_id in ["AUD", "USD", "CAD"]: + currencies_dynamodb_table.delete_item(Key={"CURRENCY":currency_id}) stocks_dynamodb_table = dynamodb_resource.Table(name=self.stocks_dynamodb_table_name) stocks_dynamodb_table.delete_item(Key={"STOCK_ID":"1"}) @@ -108,6 +112,6 @@ def test_api_gateway_200(self): def test_api_gateway_404(self): """ Call the API Gateway endpoint and check the response for a 404 (id not found) - """ + """ response = requests.get(self.api_endpoint.replace("{stock_id}","2")) - self.assertEqual(response.status_code, requests.codes.not_found) \ No newline at end of file + self.assertEqual(response.status_code, requests.codes.not_found) diff --git a/python-test-samples/hexagonal-architectures/tests/unit/mock_test.py b/python-test-samples/hexagonal-architectures/tests/unit/mock_test.py index 0778d2a5..afa4af4e 100644 --- a/python-test-samples/hexagonal-architectures/tests/unit/mock_test.py +++ b/python-test-samples/hexagonal-architectures/tests/unit/mock_test.py @@ -1,3 +1,6 @@ +""" +Unit Test +""" # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 @@ -6,10 +9,8 @@ import json from unittest import TestCase from typing import Any, Dict -import pytest import yaml import boto3 -from boto3.dynamodb.conditions import Key import moto @@ -23,7 +24,7 @@ class TestSampleLambdaWithDynamoDB(TestCase): """ Unit Test class for src/app.py """ - + def setUp(self) -> None: """ Test Set up: @@ -34,12 +35,13 @@ def setUp(self) -> None: # Create a name for a test table, and set the environment self.test_stocks_table_name = "unit_test_stock_table_name" - environ["STOCKS_DB_TABLE"] = self.test_stocks_table_name + environ["STOCKS_DB_TABLE"] = self.test_stocks_table_name # Create a mock table using the definition from the SAM YAML template # This simple technique works if there are no intrinsics (like !If or !Ref) in the # resource properties for KeySchema, AttributeDefinitions, & BillingMode. - sam_template_table_properties = self.read_sam_template()["Resources"]["StocksTable"]["Properties"] + sam_template_table_properties = \ + self.read_sam_template()["Resources"]["StocksTable"]["Properties"] self.mock_dynamodb = boto3.resource("dynamodb") self.mock_dynamodb_table = self.mock_dynamodb.create_table( TableName = self.test_stocks_table_name, @@ -47,20 +49,21 @@ def setUp(self) -> None: AttributeDefinitions = sam_template_table_properties["AttributeDefinitions"], BillingMode = sam_template_table_properties["BillingMode"] ) - + # Populate data for the tests self.mock_dynamodb_table.put_item(Item={ "STOCK_ID": "1", "Value": 3}) - + # Create a name for a test table, and set the environment self.test_currencies_table_name = "unit_test_currencies_table_name" - environ["CURRENCIES_DB_TABLE"] = self.test_currencies_table_name + environ["CURRENCIES_DB_TABLE"] = self.test_currencies_table_name # Create a mock table using the definition from the SAM YAML template # This simple technique works if there are no intrinsics (like !If or !Ref) in the # resource properties for KeySchema, AttributeDefinitions, & BillingMode. - sam_template_table_properties = self.read_sam_template()["Resources"]["CurrenciesTable"]["Properties"] + sam_template_table_properties = \ + self.read_sam_template()["Resources"]["CurrenciesTable"]["Properties"] self.mock_dynamodb = boto3.resource("dynamodb") self.mock_currencies_table = self.mock_dynamodb.create_table( TableName = self.test_currencies_table_name, @@ -68,7 +71,7 @@ def setUp(self) -> None: AttributeDefinitions = sam_template_table_properties["AttributeDefinitions"], BillingMode = sam_template_table_properties["BillingMode"] ) - + # Populate data for the tests self.mock_currencies_table.put_item(Item={ "CURRENCY": "USD", @@ -90,19 +93,19 @@ def read_sam_template(self, sam_template_fn : str = "template.yaml" ) -> dict: """ Utility Function to read the SAM template for the current project """ - with open(sam_template_fn, "r") as fp: - template =fp.read().replace("!","") # Ignoring intrinsic tags + with open(sam_template_fn, "r",encoding="utf-8") as fptr: + template =fptr.read().replace("!","") # Ignoring intrinsic tags return yaml.safe_load(template) def load_test_event(self, test_event_file_name: str) -> Dict[str, Any]: """ Load a sample event from a file """ - with open(f"tests/events/{test_event_file_name}.json","r") as f: - event = json.load(f) + with open(f"tests/events/{test_event_file_name}.json","r",encoding="utf-8") as fptr: + event = json.load(fptr) return event - + def test_lambda_handler_happy_path(self): """ Happy path test where the stock ID exists in the DynamoDB Table @@ -115,8 +118,9 @@ def test_lambda_handler_happy_path(self): test_event = self.load_test_event("testevent") test_return = app.lambda_handler(event=test_event,context=None) self.assertEqual( test_return["statusCode"] , 200) - self.assertEqual( test_return["body"] , '{"message": {"stock": "1", "values": {"EUR": 3.0, "USD": 3.93, "CAD": 4.23, "AUD": 4.53}}}') - + expected = '{"message": {"stock": "1", "values": {"EUR": 3.0, "USD": 3.93, "CAD": 4.23, "AUD": 4.53}}}' + self.assertEqual( test_return["body"] , expected) + def test_lambda_handler_failure(self): """ Failure Test where the stock ID does not exist in the DynamoDB Table @@ -129,4 +133,4 @@ def test_lambda_handler_failure(self): test_event = self.load_test_event("testevent-failure") test_return = app.lambda_handler(event=test_event,context=None) self.assertEqual( test_return["statusCode"] , 404) - self.assertEqual( test_return["body"], "Stock not found") \ No newline at end of file + self.assertEqual( test_return["body"], "Stock not found") From 8f20fafabcdee166bf15aca968a102a1244819bf Mon Sep 17 00:00:00 2001 From: Tom Romano Date: Wed, 3 May 2023 12:43:43 -0400 Subject: [PATCH 16/16] Resolving diff to main branch --- .github/workflows/metadata-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/metadata-validation.yml b/.github/workflows/metadata-validation.yml index 521a0853..985c9c22 100644 --- a/.github/workflows/metadata-validation.yml +++ b/.github/workflows/metadata-validation.yml @@ -49,4 +49,4 @@ jobs: echo $folder pip install -r .github/workflows/requirements.txt python .github/workflows/metadata_json_validator.py $folder - done + done \ No newline at end of file