-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
366 additions
and
10 deletions.
There are no files selected for viewing
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 |
---|---|---|
|
@@ -5,3 +5,4 @@ lib/**/*.d.ts | |
.jsii | ||
dist | ||
docs | ||
__pycache__ |
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 |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from "./bootstrapper"; | ||
export * from "./database"; | ||
export * from "./stac-api"; |
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,115 @@ | ||
import { | ||
Stack, | ||
aws_ec2 as ec2, | ||
aws_rds as rds, | ||
aws_lambda as lambda, | ||
aws_secretsmanager as secretsmanager, | ||
CfnOutput, | ||
} from "aws-cdk-lib"; | ||
import { | ||
PythonFunction, | ||
PythonFunctionProps, | ||
} from "@aws-cdk/aws-lambda-python-alpha"; | ||
import { HttpApi } from "@aws-cdk/aws-apigatewayv2-alpha"; | ||
import { HttpLambdaIntegration } from "@aws-cdk/aws-apigatewayv2-integrations-alpha"; | ||
import { Construct } from "constructs"; | ||
|
||
export class PgStacApiLambda extends Construct { | ||
constructor(scope: Construct, id: string, props: PgStacApiLambdaProps) { | ||
super(scope, id); | ||
|
||
const apiCode = props.apiCode || { | ||
entry: `${__dirname}/runtime`, | ||
index: "src/handler.py", | ||
handler: "handler", | ||
}; | ||
|
||
const handler = new PythonFunction(this, "stac-api", { | ||
...apiCode, | ||
/** | ||
* NOTE: Unable to use Py3.9, due to issues with hashes: | ||
* | ||
* ERROR: Hashes are required in --require-hashes mode, but they are missing | ||
* from some requirements. Here is a list of those requirements along with the | ||
* hashes their downloaded archives actually had. Add lines like these to your | ||
* requirements files to prevent tampering. (If you did not enable | ||
* --require-hashes manually, note that it turns on automatically when any | ||
* package has a hash.) | ||
* anyio==3.6.1 --hash=sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be | ||
* */ | ||
runtime: lambda.Runtime.PYTHON_3_8, | ||
architecture: lambda.Architecture.X86_64, | ||
environment: { | ||
PGSTAC_SECRET_ARN: props.dbSecret.secretArn, | ||
DB_MIN_CONN_SIZE: "0", | ||
DB_MAX_CONN_SIZE: "1", | ||
...props.apiEnv, | ||
}, | ||
vpc: props.vpc, | ||
vpcSubnets: props.subnetSelection, | ||
allowPublicSubnet: true, | ||
memorySize: 8192, | ||
}); | ||
|
||
props.dbSecret.grantRead(handler); | ||
handler.connections.allowTo(props.db, ec2.Port.tcp(5432)); | ||
|
||
const stacApi = new HttpApi(this, "api", { | ||
defaultIntegration: new HttpLambdaIntegration("integration", handler), | ||
}); | ||
|
||
new CfnOutput(this, "stac-api-output", { | ||
exportName: `${Stack.of(this).stackName}-url`, | ||
value: stacApi.url!, | ||
}); | ||
} | ||
} | ||
|
||
export interface PgStacApiLambdaProps { | ||
/** | ||
* VPC into which the lambda should be deployed. | ||
*/ | ||
readonly vpc: ec2.IVpc; | ||
|
||
/** | ||
* RDS Instance with installed pgSTAC. | ||
*/ | ||
readonly db: rds.IDatabaseInstance; | ||
|
||
/** | ||
* Subnet into which the lambda should be deployed. | ||
*/ | ||
readonly subnetSelection: ec2.SubnetSelection; | ||
|
||
/** | ||
* Secret containing connection information for pgSTAC database. | ||
*/ | ||
readonly dbSecret: secretsmanager.ISecret; | ||
|
||
/** | ||
* Custom code to run for fastapi-pgstac. | ||
* | ||
* @default - simplified version of fastapi-pgstac | ||
*/ | ||
readonly apiCode?: ApiEntrypoint; | ||
|
||
/** | ||
* Customized environment variables to send to fastapi-pgstac runtime. | ||
*/ | ||
readonly apiEnv?: Record<string, string>; | ||
} | ||
|
||
export interface ApiEntrypoint { | ||
/** | ||
* Path to the source of the function or the location for dependencies. | ||
*/ | ||
readonly entry: PythonFunctionProps["entry"]; | ||
/** | ||
* The path (relative to entry) to the index file containing the exported handler. | ||
*/ | ||
readonly index: PythonFunctionProps["index"]; | ||
/** | ||
* The name of the exported handler in the index file. | ||
*/ | ||
readonly handler: PythonFunctionProps["handler"]; | ||
} |
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 @@ | ||
*/.venv/* |
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,8 @@ | ||
mangum==0.15.1 | ||
stac-fastapi.api==2.4.1 | ||
stac-fastapi.extensions==2.4.1 | ||
stac-fastapi.pgstac==2.4.1 | ||
stac-fastapi.types==2.4.1 | ||
# https://github.com/stac-utils/stac-fastapi/pull/466 | ||
pygeoif==0.7 | ||
starlette_cramjam |
Empty file.
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,60 @@ | ||
""" | ||
FastAPI application using PGStac. | ||
""" | ||
|
||
from fastapi.middleware.cors import CORSMiddleware | ||
from fastapi.responses import ORJSONResponse | ||
from stac_fastapi.api.app import StacApi | ||
from stac_fastapi.pgstac.core import CoreCrudClient | ||
from stac_fastapi.pgstac.db import close_db_connection, connect_to_db | ||
from starlette_cramjam.middleware import CompressionMiddleware | ||
|
||
from .config import ( | ||
ApiSettings, | ||
extensions as PgStacExtensions, | ||
get_request_model as GETModel, | ||
post_request_model as POSTModel, | ||
) | ||
|
||
api_settings = ApiSettings() | ||
|
||
api = StacApi( | ||
title=api_settings.name, | ||
api_version=api_settings.version, | ||
description=api_settings.description or api_settings.name, | ||
settings=api_settings.load_postgres_settings(), | ||
extensions=PgStacExtensions, | ||
client=CoreCrudClient(post_request_model=POSTModel), | ||
search_get_request_model=GETModel, | ||
search_post_request_model=POSTModel, | ||
response_class=ORJSONResponse, | ||
middlewares=[CompressionMiddleware], | ||
) | ||
|
||
app = api.app | ||
|
||
# Set all CORS enabled origins | ||
if api_settings.cors_origins: | ||
app.add_middleware( | ||
CORSMiddleware, | ||
allow_origins=api_settings.cors_origins, | ||
allow_credentials=True, | ||
allow_methods=["GET", "POST", "OPTIONS"], | ||
allow_headers=["*"], | ||
) | ||
|
||
|
||
@app.on_event("startup") | ||
async def startup_event(): | ||
"""Connect to database on startup.""" | ||
print("Setting up DB connection...") | ||
await connect_to_db(app) | ||
print("DB connection setup.") | ||
|
||
|
||
@app.on_event("shutdown") | ||
async def shutdown_event(): | ||
"""Close database connection.""" | ||
print("Closing up DB connection...") | ||
await close_db_connection(app) | ||
print("DB connection closed.") |
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,97 @@ | ||
"""API settings. | ||
Based on https://github.com/developmentseed/eoAPI/tree/master/src/eoapi/stac""" | ||
import base64 | ||
import json | ||
from typing import Optional | ||
|
||
import boto3 | ||
import pydantic | ||
|
||
from stac_fastapi.api.models import create_get_request_model, create_post_request_model | ||
|
||
# from stac_fastapi.pgstac.extensions import QueryExtension | ||
from stac_fastapi.extensions.core import ( | ||
ContextExtension, | ||
FieldsExtension, | ||
FilterExtension, | ||
QueryExtension, | ||
SortExtension, | ||
TokenPaginationExtension, | ||
) | ||
from stac_fastapi.pgstac.config import Settings | ||
from stac_fastapi.pgstac.types.search import PgstacSearch | ||
|
||
|
||
def get_secret_dict(secret_name: str): | ||
"""Retrieve secrets from AWS Secrets Manager | ||
Args: | ||
secret_name (str): name of aws secrets manager secret containing database connection secrets | ||
profile_name (str, optional): optional name of aws profile for use in debugger only | ||
Returns: | ||
secrets (dict): decrypted secrets in dict | ||
""" | ||
|
||
# Create a Secrets Manager client | ||
session = boto3.session.Session() | ||
client = session.client(service_name="secretsmanager") | ||
|
||
get_secret_value_response = client.get_secret_value(SecretId=secret_name) | ||
|
||
if "SecretString" in get_secret_value_response: | ||
return json.loads(get_secret_value_response["SecretString"]) | ||
else: | ||
return json.loads(base64.b64decode(get_secret_value_response["SecretBinary"])) | ||
|
||
|
||
class ApiSettings(pydantic.BaseSettings): | ||
"""API settings""" | ||
|
||
name: str = "asdi-stac-api" | ||
version: str = "0.1" | ||
description: Optional[str] = None | ||
cors_origins: str = "*" | ||
cachecontrol: str = "public, max-age=3600" | ||
debug: bool = False | ||
|
||
pgstac_secret_arn: Optional[str] | ||
|
||
@pydantic.validator("cors_origins") | ||
def parse_cors_origin(cls, v): | ||
"""Parse CORS origins.""" | ||
return [origin.strip() for origin in v.split(",")] | ||
|
||
def load_postgres_settings(self) -> "Settings": | ||
"""Load postgres connection params from AWS secret""" | ||
|
||
if self.pgstac_secret_arn: | ||
secret = get_secret_dict(self.pgstac_secret_arn) | ||
|
||
return Settings( | ||
postgres_host_reader=secret["host"], | ||
postgres_host_writer=secret["host"], | ||
postgres_dbname=secret["dbname"], | ||
postgres_user=secret["username"], | ||
postgres_pass=secret["password"], | ||
postgres_port=secret["port"], | ||
) | ||
else: | ||
return Settings() | ||
|
||
class Config: | ||
"""model config""" | ||
|
||
env_file = ".env" | ||
|
||
|
||
extensions = [ | ||
FilterExtension(), | ||
QueryExtension(), | ||
SortExtension(), | ||
FieldsExtension(), | ||
TokenPaginationExtension(), | ||
ContextExtension(), | ||
] | ||
post_request_model = create_post_request_model(extensions, base_model=PgstacSearch) | ||
get_request_model = create_get_request_model(extensions) |
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,9 @@ | ||
""" | ||
Handler for AWS Lambda. | ||
""" | ||
|
||
from mangum import Mangum | ||
|
||
from .app import app | ||
|
||
handler = Mangum(app) |
Oops, something went wrong.