Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update deps versions, endpoint methods and docs endpoint #10

Merged
merged 2 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cdk.context.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"main_resources_name": "fastapi-lambda",
"tags": {
"Owner": "Santiago Garcia Arango",
"Source": "https://github.com/san99tiago/aws-cdk-fastapi-lambda",
"Source": "https://github.com/san99tiago/aws-fastapi-lambda",
"Usage": "Sample project to illustrate a quick easy FastAPI deployment on Lambda Functions"
}
}
31 changes: 15 additions & 16 deletions cdk/stacks/cdk_lambda_fastapi_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,14 @@ def create_lambda_layers(self):
dependencies of the Lambda Functions.
"""

# Layer for "LambdaPowerTools" (for logging, traces, observability, etc)
self.lambda_layer_powertools = aws_lambda.LayerVersion.from_layer_version_arn(
self,
id="LambdaLayer-Powertools",
layer_version_arn=f"arn:aws:lambda:{self.region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:35",
)

# Layer for "FastAPI" and "Mangum" Adapter libraries
self.lambda_layer_fastapi = aws_lambda.LayerVersion(
self,
id="LambdaLayer-FastAPI",
code=aws_lambda.Code.from_asset("lambda-layers/fastapi/modules"),
compatible_runtimes=[
aws_lambda.Runtime.PYTHON_3_9,
aws_lambda.Runtime.PYTHON_3_10,
aws_lambda.Runtime.PYTHON_3_11,
aws_lambda.Runtime.PYTHON_3_12,
],
description="Lambda Layer for Python with <fastapi> library",
removal_policy=RemovalPolicy.DESTROY,
Expand All @@ -81,24 +74,23 @@ def create_lambda_functions(self):
self.lambda_fastapi: aws_lambda.Function = aws_lambda.Function(
self,
id="Lambda-FastAPI",
runtime=aws_lambda.Runtime.PYTHON_3_9,
runtime=aws_lambda.Runtime.PYTHON_3_12,
handler="api/main.handler",
code=aws_lambda.Code.from_asset(PATH_TO_LAMBDA_FUNCTION_FOLDER),
timeout=Duration.seconds(30),
timeout=Duration.seconds(20),
memory_size=128,
environment={
"ENVIRONMENT": self.deployment_environment,
"LOG_LEVEL": "DEBUG",
"STATE_MACHINE_ENABLED": "true",
},
layers=[
self.lambda_layer_powertools,
self.lambda_layer_fastapi,
],
)

# NOTE: If IAM-based based auth needed, update the "auth_type" to "AWS_IAM"
self.lambda_function_url = self.lambda_fastapi.add_function_url(
auth_type=aws_lambda.FunctionUrlAuthType.AWS_IAM,
auth_type=aws_lambda.FunctionUrlAuthType.NONE
)

def generate_cloudformation_outputs(self):
Expand All @@ -115,7 +107,14 @@ def generate_cloudformation_outputs(self):

CfnOutput(
self,
"LambdaFunctionUrl",
"LambdaFunctionRootUrl",
value=self.lambda_function_url.url,
description="Root URL to invoke Lambda Function",
)

CfnOutput(
self,
"LambdaFunctionDocsUrl",
value=self.lambda_function_url.url,
description="URL to invoke Lambda Function",
description="Documentation URL to invoke Lambda Function",
)
2 changes: 1 addition & 1 deletion lambda-layers/fastapi/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
all:
[ -d "modules/python" ] || pip install -r requirements.txt -t modules/python/
[ -d "modules/python" ] || pip install -r requirements.txt -t modules/python/ --platform manylinux2014_x86_64 --only-binary=:all:

clean:
rm -rf modules
2 changes: 1 addition & 1 deletion lambda-layers/fastapi/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
fastapi==0.98.0
fastapi==0.109.0
mangum==0.17.0
2,232 changes: 1,091 additions & 1,141 deletions poetry.lock

Large diffs are not rendered by default.

29 changes: 15 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
[tool.poetry]
name = "aws-cdk-fastapi-lambda"
version = "0.0.1"
name = "aws-fastapi-lambda"
version = "1.0.0"
description = "Sample project to illustrate a real quick FastAPI deployment on Lambda Functions"
authors = ["Santiago Garcia Arango <san99tiago@gmail.com>"]
license = "Apache"
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.9"
aws-cdk-lib = "2.83.1"
python = "^3.11"
aws-cdk-lib = "2.130.0"
constructs = ">=10.0.0,<11.0.0"
black = "^23.9.1"

[tool.poetry.group.dev.dependencies]
aws-lambda-powertools = {extras = ["all"], version = "^2.16.2"}
boto3 = "^1.26.153"
poethepoet = "^0.20.0"
pytest = "^7.3.2"
pytest-mock = "^3.11.1"
coverage = "^7.2.7"
moto = "^4.1.11"
fastapi = {extras = ["all"], version = "^0.98.0"}
poethepoet = "^0.24.0"
pytest = "^7.4.4"
pytest-mock = "^3.12.0"
coverage = "^7.4.0"
black = "^23.12.1"
boto3 = "^1.34.14"
moto = "^4.2.13"
aws-lambda-powertools = {version = "^2.31.0"}
fastapi = {extras = ["all"], version = "^0.109.0"}
mangum = "^0.17.0"
pydantic = "^2.5.3"

[tool.pytest.ini_options]
minversion = "6.0"
minversion = "7.0"
pythonpath = [
"cdk",
"src/lambdas",
Expand Down
28 changes: 23 additions & 5 deletions src/lambdas/api/main.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,41 @@
# Built-in imports
import os
from typing import Union

# External imports
from fastapi import FastAPI
from mangum import Mangum
from pydantic import BaseModel


app = FastAPI(
description="Simple FastAPI server that runs on top of Lambda Functions.",
contact={"Santiago Garcia Arango": "santiago.garcia1999@hotmail.com"},
contact={"Santiago Garcia Arango": "san99tiago@gmail.com"},
title="Simple FastAPI Example",
version="0.0.1",
)


class Item(BaseModel):
name: str
price: float
is_offer: Union[bool, None] = None


@app.get("/")
async def root():
return {"message": "Hello by Santi"}


@app.get("/status")
async def get_status():
return {"status": "OK"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}


@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}


handler = Mangum(app, lifespan="off")
# This is the Lambda Function's entrypoint (handler)
handler = Mangum(app)
35 changes: 32 additions & 3 deletions tests/unit/lambdas/api/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,36 @@ def test_read_root():
assert response.json() == {"message": "Hello by Santi"}


def test_read_status():
response = client.get("/status")
def test_read_status_no_query():
response = client.get("/items/123")
assert response.status_code == 200
assert response.json() == {"status": "OK"}
assert response.json() == {"item_id": 123, "q": None}


def test_read_status_with_query():
response = client.get("/items/456?q=Santi")
assert response.status_code == 200
assert response.json() == {"item_id": 456, "q": "Santi"}


def test_update_item_success():
response = client.put(
"/items/789",
json={
"name": "test name",
"price": 99.9,
},
)
assert response.status_code == 200
assert response.json() == {"item_name": "test name", "item_id": 789}


def test_update_item_wrong_payload():
response = client.put(
"/items/789",
json={
"name": "test name",
"amount": 99.9, # Intentionally wrong key (not in model)
},
)
assert response.status_code == 422
Loading