diff --git a/chapter07/pyproject.toml b/chapter07/pyproject.toml new file mode 100644 index 0000000..25ba7d0 --- /dev/null +++ b/chapter07/pyproject.toml @@ -0,0 +1,2 @@ +[tool.pytest.ini_options] +pythonpath = "src" diff --git a/chapter07/samconfig.toml b/chapter07/samconfig.toml new file mode 100644 index 0000000..81b1cf2 --- /dev/null +++ b/chapter07/samconfig.toml @@ -0,0 +1,8 @@ +version = 0.1 +[default.deploy.parameters] +stack_name = "chapter07-stack" +resolve_s3 = true +s3_prefix = "chapter07-stack" +region = "ap-northeast-1" +capabilities = "CAPABILITY_IAM" +image_repositories = [] diff --git a/chapter07/src/receiver.py b/chapter07/src/receiver.py new file mode 100644 index 0000000..e3d0f9d --- /dev/null +++ b/chapter07/src/receiver.py @@ -0,0 +1,24 @@ +import json +import os + +import boto3 + +if os.environ['ENV'] == 'local': + s3 = boto3.client('s3', endpoint_url='http://localhost.localstack.cloud:4566') +elif os.environ['ENV'] == 'test': + s3 = boto3.client('s3', endpoint_url='http://localhost:14566') + + +def main(event): + for record in event['Records']: + print(record) + body = json.loads(record['body']) + s3.put_object( + Bucket='chapter07-bucket', + Key=f"chapter07/{body['id']}.json", + Body=record['body'], + ) + + +def lambda_handler(event, context): + main(event) diff --git a/chapter07/src/requirements.txt b/chapter07/src/requirements.txt new file mode 100644 index 0000000..d0c4540 --- /dev/null +++ b/chapter07/src/requirements.txt @@ -0,0 +1 @@ +boto3==1.34.120 diff --git a/chapter07/src/sender.py b/chapter07/src/sender.py new file mode 100644 index 0000000..61290b5 --- /dev/null +++ b/chapter07/src/sender.py @@ -0,0 +1,34 @@ +import json +import os +import random + +import boto3 + +if os.environ['ENV'] == 'local': + sqs = boto3.client('sqs', endpoint_url='http://localhost.localstack.cloud:4566') +elif os.environ['ENV'] == 'test': + sqs = boto3.client('sqs', endpoint_url='http://localhost:14566') + + +def main(event): + number = random.randint(0, 9999) + sqs.send_message( + QueueUrl='http://sqs.ap-northeast-1.localhost.localstack.cloud:4566/000000000000/chapter07-queue', + MessageBody=json.dumps( + { + 'id': f'id{number:04}', + 'body': f'This is message {number:04}.', + } + ), + ) + + return { + 'statusCode': 200, + 'body': json.dumps( + {'id': f'id{number:04}'}, + ), + } + + +def lambda_handler(event, context): + return main(event) diff --git a/chapter07/template.yaml b/chapter07/template.yaml new file mode 100644 index 0000000..4c6e612 --- /dev/null +++ b/chapter07/template.yaml @@ -0,0 +1,52 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 + +Resources: + SenderFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: chapter07-sender-function + CodeUri: ./src + Handler: sender.lambda_handler + Runtime: python3.12 + Architectures: + - x86_64 + Environment: + Variables: + ENV: local + Events: + Api: + Type: Api + Properties: + Method: POST + Path: / + Queue: + Type: AWS::SQS::Queue + Properties: + QueueName: chapter07-queue + ReceiveMessageWaitTimeSeconds: 20 + ReceiverFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: chapter07-receiver-function + CodeUri: ./src + Handler: receiver.lambda_handler + Runtime: python3.12 + Architectures: + - x86_64 + Environment: + Variables: + ENV: local + Events: + SqsEvent: + Type: SQS + Properties: + Queue: !GetAtt Queue.Arn + Bucket: + Type: AWS::S3::Bucket + Properties: + BucketName: chapter07-bucket + +Outputs: + ApiId: + Value: !Ref ServerlessRestApi diff --git a/chapter07/tests/requirements-test.txt b/chapter07/tests/requirements-test.txt new file mode 100644 index 0000000..24296dc --- /dev/null +++ b/chapter07/tests/requirements-test.txt @@ -0,0 +1,2 @@ +localstack-utils==1.0.1 +pytest==8.3.2 \ No newline at end of file diff --git a/chapter07/tests/test_receiver.py b/chapter07/tests/test_receiver.py new file mode 100644 index 0000000..00454a8 --- /dev/null +++ b/chapter07/tests/test_receiver.py @@ -0,0 +1,49 @@ +import json +from http import HTTPStatus + +import pytest +from localstack_utils.localstack import startup_localstack, stop_localstack +from receiver import main, s3 + + +@pytest.fixture(scope='module', autouse=True) +def _setup(): + startup_localstack(gateway_listen='0.0.0.0:14566') + + s3.create_bucket( + Bucket='chapter07-bucket', + CreateBucketConfiguration={'LocationConstraint': 'ap-northeast-1'}, + ) + + yield + + stop_localstack() + + +def test_main(): + event = { + 'Records': [ + { + 'body': json.dumps( + { + 'id': 'id0001', + 'body': 'This is message 0001.', + } + ), + } + ], + } + + main(event) + + response = s3.head_object( + Bucket='chapter07-bucket', + Key='chapter07/id0001.json', + ) + assert response['ResponseMetadata']['HTTPStatusCode'] == HTTPStatus.OK.value + + response = s3.get_object( + Bucket='chapter07-bucket', + Key='chapter07/id0001.json', + ) + assert response['Body'].read().decode('utf-8') == '{"id": "id0001", "body": "This is message 0001."}' diff --git a/chapter07/tests/test_sender.py b/chapter07/tests/test_sender.py new file mode 100644 index 0000000..ad8b629 --- /dev/null +++ b/chapter07/tests/test_sender.py @@ -0,0 +1,42 @@ +import json +import re + +import pytest +from localstack_utils.localstack import startup_localstack, stop_localstack +from sender import main, sqs + + +@pytest.fixture(scope='module', autouse=True) +def _setup(): + startup_localstack(gateway_listen='0.0.0.0:14566') + + sqs.create_queue( + QueueName='chapter07-queue', + Attributes={ + 'ReceiveMessageWaitTimeSeconds': '20', + }, + ) + + yield + + stop_localstack() + + +def test_main(): + event = { + 'resource': '/', + 'path': '/', + 'httpMethod': 'POST', + } + + main(event) + + response = sqs.receive_message( + QueueUrl='http://sqs.ap-northeast-1.localhost.localstack.cloud:14566/000000000000/chapter07-queue', + MaxNumberOfMessages=10, + ) + + assert 1 == len(response['Messages']) + body = json.loads(response['Messages'][0]['Body']) + assert 'id' in body + assert re.match(r'id\d{4}', body['id'])