Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f9fffc1

Browse files
authoredJul 18, 2024
Merge pull request #464 from awsdocs/AddSampleAppFiles
Add code files for Python example file-processing app
2 parents 7bb9f8a + f098e2e commit f9fffc1

File tree

5 files changed

+189
-0
lines changed

5 files changed

+189
-0
lines changed
 
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from pypdf import PdfReader, PdfWriter
2+
import uuid
3+
import os
4+
from urllib.parse import unquote_plus
5+
import boto3
6+
7+
# Create the S3 client to download and upload objects from S3
8+
s3_client = boto3.client('s3')
9+
10+
def lambda_handler(event, context):
11+
# Iterate over the S3 event object and get the key for all uploaded files
12+
for record in event['Records']:
13+
bucket = record['s3']['bucket']['name']
14+
key = unquote_plus(record['s3']['object']['key']) # Decode the S3 object key to remove any URL-encoded characters
15+
download_path = f'/tmp/{uuid.uuid4()}.pdf' # Create a path in the Lambda tmp directory to save the file to
16+
upload_path = f'/tmp/converted-{uuid.uuid4()}.pdf' # Create another path to save the encrypted file to
17+
18+
# If the file is a PDF, encrypt it and upload it to the destination S3 bucket
19+
if key.lower().endswith('.pdf'):
20+
s3_client.download_file(bucket, key, download_path)
21+
encrypt_pdf(download_path, upload_path)
22+
encrypted_key = add_encrypted_suffix(key)
23+
s3_client.upload_file(upload_path, f'{bucket}-encrypted', encrypted_key)
24+
25+
# Define the function to encrypt the PDF file with a password
26+
def encrypt_pdf(file_path, encrypted_file_path):
27+
reader = PdfReader(file_path)
28+
writer = PdfWriter()
29+
30+
for page in reader.pages:
31+
writer.add_page(page)
32+
33+
# Add a password to the new PDF
34+
# In this example, the password is hardcoded.
35+
# In a production application, don't hardcode passwords or other sensitive information.
36+
# We recommend you use AWS Secrets Manager to securely store passwords.
37+
writer.encrypt("my-secret-password")
38+
39+
# Save the new PDF to a file
40+
with open(encrypted_file_path, "wb") as file:
41+
writer.write(file)
42+
43+
# Define a function to add a suffix to the original filename after encryption
44+
def add_encrypted_suffix(original_key):
45+
filename, extension = original_key.rsplit('.', 1)
46+
return f'{filename}_encrypted.{extension}'
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[pytest]
2+
markers =
3+
order: specify test execution order
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
boto3
2+
pypdf
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
AWSTemplateFormatVersion: '2010-09-09'
2+
Transform: AWS::Serverless-2016-10-31
3+
4+
Resources:
5+
EncryptPDFFunction:
6+
Type: AWS::Serverless::Function
7+
Properties:
8+
FunctionName: EncryptPDF
9+
Architectures: [x86_64]
10+
CodeUri: ./
11+
Handler: lambda_function.lambda_handler
12+
Runtime: python3.12
13+
Timeout: 15
14+
MemorySize: 256
15+
LoggingConfig:
16+
LogFormat: JSON
17+
Policies:
18+
- AmazonS3FullAccess
19+
Events:
20+
S3Event:
21+
Type: S3
22+
Properties:
23+
Bucket: !Ref PDFSourceBucket
24+
Events: s3:ObjectCreated:*
25+
26+
PDFSourceBucket:
27+
Type: AWS::S3::Bucket
28+
Properties:
29+
BucketName: EXAMPLE-BUCKET
30+
31+
EncryptedPDFBucket:
32+
Type: AWS::S3::Bucket
33+
Properties:
34+
BucketName: EXAMPLE-BUCKET-encrypted
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import boto3
2+
import json
3+
import pytest
4+
import time
5+
import os
6+
7+
@pytest.fixture
8+
def lambda_client():
9+
return boto3.client('lambda')
10+
11+
@pytest.fixture
12+
def s3_client():
13+
return boto3.client('s3')
14+
15+
@pytest.fixture
16+
def logs_client():
17+
return boto3.client('logs')
18+
19+
@pytest.fixture(scope='session')
20+
def cleanup():
21+
# Create a new S3 client for cleanup
22+
s3_client = boto3.client('s3')
23+
24+
yield
25+
# Cleanup code will be executed after all tests have finished
26+
27+
# Delete test.pdf from the source bucket
28+
source_bucket = 'EXAMPLE-BUCKET'
29+
source_file_key = 'test.pdf'
30+
s3_client.delete_object(Bucket=source_bucket, Key=source_file_key)
31+
print(f"\nDeleted {source_file_key} from {source_bucket}")
32+
33+
# Delete test_encrypted.pdf from the destination bucket
34+
destination_bucket = 'EXAMPLE-BUCKET-encrypted'
35+
destination_file_key = 'test_encrypted.pdf'
36+
s3_client.delete_object(Bucket=destination_bucket, Key=destination_file_key)
37+
print(f"Deleted {destination_file_key} from {destination_bucket}")
38+
39+
40+
@pytest.mark.order(1)
41+
def test_source_bucket_available(s3_client):
42+
s3_bucket_name = 'EXAMPLE-BUCKET'
43+
file_name = 'test.pdf'
44+
file_path = os.path.join(os.path.dirname(__file__), file_name)
45+
46+
file_uploaded = False
47+
try:
48+
s3_client.upload_file(file_path, s3_bucket_name, file_name)
49+
file_uploaded = True
50+
except:
51+
print("Error: couldn't upload file")
52+
53+
assert file_uploaded, "Could not upload file to S3 bucket"
54+
55+
56+
57+
@pytest.mark.order(2)
58+
def test_lambda_invoked(logs_client):
59+
60+
# Wait for a few seconds to make sure the logs are available
61+
time.sleep(5)
62+
63+
# Get the latest log stream for the specified log group
64+
log_streams = logs_client.describe_log_streams(
65+
logGroupName='/aws/lambda/EncryptPDF',
66+
orderBy='LastEventTime',
67+
descending=True,
68+
limit=1
69+
)
70+
71+
latest_log_stream_name = log_streams['logStreams'][0]['logStreamName']
72+
73+
# Retrieve the log events from the latest log stream
74+
log_events = logs_client.get_log_events(
75+
logGroupName='/aws/lambda/EncryptPDF',
76+
logStreamName=latest_log_stream_name
77+
)
78+
79+
success_found = False
80+
for event in log_events['events']:
81+
message = json.loads(event['message'])
82+
status = message.get('record', {}).get('status')
83+
if status == 'success':
84+
success_found = True
85+
break
86+
87+
assert success_found, "Lambda function execution did not report 'success' status in logs."
88+
89+
@pytest.mark.order(3)
90+
def test_encrypted_file_in_bucket(s3_client):
91+
# Specify the destination S3 bucket and the expected converted file key
92+
destination_bucket = 'EXAMPLE-BUCKET-encrypted'
93+
converted_file_key = 'test_encrypted.pdf'
94+
95+
try:
96+
# Attempt to retrieve the metadata of the converted file from the destination S3 bucket
97+
s3_client.head_object(Bucket=destination_bucket, Key=converted_file_key)
98+
except s3_client.exceptions.ClientError as e:
99+
# If the file is not found, the test will fail
100+
pytest.fail(f"Converted file '{converted_file_key}' not found in the destination bucket: {str(e)}")
101+
102+
def test_cleanup(cleanup):
103+
# This test uses the cleanup fixture and will be executed last
104+
pass

0 commit comments

Comments
 (0)
Please sign in to comment.