-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
I-ALiRT: Add query api to s3 bucket (#398)
* I-ALiRT: Add query api to s3 bucket
- Loading branch information
1 parent
1b7ae8b
commit c46c59c
Showing
5 changed files
with
265 additions
and
2 deletions.
There are no files selected for viewing
86 changes: 86 additions & 0 deletions
86
sds_data_manager/constructs/ialirt_api_manager_construct.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
"""Configure the I-ALiRT API Manager.""" | ||
|
||
import aws_cdk as cdk | ||
from aws_cdk import aws_iam as iam | ||
from aws_cdk import aws_lambda as lambda_ | ||
from constructs import Construct | ||
|
||
from .api_gateway_construct import ApiGateway | ||
|
||
|
||
class IalirtApiManager(Construct): | ||
"""Construct for API Management.""" | ||
|
||
def __init__( | ||
self, | ||
scope: Construct, | ||
construct_id: str, | ||
code: lambda_.Code, | ||
api: ApiGateway, | ||
env: cdk.Environment, | ||
data_bucket, | ||
vpc, | ||
layers: list, | ||
**kwargs, | ||
) -> None: | ||
"""Initialize the SdsApiManagerConstruct. | ||
Parameters | ||
---------- | ||
scope : obj | ||
Parent construct | ||
construct_id : str | ||
A unique string identifier for this construct | ||
code : lambda_.Code | ||
Lambda code bundle | ||
api : obj | ||
The APIGateway stack | ||
env : obj | ||
The CDK environment | ||
data_bucket : obj | ||
The data bucket | ||
vpc : obj | ||
The VPC | ||
layers : list | ||
List of Lambda layers arns | ||
kwargs : dict | ||
Keyword arguments | ||
""" | ||
super().__init__(scope, construct_id, **kwargs) | ||
|
||
s3_read_policy = iam.PolicyStatement( | ||
effect=iam.Effect.ALLOW, | ||
actions=["s3:ListBucket", "s3:GetObject"], | ||
resources=[ | ||
data_bucket.bucket_arn, | ||
f"{data_bucket.bucket_arn}/*", | ||
], | ||
) | ||
|
||
# query API lambda | ||
query_api_lambda = lambda_.Function( | ||
self, | ||
id="IAlirtCodeQueryAPILambda", | ||
function_name="query-api-handler", | ||
code=code, | ||
handler="IAlirtCode.ialirt_query_api.lambda_handler", | ||
runtime=lambda_.Runtime.PYTHON_3_12, | ||
timeout=cdk.Duration.minutes(1), | ||
memory_size=1000, | ||
allow_public_subnet=True, | ||
vpc=vpc, | ||
environment={ | ||
"S3_BUCKET": data_bucket.bucket_name, | ||
"REGION": env.region, | ||
}, | ||
layers=layers, | ||
architecture=lambda_.Architecture.ARM_64, | ||
) | ||
|
||
query_api_lambda.add_to_role_policy(s3_read_policy) | ||
|
||
api.add_route( | ||
route="ialirt-log-query", | ||
http_method="GET", | ||
lambda_function=query_api_lambda, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
sds_data_manager/lambda_code/IAlirtCode/ialirt_query_api.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
"""Define lambda to support the download API.""" | ||
|
||
import json | ||
import logging | ||
import os | ||
from datetime import datetime | ||
|
||
import boto3 | ||
import botocore | ||
|
||
# Logger setup | ||
logger = logging.getLogger(__name__) | ||
logger.setLevel(logging.INFO) | ||
|
||
|
||
def lambda_handler(event, context): | ||
"""Entry point to the query API lambda. | ||
Parameters | ||
---------- | ||
event : dict | ||
The JSON formatted document with the data required for the | ||
lambda function to process | ||
context : LambdaContext | ||
This object provides methods and properties that provide | ||
information about the invocation, function, | ||
and runtime environment. | ||
Notes | ||
----- | ||
Based on filename flight_iois_X.log.YYYY-DOYTHH:MM:SS.ssssss.txt. | ||
This is the log file produced by IOIS for each instance. | ||
Example | ||
------- | ||
Below is an event example: | ||
{ | ||
"queryStringParameters": { | ||
"year": "2024", | ||
"doy": "141", | ||
"instance": "1" | ||
} | ||
} | ||
""" | ||
logger.info(f"Event: {event}") | ||
logger.info(f"Context: {context}") | ||
|
||
logger.info("Received event: " + json.dumps(event, indent=2)) | ||
|
||
query_params = event["queryStringParameters"] | ||
year = query_params.get("year") | ||
doy = query_params.get("doy") | ||
instance = query_params.get("instance") | ||
|
||
try: | ||
day = datetime.strptime(f"{year}{doy}", "%Y%j") | ||
except ValueError: | ||
return { | ||
"statusCode": 400, | ||
"headers": {"Content-Type": "application/json"}, | ||
"body": json.dumps( | ||
{"error": "Invalid year or day format. Use YYYY and DOY."} | ||
), | ||
} | ||
|
||
prefix = day.strftime(f"logs/flight_iois_{instance}.log.%Y-%j") | ||
|
||
bucket = os.getenv("S3_BUCKET") | ||
region = os.getenv("REGION") | ||
|
||
s3_client = boto3.client( | ||
"s3", | ||
region_name=region, | ||
config=botocore.client.Config(signature_version="s3v4"), | ||
) | ||
|
||
response = s3_client.list_objects_v2(Bucket=bucket, Prefix=prefix) | ||
files = [] | ||
|
||
for obj in response.get("Contents", []): | ||
filename = obj["Key"].split("/")[-1] | ||
files.append(filename) | ||
|
||
response = { | ||
"statusCode": 200, | ||
"headers": {"Content-Type": "application/json"}, | ||
"body": json.dumps({"files": files}), | ||
} | ||
|
||
return response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
"""Tests for the I-ALiRT Query API.""" | ||
|
||
import json | ||
|
||
from sds_data_manager.lambda_code.IAlirtCode import ialirt_query_api | ||
|
||
|
||
def test_query_within_date_range(s3_client): | ||
"""Test that the query API returns files within the specified date range.""" | ||
s3_client.create_bucket(Bucket="test-data-bucket") | ||
|
||
# Adding files within and outside of the desired date range | ||
s3_client.put_object( | ||
Bucket="test-data-bucket", | ||
Key="logs/flight_iois_1.log.2024-141T16-55-46_123456.txt", | ||
Body=b"test", | ||
) | ||
s3_client.put_object( | ||
Bucket="test-data-bucket", | ||
Key="logs/flight_iois_1.log.2024-141T16-54-46_123456.txt", | ||
Body=b"test", | ||
) | ||
s3_client.put_object( | ||
Bucket="test-data-bucket", | ||
Key="logs/flight_iois_1.log.2025-141T16-54-46_123456.txt", | ||
Body=b"test", | ||
) | ||
|
||
event = {"queryStringParameters": {"year": "2024", "doy": "141", "instance": "1"}} | ||
|
||
response = ialirt_query_api.lambda_handler(event=event, context=None) | ||
response_data = json.loads(response["body"]) | ||
|
||
assert response["statusCode"] == 200 | ||
assert response_data["files"] == [ | ||
"flight_iois_1.log.2024-141T16-54-46_123456.txt", | ||
"flight_iois_1.log.2024-141T16-55-46_123456.txt", | ||
] | ||
|
||
|
||
def test_invalid_date_format(): | ||
"""Test that an error is returned for invalid date formats.""" | ||
event = {"queryStringParameters": {"year": "invalid_date", "doy": "invalid_date"}} | ||
|
||
response = ialirt_query_api.lambda_handler(event=event, context=None) | ||
|
||
assert response["statusCode"] == 400 | ||
assert "Invalid year or day format. Use YYYY and DOY." in response["body"] |