aws-cost-calculator-cli 1.6.3__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of aws-cost-calculator-cli might be problematic. Click here for more details.

@@ -1,148 +0,0 @@
1
- """
2
- Lambda handler for profile CRUD operations.
3
- """
4
- import json
5
- import os
6
- import boto3
7
- from datetime import datetime
8
-
9
-
10
- def handler(event, context):
11
- """Handle profile CRUD operations."""
12
-
13
- # Parse request
14
- try:
15
- if isinstance(event.get('body'), str):
16
- body = json.loads(event['body'])
17
- else:
18
- body = event.get('body', {})
19
- except:
20
- body = event
21
-
22
- # Validate API secret
23
- headers = event.get('headers', {})
24
- api_secret = headers.get('X-API-Secret') or headers.get('x-api-secret')
25
-
26
- secret_name = os.environ.get('SECRET_NAME', 'cost-calculator-api-secret')
27
- secrets_client = boto3.client('secretsmanager')
28
-
29
- try:
30
- secret_response = secrets_client.get_secret_value(SecretId=secret_name)
31
- expected_secret = secret_response['SecretString']
32
-
33
- if api_secret != expected_secret:
34
- return {
35
- 'statusCode': 401,
36
- 'headers': {'Content-Type': 'application/json'},
37
- 'body': json.dumps({'error': 'Unauthorized'})
38
- }
39
- except Exception as e:
40
- return {
41
- 'statusCode': 500,
42
- 'headers': {'Content-Type': 'application/json'},
43
- 'body': json.dumps({'error': f'Secret validation failed: {str(e)}'})
44
- }
45
-
46
- # Get operation
47
- operation = body.get('operation') # list, get, create, update, delete
48
- profile_name = body.get('profile_name')
49
-
50
- # DynamoDB table
51
- table_name = os.environ.get('PROFILES_TABLE', 'cost-calculator-profiles')
52
- dynamodb = boto3.resource('dynamodb')
53
- table = dynamodb.Table(table_name)
54
-
55
- try:
56
- if operation == 'list':
57
- # List all profiles
58
- response = table.scan()
59
- profiles = response.get('Items', [])
60
- return {
61
- 'statusCode': 200,
62
- 'headers': {'Content-Type': 'application/json'},
63
- 'body': json.dumps({'profiles': profiles})
64
- }
65
-
66
- elif operation == 'get':
67
- # Get specific profile
68
- if not profile_name:
69
- return {
70
- 'statusCode': 400,
71
- 'headers': {'Content-Type': 'application/json'},
72
- 'body': json.dumps({'error': 'profile_name required'})
73
- }
74
-
75
- response = table.get_item(Key={'profile_name': profile_name})
76
- if 'Item' not in response:
77
- return {
78
- 'statusCode': 404,
79
- 'headers': {'Content-Type': 'application/json'},
80
- 'body': json.dumps({'error': 'Profile not found'})
81
- }
82
-
83
- return {
84
- 'statusCode': 200,
85
- 'headers': {'Content-Type': 'application/json'},
86
- 'body': json.dumps({'profile': response['Item']})
87
- }
88
-
89
- elif operation == 'create' or operation == 'update':
90
- # Create or update profile
91
- if not profile_name:
92
- return {
93
- 'statusCode': 400,
94
- 'headers': {'Content-Type': 'application/json'},
95
- 'body': json.dumps({'error': 'profile_name required'})
96
- }
97
-
98
- accounts = body.get('accounts', [])
99
- description = body.get('description', '')
100
-
101
- item = {
102
- 'profile_name': profile_name,
103
- 'accounts': accounts,
104
- 'description': description,
105
- 'updated_at': datetime.utcnow().isoformat()
106
- }
107
-
108
- if operation == 'create':
109
- item['created_at'] = datetime.utcnow().isoformat()
110
-
111
- table.put_item(Item=item)
112
-
113
- return {
114
- 'statusCode': 200,
115
- 'headers': {'Content-Type': 'application/json'},
116
- 'body': json.dumps({'message': 'Profile saved', 'profile': item})
117
- }
118
-
119
- elif operation == 'delete':
120
- # Delete profile
121
- if not profile_name:
122
- return {
123
- 'statusCode': 400,
124
- 'headers': {'Content-Type': 'application/json'},
125
- 'body': json.dumps({'error': 'profile_name required'})
126
- }
127
-
128
- table.delete_item(Key={'profile_name': profile_name})
129
-
130
- return {
131
- 'statusCode': 200,
132
- 'headers': {'Content-Type': 'application/json'},
133
- 'body': json.dumps({'message': 'Profile deleted'})
134
- }
135
-
136
- else:
137
- return {
138
- 'statusCode': 400,
139
- 'headers': {'Content-Type': 'application/json'},
140
- 'body': json.dumps({'error': 'Invalid operation. Use: list, get, create, update, delete'})
141
- }
142
-
143
- except Exception as e:
144
- return {
145
- 'statusCode': 500,
146
- 'headers': {'Content-Type': 'application/json'},
147
- 'body': json.dumps({'error': str(e)})
148
- }
@@ -1,106 +0,0 @@
1
- """
2
- Lambda handler for trends analysis.
3
- """
4
- import json
5
- import boto3
6
- import os
7
- from algorithms.trends import analyze_trends
8
-
9
- # Get API secret from Secrets Manager
10
- secrets_client = boto3.client('secretsmanager')
11
- api_secret_arn = os.environ['API_SECRET_ARN']
12
- api_secret = secrets_client.get_secret_value(SecretId=api_secret_arn)['SecretString']
13
-
14
-
15
- def handler(event, context):
16
- """
17
- Lambda handler for trends analysis.
18
-
19
- Expected event:
20
- {
21
- "credentials": {
22
- "access_key": "AKIA...",
23
- "secret_key": "...",
24
- "session_token": "..." (optional)
25
- },
26
- "accounts": ["123456789012", "987654321098"],
27
- "weeks": 4
28
- }
29
- """
30
- # Handle OPTIONS for CORS
31
- if event.get('requestContext', {}).get('http', {}).get('method') == 'OPTIONS':
32
- return {
33
- 'statusCode': 200,
34
- 'headers': {
35
- 'Access-Control-Allow-Origin': '*',
36
- 'Access-Control-Allow-Headers': '*',
37
- 'Access-Control-Allow-Methods': 'POST, OPTIONS'
38
- },
39
- 'body': ''
40
- }
41
-
42
- try:
43
- # Validate API secret
44
- headers = event.get('headers', {})
45
- provided_secret = headers.get('x-api-secret') or headers.get('X-API-Secret')
46
-
47
- if provided_secret != api_secret:
48
- return {
49
- 'statusCode': 401,
50
- 'headers': {'Access-Control-Allow-Origin': '*'},
51
- 'body': json.dumps({'error': 'Unauthorized'})
52
- }
53
-
54
- # Parse request body
55
- body = json.loads(event.get('body', '{}'))
56
-
57
- credentials = body.get('credentials', {})
58
- accounts = body.get('accounts', [])
59
- weeks = body.get('weeks', 3)
60
-
61
- if not credentials or not accounts:
62
- return {
63
- 'statusCode': 400,
64
- 'headers': {'Access-Control-Allow-Origin': '*'},
65
- 'body': json.dumps({'error': 'Missing credentials or accounts'})
66
- }
67
-
68
- # Create Cost Explorer client with provided credentials
69
- ce_client = boto3.client(
70
- 'ce',
71
- region_name='us-east-1',
72
- aws_access_key_id=credentials['access_key'],
73
- aws_secret_access_key=credentials['secret_key'],
74
- aws_session_token=credentials.get('session_token')
75
- )
76
-
77
- # Run analysis
78
- trends_data = analyze_trends(ce_client, accounts, weeks)
79
-
80
- # Convert datetime objects to strings for JSON serialization
81
- def convert_dates(obj):
82
- if isinstance(obj, dict):
83
- return {k: convert_dates(v) for k, v in obj.items()}
84
- elif isinstance(obj, list):
85
- return [convert_dates(item) for item in obj]
86
- elif hasattr(obj, 'isoformat'):
87
- return obj.isoformat()
88
- return obj
89
-
90
- trends_data = convert_dates(trends_data)
91
-
92
- return {
93
- 'statusCode': 200,
94
- 'headers': {
95
- 'Access-Control-Allow-Origin': '*',
96
- 'Content-Type': 'application/json'
97
- },
98
- 'body': json.dumps(trends_data)
99
- }
100
-
101
- except Exception as e:
102
- return {
103
- 'statusCode': 500,
104
- 'headers': {'Access-Control-Allow-Origin': '*'},
105
- 'body': json.dumps({'error': str(e)})
106
- }