awsquery 0.1.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.
@@ -0,0 +1,76 @@
1
+ # Default column filters for AWS service actions
2
+ # Phase 3A: Core Services (Priority 1)
3
+
4
+ ec2:
5
+ describe_instances:
6
+ columns:
7
+ - "Tags.Name"
8
+ - "InstanceId"
9
+ - "InstanceType"
10
+ - "State.Name"
11
+ - "PublicIpAddress"
12
+ - "PrivateIpAddress"
13
+ description: "Default columns for EC2 instance listing"
14
+
15
+ describe_security_groups:
16
+ columns:
17
+ - "GroupName"
18
+ - "GroupId"
19
+ - "Description"
20
+ - "VpcId"
21
+ description: "Default columns for security group listing"
22
+
23
+ describe_volumes:
24
+ columns:
25
+ - "VolumeId"
26
+ - "VolumeType"
27
+ - "Size"
28
+ - "State"
29
+ - "AvailabilityZone"
30
+ - "Tags.Name"
31
+ description: "Default columns for EBS volume listing"
32
+
33
+ s3:
34
+ list_buckets:
35
+ columns:
36
+ - "Name"
37
+ - "CreationDate"
38
+ description: "Default columns for S3 bucket listing"
39
+
40
+ list_objects_v2:
41
+ columns:
42
+ - "Key"
43
+ - "LastModified"
44
+ - "Size"
45
+ - "StorageClass"
46
+ description: "Default columns for S3 object listing"
47
+
48
+ lambda:
49
+ list_functions:
50
+ columns:
51
+ - "FunctionName"
52
+ - "Runtime"
53
+ - "LastModified"
54
+ - "CodeSize"
55
+ - "Description"
56
+ description: "Default columns for Lambda function listing"
57
+
58
+ rds:
59
+ describe_db_instances:
60
+ columns:
61
+ - "DBInstanceIdentifier"
62
+ - "DBInstanceClass"
63
+ - "Engine"
64
+ - "DBInstanceStatus"
65
+ - "AllocatedStorage"
66
+ - "AvailabilityZone"
67
+ description: "Default columns for RDS instance listing"
68
+
69
+ cloudformation:
70
+ describe_stacks:
71
+ columns:
72
+ - "StackName"
73
+ - "StackStatus"
74
+ - "CreationTime"
75
+ - "LastUpdated"
76
+ description: "Default columns for CloudFormation stack listing"
awsquery/filters.py ADDED
@@ -0,0 +1,308 @@
1
+ """Filtering logic for AWS Query Tool."""
2
+
3
+ import sys
4
+
5
+ from .formatters import convert_parameter_name, flatten_dict_keys, transform_tags_structure
6
+ from .utils import debug_print, simplify_key
7
+
8
+
9
+ def filter_resources(resources, value_filters):
10
+ """Filter resources by value filters (ALL must match)"""
11
+ if not value_filters:
12
+ return resources
13
+
14
+ for filter_text in value_filters:
15
+ debug_print(f"Applying value filter: {filter_text}")
16
+
17
+ # Apply tag transformation before filtering
18
+ transformed_resources = []
19
+ for resource in resources:
20
+ transformed = transform_tags_structure(resource)
21
+ transformed_resources.append(transformed)
22
+
23
+ filtered: list[dict] = []
24
+ for resource in transformed_resources:
25
+ flattened = flatten_dict_keys(resource)
26
+
27
+ searchable_items = []
28
+
29
+ searchable_items.extend([key.lower() for key in flattened.keys()])
30
+
31
+ searchable_items.extend([str(value).lower() for value in flattened.values()])
32
+
33
+ if len(filtered) + len([r for r in resources if r != resource]) < 3:
34
+ debug_print(f"Sample flattened keys: {list(flattened.keys())[:5]}")
35
+ debug_print(f"Sample searchable items: {searchable_items[:10]}")
36
+
37
+ matches_all = True
38
+ for filter_text in value_filters:
39
+ filter_lower = filter_text.lower()
40
+ if not any(filter_lower in item for item in searchable_items):
41
+ matches_all = False
42
+ break
43
+ else:
44
+ matching_items = [item for item in searchable_items if filter_lower in item]
45
+ debug_print(
46
+ f"Filter '{filter_text}' matched: {matching_items[:3]}"
47
+ f"{'...' if len(matching_items) > 3 else ''}"
48
+ )
49
+
50
+ if matches_all:
51
+ filtered.append(resource)
52
+
53
+ debug_print(f"Found {len(filtered)} resources matching filters (out of {len(resources)} total)")
54
+ return filtered
55
+
56
+
57
+ def parse_multi_level_filters_for_mode(argv, mode="single"):
58
+ """Parse command line args with -- separators for proper filtering based on mode
59
+
60
+ Mode behavior:
61
+ single: resource_filters=[], everything else as value/column filters
62
+ multi: proper semantic meaning of separators:
63
+ - Args before first -- = resource filters
64
+ - Args between first and second -- = value filters
65
+ - Args after second -- = column filters
66
+ """
67
+ separator_positions = []
68
+ for i, arg in enumerate(argv):
69
+ if arg == "--":
70
+ separator_positions.append(i)
71
+
72
+ segments = []
73
+ start = 0
74
+
75
+ for pos in separator_positions:
76
+ segments.append(argv[start:pos])
77
+ start = pos + 1
78
+
79
+ segments.append(argv[start:])
80
+
81
+ first_segment = segments[0] if segments else []
82
+
83
+ third_segment: list[str]
84
+ if len(segments) == 1:
85
+ second_segment = []
86
+ third_segment = []
87
+ elif len(segments) == 2:
88
+ second_segment = segments[1]
89
+ third_segment = []
90
+ else:
91
+ second_segment = segments[1]
92
+ third_segment = segments[2]
93
+
94
+ base_command = []
95
+ service_found = False
96
+ action_found = False
97
+ extra_args = []
98
+
99
+ for arg in first_segment:
100
+ if arg.startswith("-"):
101
+ base_command.append(arg)
102
+ elif not service_found:
103
+ base_command.append(arg)
104
+ service_found = True
105
+ elif not action_found:
106
+ base_command.append(arg)
107
+ action_found = True
108
+ else:
109
+ extra_args.append(arg)
110
+
111
+ if mode == "single":
112
+ resource_filters: list[str] = []
113
+
114
+ if len(segments) == 1:
115
+ value_filters = extra_args
116
+ column_filters = []
117
+ elif len(segments) == 2:
118
+ value_filters = extra_args
119
+ column_filters = second_segment
120
+ else:
121
+ value_filters = extra_args + second_segment
122
+ column_filters = third_segment
123
+
124
+ elif mode == "multi":
125
+ if len(segments) == 1:
126
+ resource_filters = []
127
+ value_filters = extra_args
128
+ column_filters = []
129
+ elif len(segments) == 2:
130
+ resource_filters = []
131
+ value_filters = extra_args
132
+ column_filters = second_segment
133
+ else:
134
+ resource_filters = extra_args
135
+ value_filters = second_segment
136
+ column_filters = third_segment
137
+
138
+ else:
139
+ raise ValueError(f"Invalid mode '{mode}'. Must be 'single' or 'multi'.")
140
+
141
+ debug_print(
142
+ f"Multi-level parsing (mode={mode}) - Base: {base_command}, "
143
+ f"Resource: {resource_filters}, Value: {value_filters}, "
144
+ f"Column: {column_filters}"
145
+ )
146
+
147
+ return base_command, resource_filters, value_filters, column_filters
148
+
149
+
150
+ def extract_parameter_values(resources, parameter_name):
151
+ """Extract parameter values from list operation results"""
152
+ if not resources:
153
+ return []
154
+
155
+ values = []
156
+
157
+ if resources and isinstance(resources[0], str):
158
+ debug_print(
159
+ f"Resources are simple strings, using them directly for parameter '{parameter_name}'"
160
+ )
161
+ return resources
162
+
163
+ # Apply tag transformation before parameter extraction
164
+ transformed_resources = []
165
+ for resource in resources:
166
+ transformed = transform_tags_structure(resource)
167
+ transformed_resources.append(transformed)
168
+
169
+ pascal_case_name = convert_parameter_name(parameter_name)
170
+ search_names = [parameter_name, pascal_case_name]
171
+
172
+ debug_print(f"Looking for parameter values using names: {search_names}")
173
+
174
+ for resource in transformed_resources:
175
+ flat = flatten_dict_keys(resource)
176
+
177
+ found_value = None
178
+ for search_name in search_names:
179
+ if search_name in flat:
180
+ value = flat[search_name]
181
+ if value:
182
+ found_value = str(value)
183
+ break
184
+
185
+ if found_value:
186
+ values.append(found_value)
187
+ continue
188
+
189
+ for search_name in search_names:
190
+ for key, value in flat.items():
191
+ if key.lower() == search_name.lower() and value:
192
+ found_value = str(value)
193
+ break
194
+ if found_value:
195
+ break
196
+
197
+ if found_value:
198
+ values.append(found_value)
199
+ continue
200
+
201
+ for search_name in search_names:
202
+ matching_keys = [k for k in flat.keys() if search_name.lower() in k.lower()]
203
+ if matching_keys:
204
+ key = matching_keys[0]
205
+ value = flat[key]
206
+ if value:
207
+ values.append(str(value))
208
+ break
209
+
210
+ if found_value:
211
+ continue
212
+
213
+ # Standard field fallback when parameter-specific field not found
214
+ standard_fields = []
215
+ param_lower = parameter_name.lower()
216
+
217
+ if param_lower.endswith("name"):
218
+ standard_fields.append("Name")
219
+ elif param_lower.endswith("id"):
220
+ standard_fields.append("Id")
221
+ elif param_lower.endswith(("arn", "ARN")):
222
+ standard_fields.extend(["Arn", "ARN"])
223
+ elif param_lower.endswith("key"):
224
+ standard_fields.append("Key")
225
+ elif param_lower.endswith("value"):
226
+ standard_fields.append("Value")
227
+ else:
228
+ # Common AWS resource types that typically have a Name field
229
+ resource_types_with_names = [
230
+ "bucket",
231
+ "cluster",
232
+ "instance",
233
+ "volume",
234
+ "snapshot",
235
+ "image",
236
+ "vpc",
237
+ "subnet",
238
+ "queue",
239
+ "topic",
240
+ "table",
241
+ "function",
242
+ "role",
243
+ "user",
244
+ "group",
245
+ "policy",
246
+ "stack",
247
+ "template",
248
+ "pipeline",
249
+ "repository",
250
+ "branch",
251
+ "commit",
252
+ "build",
253
+ "project",
254
+ "job",
255
+ "task",
256
+ "service",
257
+ "container",
258
+ "node",
259
+ "nodegroup",
260
+ "database",
261
+ "endpoint",
262
+ "domain",
263
+ "certificate",
264
+ "key",
265
+ "secret",
266
+ "parameter",
267
+ ]
268
+
269
+ if param_lower in resource_types_with_names:
270
+ standard_fields.append("Name")
271
+ debug_print(f"Parameter '{parameter_name}' is a resource type, will try Name field")
272
+
273
+ if standard_fields:
274
+ debug_print(
275
+ f"No specific field found for '{parameter_name}', "
276
+ f"trying standard fields: {standard_fields}"
277
+ )
278
+ for standard_field in standard_fields:
279
+ if standard_field in flat:
280
+ value = flat[standard_field]
281
+ if value:
282
+ debug_print(
283
+ f"Found standard field '{standard_field}' "
284
+ f"for parameter '{parameter_name}'"
285
+ )
286
+ values.append(str(value))
287
+ found_value = str(value)
288
+ break
289
+
290
+ for key, value in flat.items():
291
+ if key.lower() == standard_field.lower() and value:
292
+ debug_print(
293
+ f"Found standard field '{key}' (case-insensitive) "
294
+ f"for parameter '{parameter_name}'"
295
+ )
296
+ values.append(str(value))
297
+ found_value = str(value)
298
+ break
299
+
300
+ if found_value:
301
+ break
302
+
303
+ debug_print(
304
+ f"Extracted {len(values)} values for parameter '{parameter_name}': "
305
+ f"{values[:3]}{'...' if len(values) > 3 else ''}"
306
+ )
307
+
308
+ return values