fastapi-aws 0.0.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,332 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi-aws
3
+ Version: 0.0.7
4
+ Summary: AWS Integrations for FastAPI exported OpenAPI specifications
5
+ Home-page: https://github.com/bayinfosys/fastapi-aws
6
+ Author: Edward Grundy
7
+ Author-email: ed@bayis.co.uk
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.6
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: fastapi
14
+ Requires-Dist: httpx
15
+ Requires-Dist: uvicorn[standard]
16
+ Dynamic: author
17
+ Dynamic: author-email
18
+ Dynamic: classifier
19
+ Dynamic: description
20
+ Dynamic: description-content-type
21
+ Dynamic: home-page
22
+ Dynamic: requires-dist
23
+ Dynamic: requires-python
24
+ Dynamic: summary
25
+
26
+ # FastAPI AWS ApiGateway Integration
27
+
28
+ This extension enables routes to be defined in FastAPI with extra decorator parameters for AWS integrations.
29
+
30
+ The exported `private_openapi.json` will then contain integrations for:
31
+ + `AWS Lambda function`
32
+ + `AWS Step function`
33
+ + `AWS S3 bucket`
34
+
35
+ Secured via:
36
+ + `AWS Cognito User Pools`
37
+ + `AWS Lambda Authorizer`
38
+
39
+ This specification can be uploaded to AWS APIGateway as a definition for a REST API (see below for automatation with terraform). CORS definitions for routes are also auto-generated.
40
+
41
+ This allows your AWS APIGateway REST API to be defined in python, reference pydantic models, integrated into your application code, etc.
42
+
43
+ A `public_openapi.json` is also produced which can be presented to swagger and other tools for use in documentation.
44
+
45
+
46
+ ## Usage
47
+
48
+ ### Endpoints
49
+
50
+ Import the `AWSAPIRouter` which uses the default `AWSAPIRoute` to define routes.
51
+ Simply use one of the following integration keywords to define the `openapi_extra` contents:
52
+ + `aws_lambda_uri` trigger a lambda function
53
+ + `aws_sfn_sync_arn` invoke a step-function synchronously
54
+ + `aws_s3_bucket` s3 object access from apigw
55
+ + `mock_response` fixed apigw responses (TBD)
56
+ + `dynamodb` dynamodb queries via apigw endpoints (TBD)
57
+
58
+ The keyword-argument is used to define the format of the `x-amazon-apigateway-integration` added to the `openapi_extra` parameter.
59
+
60
+ **NOTE**: the default `FastAPI.router` should be replaced with a new `AWSAPIRouter` **before** adding any routes. If routes are added to the router, and the `app.include_router(aws_router)` is called, the `app.include_router` method will fail to pass the aws kwargs and cause the `AWSAPIRoute` to throw a `ValueError`. This is due to FastAPI sanitizing the kwargs to routes.
61
+
62
+
63
+ ```python
64
+ from fastapi import FastAPI, Depends
65
+ from pydantic import BaseModel
66
+ from fastapi.security import HTTPBearer
67
+
68
+ from fastapi_aws import AWSAPIRouter
69
+
70
+ # Instantiate the custom router
71
+ router = AWSAPIRouter()
72
+
73
+ # Create the FastAPI app and include the router
74
+ # NB: the router must be overridden **before** creating any routes
75
+ app = FastAPI()
76
+ app.router = router
77
+ # app.include_router(router) will cause the @router.get decorator to fail to add routes correctly
78
+
79
+ # Use the custom router to define routes so we can use the .get, .post, etc methods
80
+ @router.get(
81
+ "/user/{name}",
82
+ aws_lambda_uri="${user_function_arn}",
83
+ description="Get a user profile information",
84
+ summary="Username",
85
+ tags=["user"],
86
+ )
87
+ async def get_user_profile_information(name: str):
88
+ return {"status": "ok", "name": "hello, world"}
89
+
90
+ @router.post(
91
+ "/user/{name}",
92
+ aws_lambda_uri="${user_function_arn}",
93
+ description="Set a user profile name",
94
+ summary="Username",
95
+ tags=["user"],
96
+ )
97
+ async def set_user_profile(name: str):
98
+ return {"status": "ok"}
99
+
100
+ @router.post(
101
+ "/user-step-function",
102
+ aws_sfn_sync_arn="${step_function_arn}",
103
+ description="trigger a sync step function from this endpoint",
104
+ summary="operation",
105
+ tags=["user", "operation"]
106
+ )
107
+ async def user_post_data(user_info: UserInfoModel):
108
+ return "goodbye"
109
+ ```
110
+
111
+ ### Authorization
112
+
113
+ `fastapi_aws.authorizers` exports a number of authorizers which can be added to endpoints for security.
114
+
115
+ These classes will also export the correct AWS integrations to build and associate authorizers with API Gateway endpoints.
116
+
117
+ + `CognitoAuthorizer` enables AWS Cognito user pool authorization,
118
+ + `LambdaAuthorizer` enables custom AWS Lambda function authorizers.
119
+
120
+ Both classes export the authorizers in the openapi spec, so an authorizer should **NOT** be provisioned in infrastructure.
121
+
122
+ To add custom header fields for the authorizers use the `header_names` parameter.
123
+
124
+ ```python
125
+ from fastapi import Security
126
+ from fastapi_aws import AWSAPIRouter, LambdaAuthorizer
127
+
128
+ bearer_auth = LambdaAuthorizer(
129
+ authorizer_name="${bearer_authorizer_name}",
130
+ aws_lambda_uri="${lambda_authorizer_uri}",
131
+ aws_iam_role_arn="${lambda_authorizer_iam_role_arn}",
132
+ header_names=["Authorization", "x-api-key"]
133
+ )
134
+
135
+ router = AWSAPIRouter()
136
+
137
+ @router.get(
138
+ "/user/{name}",
139
+ aws_lambda_uri="${user_function_arn}",
140
+ description="Get a user profile information",
141
+ summary="Username",
142
+ tags=["user"],
143
+ )
144
+ async def get_user_profile_information(name: str, username=Security(bearer_auth)):
145
+ return {"status": "ok", "name": "hello, world"}
146
+ ```
147
+
148
+ In the above example, the `/user/{name}` endpoint will only succeed if the correct credentials are provided in either the `Authorization` or `x-api-key` header fields.
149
+
150
+ **NB**: the assignment `user=Security...` is required to ensure export to OpenAPI.
151
+
152
+ ## Export OpenAPI specification
153
+
154
+ The module will create an OpenAPI spec from a routes file, and output two specifications:
155
+ 1. A private OpenAPI spec with the (optional) CORS definitions and `x-amazon-apigateway-integration` definitions for consumption by AWS ApiGateway REST API, and
156
+ 2. A public OpenAPI spec which can be presented for public consumption via swagger documentation etc.
157
+
158
+ NB: the python code for these endpoints will not be executed in anyway, they are purely descriptive.
159
+
160
+ Given a `my-routes.py`:
161
+ ```python
162
+ from pydantic import BaseModel
163
+ from fastapi import Security
164
+
165
+ from fastapi_aws import AWSAPIRouter, LambdaAuthorizer
166
+
167
+
168
+ class UserRequest(BaseModel):
169
+ username: str
170
+
171
+
172
+ class UserResponse(BaseModel):
173
+ username: str
174
+
175
+
176
+ class UserPublicResponse(BaseModel):
177
+ username: str
178
+
179
+
180
+ lambda_auth = LambdaAuthorizer(
181
+ authorizer_name="${lambda_authorizer_name}",
182
+ aws_lambda_uri="${lambda_authorizer_uri}",
183
+ aws_iam_role_arn="${lambda_authorizer_iam_role_arn}",
184
+ )
185
+
186
+ router = AWSAPIRouter()
187
+
188
+ @router.post(
189
+ "/user",
190
+ description="post some user information",
191
+ response_model=UserResponse,
192
+ aws_lambda_uri="${polling_start_lambda_arn}",
193
+ aws_iam_arn="${polling_start_lambda_iam_role_arn}",
194
+ tags=["users"],
195
+ )
196
+ async def user(body: UserRequest, user=Security(lambda_auth)):
197
+ return UserResponse()
198
+
199
+
200
+ @router.get(
201
+ "/public/user",
202
+ description="retrieve public user information",
203
+ response_model=UserPublicResponse,
204
+ aws_lambda_uri="${user_public_data_lambda_arn}",
205
+ aws_iam_arn="${user_public_data_lambda_iam_role_arn}",
206
+ tags=["users"],
207
+ )
208
+ async def fetch_user_info():
209
+ return UserPublicResponse()
210
+ ```
211
+
212
+
213
+ These specifications are created from the module via:
214
+ ```bash
215
+ fastapi_aws \
216
+ --title my-api \
217
+ --router my-routes.py \
218
+ --out-public ./api_public.json \
219
+ --out-private ./api_private.json \
220
+ --version 0.0.1
221
+ ```
222
+
223
+ or via the docker image with:
224
+ ```bash
225
+ docker run -it --rm \
226
+ -v $(shell pwd)/my-api:/app/routes:ro \
227
+ -v $(shell pwd)/terraform/rest-definition:/out \
228
+ fastapi_aws \
229
+ --title my-api \
230
+ --router routes.routes:router \
231
+ --out-public /out/api_public_definition.json \
232
+ --out-private /out/api_private_definition.json \
233
+ --version 0.0.1
234
+ ```
235
+
236
+
237
+ ## Terraform integration
238
+
239
+ The `openapi.json` can be used to define a `REST API` in `AWS ApiGateway`.
240
+
241
+ By setting the `aws_integration_uri` to a placeholder string compatible with terraform templates,
242
+ the exported `openapi.json` file can be loaded with `templatefile`.
243
+ The template can then substitute the placeholder string with the actual lambda function arns from the terraform resources.
244
+
245
+ ```terraform
246
+ # Define a lambda function which we want to invoke from the API endpoint
247
+ resource "aws_lambda_function" "user_function" {
248
+ # ... Lambda configuration ...
249
+ function_name = "user-function"
250
+ # Other configurations
251
+ }
252
+
253
+ # Create an IAM role for API Gateway to invoke Lambda functions
254
+ resource "aws_iam_role" "api_gateway_role" {
255
+ name = "api-gateway-lambda-invoke-role"
256
+
257
+ assume_role_policy = jsonencode({
258
+ "Version": "2012-10-17",
259
+ "Statement": [{
260
+ "Action": "sts:AssumeRole",
261
+ "Effect": "Allow",
262
+ "Principal": {
263
+ "Service": "apigateway.amazonaws.com"
264
+ }
265
+ }]
266
+ })
267
+ }
268
+
269
+ # Attach policy to allow invocation of Lambda functions
270
+ resource "aws_iam_role_policy" "api_gateway_policy" {
271
+ name = "api-gateway-lambda-invoke-policy"
272
+ role = aws_iam_role.api_gateway_role.id
273
+
274
+ policy = jsonencode({
275
+ "Version": "2012-10-17",
276
+ "Statement": [{
277
+ "Action": "lambda:InvokeFunction",
278
+ "Effect": "Allow",
279
+ "Resource": aws_lambda_function.user_function.arn
280
+ }]
281
+ })
282
+ }
283
+
284
+ # Load the OpenAPI specification and replace placeholders
285
+ data "template_file" "openapi_spec" {
286
+ template = file("${path.module}/openapi.json")
287
+
288
+ vars = {
289
+ user_function_arn = aws_lambda_function.user_function.arn
290
+ lambda_invoke_iam_role_arn = aws_iam_role.api_gateway_role.arn
291
+ }
292
+ }
293
+
294
+ # Create the API Gateway REST API using the OpenAPI specification
295
+ resource "aws_api_gateway_rest_api" "api" {
296
+ name = "My API"
297
+
298
+ body = data.template_file.openapi_spec.rendered
299
+ }
300
+
301
+ # Deploy the API
302
+ resource "aws_api_gateway_deployment" "api_deployment" {
303
+ depends_on = [aws_api_gateway_rest_api.api]
304
+ rest_api_id = aws_api_gateway_rest_api.api.id
305
+ stage_name = "prod"
306
+ }
307
+ ```
308
+
309
+ ## Example
310
+
311
+ The following `Makefile` produces the openapi specs during a CICD process with:
312
+
313
+ ```makefile
314
+ APP_NAME=Example-App
315
+ GIT_TAG=$(shell git describe --tags)
316
+
317
+ build/api-definition:
318
+ docker run -it --rm \
319
+ -v $(shell pwd)/package/src/api:/app/routes:ro \
320
+ -v $(shell pwd)/terraform/rest:/out \
321
+ fastapi_aws \
322
+ --title $(APP_NAME) \
323
+ --router routes.routes:router \
324
+ --out-public /out/api_public_definition.json \
325
+ --out-private /out/api_private_definition.json \
326
+ --version $(GIT_TAG)
327
+
328
+ build/infrastructure:
329
+ terraform init
330
+ terraform plan -out new.plan
331
+ terraform apply && rm new.plan
332
+ ```
@@ -0,0 +1,12 @@
1
+ fastapi_aws/__init__.py,sha256=EAi65IfuiGJh9HE-Q-1uutBRmNKRnLzTkesdh9bs6s8,293
2
+ fastapi_aws/__main__.py,sha256=6eY2-rIqV69zPvNCOpS60PW0NGDoNQ8mktZ1koeZKzs,11785
3
+ fastapi_aws/_version.py,sha256=Fi0KXFzRbmtJaZ31ANCJStB858SeU6K67Widq0qwDIs,511
4
+ fastapi_aws/authorizers.py,sha256=zIFIB5P36bAPkMmzLplaFT-_sgEL4oBzs3dKjTdPIQ4,5874
5
+ fastapi_aws/integrations.py,sha256=b-zYoXOx-ep8AJJdGPdWLoGXErugtpLJzO-h-S2EIUE,10126
6
+ fastapi_aws/models.py,sha256=Ryy1OG2efHp31VQ9ucNw8Rwc2wXZS9VCjhFxYkhY2sk,1657
7
+ fastapi_aws/route.py,sha256=Jbx_E6Q5jp4IV4yjFlRxa3-0dIhWGtCstbK9ZyVouEI,8291
8
+ fastapi_aws/router.py,sha256=TuUkquFHLcZYqzwqjo_-docKCgtr1Rq6raipgfQf8pA,1538
9
+ fastapi_aws-0.0.7.dist-info/METADATA,sha256=LYMJzQHaALQrMEO7eqd_w79l8ScHOKsQTpB3Rw-pe2I,10288
10
+ fastapi_aws-0.0.7.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
11
+ fastapi_aws-0.0.7.dist-info/top_level.txt,sha256=920A6G6vs74mfgdQlmJ_wRgI55iup7aIdENDeSmL0M8,12
12
+ fastapi_aws-0.0.7.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (77.0.3)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ fastapi_aws