@@ -62,6 +62,11 @@ def boto3_session() -> Session:
6262 return Session (aws_access_key_id = "fake" , aws_secret_access_key = "fake" , region_name = AWS_REGION )
6363
6464
65+ @pytest .fixture (scope = "session" )
66+ def api_gateway_client (boto3_session : Session , localstack : URL ) -> BaseClient :
67+ return boto3_session .client ("apigateway" , endpoint_url = str (localstack ))
68+
69+
6570@pytest .fixture (scope = "session" )
6671def lambda_client (boto3_session : Session , localstack : URL ) -> BaseClient :
6772 return boto3_session .client ("lambda" , endpoint_url = str (localstack ))
@@ -123,18 +128,42 @@ def iam_role(iam_client: BaseClient) -> Generator[str]:
123128 }
124129 ],
125130 }
131+ dynamodb_policy = {
132+ "Version" : "2012-10-17" ,
133+ "Statement" : [
134+ {
135+ "Effect" : "Allow" ,
136+ "Action" : [
137+ "dynamodb:GetItem" ,
138+ "dynamodb:PutItem" ,
139+ "dynamodb:UpdateItem" ,
140+ "dynamodb:DeleteItem" ,
141+ "dynamodb:Scan" ,
142+ "dynamodb:Query" ,
143+ ],
144+ "Resource" : "arn:aws:dynamodb:*:*:table/*" ,
145+ }
146+ ],
147+ }
126148
127- # Create the IAM Policy
128- policy = iam_client .create_policy (PolicyName = policy_name , PolicyDocument = json .dumps (log_policy ))
129- policy_arn = policy ["Policy" ]["Arn" ]
149+ # Create CloudWatch Logs policy (as before)
150+ log_policy_resp = iam_client .create_policy (PolicyName = policy_name , PolicyDocument = json .dumps (log_policy ))
151+ log_policy_arn = log_policy_resp ["Policy" ]["Arn" ]
152+ iam_client .attach_role_policy (RoleName = role_name , PolicyArn = log_policy_arn )
130153
131- # Attach Policy to Role
132- iam_client .attach_role_policy (RoleName = role_name , PolicyArn = policy_arn )
154+ # Create DynamoDB policy
155+ ddb_policy_resp = iam_client .create_policy (
156+ PolicyName = "LambdaDynamoDBPolicy" , PolicyDocument = json .dumps (dynamodb_policy )
157+ )
158+ ddb_policy_arn = ddb_policy_resp ["Policy" ]["Arn" ]
159+ iam_client .attach_role_policy (RoleName = role_name , PolicyArn = ddb_policy_arn )
133160
134161 yield role ["Role" ]["Arn" ]
135162
136- iam_client .detach_role_policy (RoleName = role_name , PolicyArn = policy_arn )
137- iam_client .delete_policy (PolicyArn = policy_arn )
163+ iam_client .detach_role_policy (RoleName = role_name , PolicyArn = log_policy_arn )
164+ iam_client .delete_policy (PolicyArn = log_policy_arn )
165+ iam_client .detach_role_policy (RoleName = role_name , PolicyArn = ddb_policy_arn )
166+ iam_client .delete_policy (PolicyArn = ddb_policy_arn )
138167 iam_client .delete_role (RoleName = role_name )
139168
140169
@@ -194,6 +223,71 @@ def wait_for_function_active(function_name, lambda_client):
194223 raise FunctionNotActiveError
195224
196225
226+ @pytest .fixture (scope = "session" )
227+ def configured_api_gateway (api_gateway_client , lambda_client , flask_function : str ):
228+ region = lambda_client .meta .region_name
229+
230+ api = api_gateway_client .create_rest_api (name = "API Gateway Lambda integration" )
231+ rest_api_id = api ["id" ]
232+
233+ resources = api_gateway_client .get_resources (restApiId = rest_api_id )
234+ root_id = next (item ["id" ] for item in resources ["items" ] if item ["path" ] == "/" )
235+
236+ patient_check_res = api_gateway_client .create_resource (
237+ restApiId = rest_api_id , parentId = root_id , pathPart = "patient-check"
238+ )
239+ patient_check_id = patient_check_res ["id" ]
240+
241+ id_res = api_gateway_client .create_resource (restApiId = rest_api_id , parentId = patient_check_id , pathPart = "{id}" )
242+ resource_id = id_res ["id" ]
243+
244+ api_gateway_client .put_method (
245+ restApiId = rest_api_id ,
246+ resourceId = resource_id ,
247+ httpMethod = "GET" ,
248+ authorizationType = "NONE" ,
249+ requestParameters = {"method.request.path.id" : True },
250+ )
251+
252+ # Integration with actual region
253+ lambda_uri = (
254+ f"arn:aws:apigateway:{ region } :lambda:path/2015-03-31/functions/"
255+ f"arn:aws:lambda:{ region } :000000000000:function:{ flask_function } /invocations"
256+ )
257+ api_gateway_client .put_integration (
258+ restApiId = rest_api_id ,
259+ resourceId = resource_id ,
260+ httpMethod = "GET" ,
261+ type = "AWS_PROXY" ,
262+ integrationHttpMethod = "POST" ,
263+ uri = lambda_uri ,
264+ passthroughBehavior = "WHEN_NO_MATCH" ,
265+ )
266+
267+ # Permission with matching region
268+ lambda_client .add_permission (
269+ FunctionName = flask_function ,
270+ StatementId = "apigateway-access" ,
271+ Action = "lambda:InvokeFunction" ,
272+ Principal = "apigateway.amazonaws.com" ,
273+ SourceArn = f"arn:aws:execute-api:{ region } :000000000000:{ rest_api_id } /*/GET/patient-check/*" ,
274+ )
275+
276+ # Deploy the API
277+ api_gateway_client .create_deployment (restApiId = rest_api_id , stageName = "dev" )
278+
279+ return {
280+ "rest_api_id" : rest_api_id ,
281+ "resource_id" : resource_id ,
282+ "invoke_url" : f"http://{ rest_api_id } .execute-api.localhost.localstack.cloud:4566/dev/patient-check/{{id}}" ,
283+ }
284+
285+
286+ @pytest .fixture
287+ def api_gateway_endpoint (configured_api_gateway : dict ) -> URL :
288+ return URL (f"http://{ configured_api_gateway ['rest_api_id' ]} .execute-api.localhost.localstack.cloud:4566/dev" )
289+
290+
197291@pytest .fixture (scope = "session" )
198292def person_table (dynamodb_resource : ServiceResource ) -> Generator [Any ]:
199293 table = dynamodb_resource .create_table (
0 commit comments