1- #
1+ #
22# Copyright 2024 ABSA Group Limited
3- #
3+ #
44# Licensed under the Apache License, Version 2.0 (the "License");
55# you may not use this file except in compliance with the License.
66# You may obtain a copy of the License at
7- #
7+ #
88# http://www.apache.org/licenses/LICENSE-2.0
9- #
9+ #
1010# Unless required by applicable law or agreed to in writing, software
1111# distributed under the License is distributed on an "AS IS" BASIS,
1212# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313# See the License for the specific language governing permissions and
1414# limitations under the License.
15- #
15+ #
1616import base64
1717import json
1818import logging
2828from jsonschema .exceptions import ValidationError
2929
3030# Resolve project root (parent directory of this file's directory)
31- _PROJECT_ROOT = os .path .abspath (os .path .join (os .path .dirname (__file__ ), '..' ))
32- _CONF_DIR = os .path .join (_PROJECT_ROOT , ' conf' )
31+ _PROJECT_ROOT = os .path .abspath (os .path .join (os .path .dirname (__file__ ), ".." ))
32+ _CONF_DIR = os .path .join (_PROJECT_ROOT , " conf" )
3333
3434sys .path .append (os .path .join (os .path .dirname (__file__ )))
3535
4545logger .addHandler (logging .StreamHandler ())
4646logger .debug ("Initialized LOGGER" )
4747
48- with open (os .path .join (_CONF_DIR , ' api.yaml' ), 'r' ) as file :
48+ with open (os .path .join (_CONF_DIR , " api.yaml" ), "r" ) as file :
4949 API = file .read ()
5050logger .debug ("Loaded API definition" )
5151
5252TOPICS = {}
53- with open (os .path .join (_CONF_DIR , ' topic_runs.json' ), 'r' ) as file :
53+ with open (os .path .join (_CONF_DIR , " topic_runs.json" ), "r" ) as file :
5454 TOPICS ["public.cps.za.runs" ] = json .load (file )
55- with open (os .path .join (_CONF_DIR , ' topic_dlchange.json' ), 'r' ) as file :
55+ with open (os .path .join (_CONF_DIR , " topic_dlchange.json" ), "r" ) as file :
5656 TOPICS ["public.cps.za.dlchange" ] = json .load (file )
57- with open (os .path .join (_CONF_DIR , ' topic_test.json' ), 'r' ) as file :
57+ with open (os .path .join (_CONF_DIR , " topic_test.json" ), "r" ) as file :
5858 TOPICS ["public.cps.za.test" ] = json .load (file )
5959logger .debug ("Loaded TOPICS" )
6060
61- with open (os .path .join (_CONF_DIR , ' config.json' ), 'r' ) as file :
61+ with open (os .path .join (_CONF_DIR , " config.json" ), "r" ) as file :
6262 CONFIG = json .load (file )
6363logger .debug ("Loaded main CONFIG" )
6464
65- aws_s3 = boto3 .Session ().resource ('s3' , verify = False )
65+ aws_s3 = boto3 .Session ().resource ("s3" , verify = False )
6666logger .debug ("Initialized AWS S3 Client" )
6767
6868if CONFIG ["access_config" ].startswith ("s3://" ):
69- name_parts = CONFIG ["access_config" ].split ('/' )
69+ name_parts = CONFIG ["access_config" ].split ("/" )
7070 bucket_name = name_parts [2 ]
7171 bucket_object = "/" .join (name_parts [3 :])
7272 ACCESS = json .loads (aws_s3 .Bucket (bucket_name ).Object (bucket_object ).get ()["Body" ].read ().decode ("utf-8" ))
8484writer_kafka .init (logger , CONFIG )
8585writer_postgres .init (logger )
8686
87+
8788def _error_response (status , err_type , message ):
8889 return {
8990 "statusCode" : status ,
9091 "headers" : {"Content-Type" : "application/json" },
91- "body" : json .dumps ({
92- "success" : False ,
93- "statusCode" : status ,
94- "errors" : [{"type" : err_type , "message" : message }]
95- })
92+ "body" : json .dumps (
93+ {"success" : False , "statusCode" : status , "errors" : [{"type" : err_type , "message" : message }]}
94+ ),
9695 }
9796
97+
9898def get_api ():
99- return {
100- "statusCode" : 200 ,
101- "body" : API
102- }
99+ return {"statusCode" : 200 , "body" : API }
100+
103101
104102def get_token ():
105103 logger .debug ("Handling GET Token" )
106- return {
107- "statusCode" : 303 ,
108- "headers" : {"Location" : TOKEN_PROVIDER_URL }
109- }
110-
104+ return {"statusCode" : 303 , "headers" : {"Location" : TOKEN_PROVIDER_URL }}
105+
106+
111107def get_topics ():
112108 logger .debug ("Handling GET Topics" )
113109 return {
114110 "statusCode" : 200 ,
115111 "headers" : {"Content-Type" : "application/json" },
116- "body" : json .dumps ([topicName for topicName in TOPICS ])
112+ "body" : json .dumps ([topicName for topicName in TOPICS ]),
117113 }
118-
114+
115+
119116def get_topic_schema (topicName ):
120117 logger .debug (f"Handling GET TopicSchema({ topicName } )" )
121118 if topicName not in TOPICS :
122119 return _error_response (404 , "topic" , f"Topic '{ topicName } ' not found" )
123-
124- return {
125- "statusCode" : 200 ,
126- "headers" : {"Content-Type" : "application/json" },
127- "body" : json .dumps (TOPICS [topicName ])
128- }
120+
121+ return {"statusCode" : 200 , "headers" : {"Content-Type" : "application/json" }, "body" : json .dumps (TOPICS [topicName ])}
122+
129123
130124def post_topic_message (topicName , topicMessage , tokenEncoded ):
131125 logger .debug (f"Handling POST { topicName } " )
132126 try :
133127 token = jwt .decode (tokenEncoded , TOKEN_PUBLIC_KEY , algorithms = ["RS256" ])
134128 except Exception :
135- return _error_response (401 , "auth" , "Invalid or missing token" )
129+ return _error_response (401 , "auth" , "Invalid or missing token" )
136130
137131 if topicName not in TOPICS :
138132 return _error_response (404 , "topic" , f"Topic '{ topicName } ' not found" )
@@ -144,8 +138,8 @@ def post_topic_message(topicName, topicMessage, tokenEncoded):
144138 try :
145139 validate (instance = topicMessage , schema = TOPICS [topicName ])
146140 except ValidationError as e :
147- return _error_response (400 , "validation" , e .message )
148-
141+ return _error_response (400 , "validation" , e .message )
142+
149143 # Run all writers independently (avoid short-circuit so failures in one don't skip others)
150144 kafka_ok , kafka_err = writer_kafka .write (topicName , topicMessage )
151145 eventbridge_ok , eventbridge_err = writer_eventbridge .write (topicName , topicMessage )
@@ -163,31 +157,26 @@ def post_topic_message(topicName, topicMessage, tokenEncoded):
163157 return {
164158 "statusCode" : 500 ,
165159 "headers" : {"Content-Type" : "application/json" },
166- "body" : json .dumps ({
167- "success" : False ,
168- "statusCode" : 500 ,
169- "errors" : errors
170- })
160+ "body" : json .dumps ({"success" : False , "statusCode" : 500 , "errors" : errors }),
171161 }
172162
173163 return {
174164 "statusCode" : 202 ,
175165 "headers" : {"Content-Type" : "application/json" },
176- "body" : json .dumps ({
177- "success" : True ,
178- "statusCode" : 202
179- })
166+ "body" : json .dumps ({"success" : True , "statusCode" : 202 }),
180167 }
181168
169+
182170def extract_token (eventHeaders ):
183171 # Initial implementation used bearer header directly
184172 if "bearer" in eventHeaders :
185173 return eventHeaders ["bearer" ]
186-
174+
187175 if "Authorization" in eventHeaders and eventHeaders ["Authorization" ].startswith ("Bearer " ):
188- return eventHeaders ["Authorization" ][len ("Bearer " ):]
189-
190- return "" # Will result in 401
176+ return eventHeaders ["Authorization" ][len ("Bearer " ) :]
177+
178+ return "" # Will result in 401
179+
191180
192181def lambda_handler (event , context ):
193182 try :
@@ -201,7 +190,11 @@ def lambda_handler(event, context):
201190 if event ["httpMethod" ] == "GET" :
202191 return get_topic_schema (event ["pathParameters" ]["topic_name" ].lower ())
203192 if event ["httpMethod" ] == "POST" :
204- return post_topic_message (event ["pathParameters" ]["topic_name" ].lower (), json .loads (event ["body" ]), extract_token (event ["headers" ]))
193+ return post_topic_message (
194+ event ["pathParameters" ]["topic_name" ].lower (),
195+ json .loads (event ["body" ]),
196+ extract_token (event ["headers" ]),
197+ )
205198 if event ["resource" ].lower () == "/terminate" :
206199 sys .exit ("TERMINATING" )
207200 return _error_response (404 , "route" , "Resource not found" )
0 commit comments