refactor: simplify crud-pricing to pure CRUD layer - remove AWS API clients
All checks were successful
kinec.tech/airun-pathfinder-crud-pricing/pipeline/head This commit looks good

Removed AWS Pricing API and Cost Explorer clients to make crud-pricing
a pure DynamoDB CRUD layer. This fixes layer violation where CRUD was
making external AWS API calls.

Changes:
- Deleted src/aws_pricing.rs (AWS Pricing API client)
- Deleted src/cost_explorer.rs (Cost Explorer client)
- Removed PricingClient and StsClient from main.rs
- Removed QueryAwsApi and QueryCostExplorer operations
- Removed fetch_if_missing behavior (Get operation now only reads from cache)
- Removed aws-sdk-pricing, aws-sdk-costexplorer, aws-sdk-sts dependencies
- Removed pricing_api_access and sts_assume_role IAM policies

Result: Pure CRUD layer with only DynamoDB operations (Get, Put, ListCommon, IncrementAccess)
Reduced from ~1100 lines to ~600 lines

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-27 04:44:44 -05:00
parent e88609d724
commit ea8883d0da
6 changed files with 8 additions and 815 deletions

View File

@@ -1,12 +1,8 @@
mod aws_pricing;
mod cost_explorer;
mod db;
mod models;
use aws_config::BehaviorVersion;
use aws_sdk_dynamodb::Client as DynamoDbClient;
use aws_sdk_pricing::Client as PricingClient;
use aws_sdk_sts::Client as StsClient;
use lambda_runtime::{service_fn, Error, LambdaEvent};
use serde_json::Value;
use std::env;
@@ -44,8 +40,6 @@ async fn function_handler(event: LambdaEvent<Value>) -> Result<Value, Error> {
// Load AWS config and create clients
let config = aws_config::load_defaults(BehaviorVersion::latest()).await;
let dynamodb_client = DynamoDbClient::new(&config);
let pricing_client = PricingClient::new(&config);
let sts_client = StsClient::new(&config);
let table_name = env::var("TABLE_NAME").unwrap_or_else(|_| "pathfinder-dev-pricing".to_string());
@@ -53,8 +47,6 @@ async fn function_handler(event: LambdaEvent<Value>) -> Result<Value, Error> {
let result = handle_operation(
request.operation,
&dynamodb_client,
&pricing_client,
&sts_client,
&table_name,
)
.await;
@@ -73,8 +65,6 @@ async fn function_handler(event: LambdaEvent<Value>) -> Result<Value, Error> {
async fn handle_operation(
operation: PricingOperation,
dynamodb_client: &DynamoDbClient,
pricing_client: &PricingClient,
sts_client: &StsClient,
table_name: &str,
) -> Result<Value, String> {
match operation {
@@ -83,9 +73,9 @@ async fn handle_operation(
region,
pricing_type,
aws_account_id,
fetch_if_missing,
fetch_if_missing: _,
} => {
info!("Operation: Get (type={:?}, fetch_if_missing={})", pricing_type, fetch_if_missing);
info!("Operation: Get (type={:?})", pricing_type);
// Check cache
let cached = db::get_pricing(
@@ -126,30 +116,8 @@ async fn handle_operation(
"cacheStatus": "hit"
}))
}
None if fetch_if_missing => {
// Cache miss - fetch from AWS
info!("Cache miss, fetching from AWS API");
let pricing = fetch_pricing(
pricing_client,
sts_client,
&instance_type,
&region,
&pricing_type,
aws_account_id.as_deref(),
)
.await?;
// Cache it
db::put_pricing(dynamodb_client, table_name, &pricing).await?;
Ok(serde_json::json!({
"pricing": pricing,
"cacheStatus": "miss"
}))
}
None => {
Err("Pricing not found and fetch_if_missing=false".to_string())
Err("Pricing not found in cache".to_string())
}
}
}
@@ -209,79 +177,5 @@ async fn handle_operation(
"region": region
}))
}
PricingOperation::QueryAwsApi {
instance_type,
region,
} => {
info!("Operation: QueryAwsApi ({} in {})", instance_type, region);
let pricing = aws_pricing::fetch_ec2_pricing(pricing_client, &instance_type, &region).await?;
Ok(serde_json::json!({
"pricing": pricing,
"source": "aws-pricing-api"
}))
}
PricingOperation::QueryCostExplorer {
instance_type,
region,
aws_account_id,
role_arn,
} => {
info!(
"Operation: QueryCostExplorer ({} in {}, account={})",
instance_type, region, aws_account_id
);
let pricing = cost_explorer::fetch_account_specific_pricing(
sts_client,
&instance_type,
&region,
&aws_account_id,
&role_arn,
)
.await?;
Ok(serde_json::json!({
"pricing": pricing,
"source": "cost-explorer",
"includesEDP": true
}))
}
}
}
/// Fetch pricing based on type (retail or account-specific)
async fn fetch_pricing(
pricing_client: &PricingClient,
sts_client: &StsClient,
instance_type: &str,
region: &str,
pricing_type: &PricingType,
aws_account_id: Option<&str>,
) -> Result<crate::models::PricingData, String> {
match pricing_type {
PricingType::Retail => {
info!("Fetching retail pricing from AWS Pricing API");
aws_pricing::fetch_ec2_pricing(pricing_client, instance_type, region).await
}
PricingType::AccountSpecific => {
let account_id = aws_account_id.ok_or("aws_account_id required for account-specific pricing")?;
// Role ARN is expected to be in format: arn:aws:iam::{account_id}:role/pathfinder-pricing-access
let role_arn = format!("arn:aws:iam::{}:role/pathfinder-pricing-access", account_id);
info!("Fetching account-specific pricing from Cost Explorer");
cost_explorer::fetch_account_specific_pricing(
sts_client,
instance_type,
region,
account_id,
&role_arn,
)
.await
}
}
}