1+ import json
2+ from pathlib import Path
3+ from tty import CFLAG
4+
5+ import aws_cdk
6+ from aws_cdk import (
7+ # Duration,
8+ Stack ,
9+ aws_sqs as sqs ,
10+ aws_dynamodb as dynamodb ,
11+ aws_iam as iam ,
12+ aws_lambda as _lambda ,
13+ CfnOutput as Output
14+ )
15+ from constructs import Construct
16+
17+ class QuizAppStack (Stack ):
18+
19+ def __init__ (self , scope : Construct , construct_id : str , ** kwargs ) -> None :
20+ super ().__init__ (scope , construct_id , ** kwargs )
21+
22+ # TABLES
23+ quizzes_table = dynamodb .Table (
24+ self ,
25+ "QuizzesTable" ,
26+ table_name = "Quizzes" ,
27+ partition_key = dynamodb .Attribute (
28+ name = "QuizID" ,
29+ type = dynamodb .AttributeType .STRING ,
30+ ),
31+ billing_mode = dynamodb .BillingMode .PROVISIONED ,
32+ read_capacity = 5 ,
33+ write_capacity = 5 ,
34+ )
35+
36+ user_submissions_table = dynamodb .Table (
37+ self ,
38+ "UserSubmissionsTable" ,
39+ table_name = "UserSubmissions" ,
40+ partition_key = dynamodb .Attribute (
41+ name = "SubmissionID" ,
42+ type = dynamodb .AttributeType .STRING ,
43+ ),
44+ billing_mode = dynamodb .BillingMode .PROVISIONED ,
45+ read_capacity = 5 ,
46+ write_capacity = 5 ,
47+ )
48+ user_submissions_table .add_global_secondary_index (
49+ index_name = "QuizID-Score-index" ,
50+ partition_key = dynamodb .Attribute (
51+ name = "QuizID" ,
52+ type = dynamodb .AttributeType .STRING ,
53+ ),
54+ sort_key = dynamodb .Attribute (
55+ name = "Score" ,
56+ type = dynamodb .AttributeType .NUMBER ,
57+ ),
58+ projection_type = dynamodb .ProjectionType .ALL ,
59+ read_capacity = 5 ,
60+ write_capacity = 5 ,
61+ )
62+
63+ Output (self , "QuizTable" , value = quizzes_table .table_name )
64+ Output (self , "UserSubmissionsTable" , value = user_submissions_table .table_name )
65+
66+
67+ functions_and_roles = [
68+ ("CreateQuizFunction" ,"configurations/create_quiz_policy.json" ,"CreateQuizRole" , "lambdas/get_quiz/handler.py" ),
69+ ("GetQuizFunction" ,"configurations/get_quiz_policy.json" ,"GetQuizRole" , "lambdas/get_quiz/handler.py" ),
70+ ("SubmitQuizFunction" ,"configurations/submit_quiz_policy.json" , "SubmitQuizRole" , "lambdas/submit_quiz/handler.py" ),
71+ ("ScoringFunction" , "configurations/scoring_policy.json" , "ScoringRole" , "lambdas/scoring/handler.py" ),
72+ ("GetSubmissionFunction" , "configurations/get_submission_policy.json" , "GetSubmissionRole" , "lambdas/get_submission/handler.py" ),
73+ ("GetLeaderboardFunction" , "configurations/get_leaderboard_policy.json" , "GetLeaderboardRole" , "lambdas/get_leaderboard/handler.py" ),
74+ ("ListPublicQuizzesFunction" , "configurations/list_quizzes_policy.json" , "ListQuizzesRole" , "lambdas/list_quizzes/handler.py" ),
75+ ("RetryQuizzesWritesFunction" ,"configurations/retry_quizzes_writes_policy.json" , "RetryQuizzesWritesRole" , "lambdas/retry_quizzes_writes/handler.py" ),
76+ ]
77+
78+ for function_info in functions_and_roles :
79+
80+ function_name , policy_file_path , role_name , handler_path = function_info
81+ policy_json = self .read_policy_file (f"../{ policy_file_path } " )
82+ policy_document = iam .PolicyDocument .from_json (policy_json )
83+
84+ policy = iam .ManagedPolicy (
85+ self ,
86+ "FunctionPolicy" ,
87+ managed_policy_name = f"{ function_name } Policy" ,
88+ document = policy_document ,
89+ )
90+
91+ role = iam .Role (
92+ self ,
93+ "LambdaExecutionRole" ,
94+ role_name = role_name ,
95+ assumed_by = iam .ServicePrincipal ("lambda.amazonaws.com" ),
96+ description = f"Role for Lambda function { function_name } " ,
97+ )
98+
99+ # Attach the policy to the role
100+ role .add_managed_policy (policy )
101+
102+ _lambda .Function (
103+ self ,
104+ "LambdaFunction" ,
105+ function_name = function_name ,
106+ runtime = _lambda .Runtime .PYTHON_3_11 ,
107+ handler = "handler.lambda_handler" ,
108+ code = _lambda .Code .from_asset (handler_path ),
109+ role = role ,
110+ timeout = aws_cdk .Duration .seconds (30 ),
111+ )
112+
113+ sqs .Queue (scope , "QuizSubmissionQueue" , queue_name = "QuizSubmissionQueue" )
114+
115+ @staticmethod
116+ def read_policy_file (file_path : str ) -> dict :
117+ """Reads a JSON policy file and returns it as a dictionary."""
118+ file_path = Path (file_path )
119+ if not file_path .exists ():
120+ raise FileNotFoundError (f"Policy file not found: { file_path } " )
121+ with open (file_path , "r" ) as file :
122+ return json .load (file )
0 commit comments