Skip to content

Commit 5280a23

Browse files
authored
fixes for various issues in 4.2.3 (#1232)
* fixes for various issues in 4.2.3
1 parent d3e6924 commit 5280a23

File tree

7 files changed

+62
-49
lines changed

7 files changed

+62
-49
lines changed

cfn-templates/cid-cfn.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# https://github.com/aws-samples/aws-cudos-framework-deployment/blob/main/cfn-templates/cid-cfn.yml
22
AWSTemplateFormatVersion: '2010-09-09'
3-
Description: Deployment of Cloud Intelligence Dashboards v4.2.3 - AWS Solution SO9011
3+
Description: Deployment of Cloud Intelligence Dashboards v4.2.4 - AWS Solution SO9011
44
Metadata:
55
AWS::CloudFormation::Interface:
66
ParameterGroups:
@@ -2070,7 +2070,7 @@ Resources:
20702070
SourceBucket: !Ref ReferenceAssetsBucket
20712071
DestinationBucket: !Ref LocalAssetsBucket
20722072
Keys:
2073-
- 'cid-resource-lambda-layer/cid-4.2.3.zip' #replace version here if needed
2073+
- 'cid-resource-lambda-layer/cid-4.2.4.zip' #replace version here if needed
20742074

20752075
CidResourceLambdaLayer:
20762076
Type: AWS::Lambda::LayerVersion
@@ -2085,7 +2085,7 @@ Resources:
20852085
- LambdaLayerBucketPrefixIsManaged
20862086
- !FindInMap [RegionMap, !Ref 'AWS::Region', BucketName]
20872087
- !Sub '${LambdaLayerBucketPrefix}-${AWS::Region}' # Region added for backward compatibility
2088-
S3Key: 'cid-resource-lambda-layer/cid-4.2.3.zip' #replace version here if needed
2088+
S3Key: 'cid-resource-lambda-layer/cid-4.2.4.zip' #replace version here if needed
20892089
CompatibleRuntimes:
20902090
- python3.10
20912091
- python3.11

cid/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__version__ = '4.2.3'
1+
__version__ = '4.2.4'
22

cid/helpers/athena.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -282,16 +282,18 @@ def list_work_groups(self) -> list:
282282
logger.debug(f'WorkGroups: {result.get("WorkGroups")}')
283283
return result.get('WorkGroups')
284284

285-
def get_table_metadata(self, table_name: str, database_name: str=None) -> dict:
286-
table_metadata = self._metadata.get(table_name)
287-
params = {
288-
'CatalogName': self.CatalogName,
289-
'DatabaseName': database_name or self.DatabaseName,
290-
'TableName': table_name
291-
}
285+
def get_table_metadata(self, table_name: str, database_name: str=None, no_cache: bool=False) -> dict:
286+
table_metadata = None
287+
if not no_cache:
288+
table_metadata = self._metadata.get(table_name)
292289
if not table_metadata:
290+
params = {
291+
'CatalogName': self.CatalogName,
292+
'DatabaseName': database_name or self.DatabaseName,
293+
'TableName': table_name,
294+
}
293295
table_metadata = self.client.get_table_metadata(**params).get('TableMetadata')
294-
self._metadata.update({table_name: table_metadata})
296+
self._metadata[table_name] = table_metadata
295297

296298
return table_metadata
297299

cid/helpers/cur.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,10 @@ def tag_and_cost_category_fields(self) -> list:
184184
return self._tag_and_cost_category
185185

186186
self._tag_and_cost_category = []
187-
number_of_rows_scanned = 100000 # empiric value
188-
for tag_type in ["resource_tags", 'cost_category']:
187+
number_of_rows_scanned = 500000 # empiric value
188+
for tag_type in ['resource_tags', 'cost_category']:
189+
if tag_type not in self.fields:
190+
logging.debug(f'skipping {tag_type} scan')
189191
cid_print(f'Scanning {tag_type} in {self.table_name}.')
190192
try:
191193
res = self.athena.query(
@@ -211,7 +213,9 @@ def tag_and_cost_category_fields(self) -> list:
211213
cid_print(f' <BOLD>{tag_type:<{max_width}} | Distinct Values <END> ')
212214
for line in res:
213215
if int(line[1]) > 10:
214-
cid_print(f' <BOLD>{line[0]:<{max_width}}<END> | {line[1]} ')
216+
name = line[0]
217+
name = name.replace('user_', '')
218+
cid_print(f' <BOLD>{name:<{max_width}}<END> | {line[1]} ')
215219
self._tag_and_cost_category += sorted([f"{tag_type}['{line[0]}']" for line in res])
216220
except (self.athena.client.exceptions.ClientError, CidCritical, ValueError) as exc:
217221
logger.error(f'Failed to read {tag_type} from {self.table_name}: "{exc}". Will continue without.')

cid/helpers/quicksight/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,8 @@ def ensure_dataset_refresh_schedule(self, dataset_id, schedules: list):
12051205
schedule["RefreshType"] = schedule.get("RefreshType", "FULL_REFRESH")
12061206
if "providedBy" in schedule:
12071207
del schedule["providedBy"]
1208+
if "source" in schedule:
1209+
del schedule["source"]
12081210

12091211
if not existing_schedule:
12101212
# Avoid adding a new schedule when customer already has put a schedule manually as this can lead to additional charges.

cid/helpers/quicksight/dataset.py

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -68,33 +68,12 @@ def patch(dataset, custom_fields={}, athena=None):
6868
'''
6969
def _get_athena_columns(table, database=None):
7070
'''returns athena columns'''
71+
metadata = athena.get_table_metadata(table, database_name=database, no_cache=True)
7172
return [
72-
line[0].split(None, 1)
73-
for line in athena.query(
74-
f'SHOW COLUMNS FROM {table}',
75-
include_header=True, database=database
76-
)
73+
(col['Name'], col['Type'])
74+
for col in metadata.get('Columns', [])
7775
]
7876

79-
def _replace_columns(existing_columns, new_columns):
80-
'''replace columns but keep the order, ignore case changes
81-
assert _replace_columns(
82-
[{'Name': 'a'}, {'Name': 'B'}, {'Name': 'c'}],
83-
[{'Name': 'A'}, {'Name': 'b'}, {'Name': 'd'}]) \
84-
== [{'Name': 'a'}, {'Name': 'B'}, {'Name': 'd'}]
85-
Different types will be replaced
86-
'''
87-
existing_columns = [
88-
existing_col
89-
for existing_col in existing_columns
90-
if str(existing_col).lower() in [str(c).lower() for c in new_columns]
91-
] # filter out old
92-
for col in new_columns: # add new
93-
if col['Name'].lower() not in [c['Name'].lower() for c in existing_columns]:
94-
existing_columns.append(col)
95-
#REFACTOR: what if col is there but another type?
96-
return existing_columns
97-
9877
def _athena_to_qs_type(col, athena_type):
9978
'''map athena type to QS type
10079
The following data types are supported in SPICE: Date, Decimal-fixed, Decimal-float, Integer, and String.
@@ -125,8 +104,14 @@ def _athena_to_qs_type(col, athena_type):
125104
root_lt = dataset['LogicalTableMap'][lt]
126105
break
127106
else:
128-
raise ValueError(f'Unable to find a root logical table in the dataset {dataset}')
129-
projected_cols = next(ds['ProjectOperation']["ProjectedColumns"] for ds in root_lt['DataTransforms'] if 'ProjectOperation' in ds)
107+
# take the first one and let's hope it is fine
108+
root_lt = next(iter(dataset['LogicalTableMap'].values()))
109+
110+
projected_cols = next( # get the first DataTrasform with ProjectOperation
111+
ds['ProjectOperation']["ProjectedColumns"]
112+
for ds in root_lt['DataTransforms']
113+
if 'ProjectOperation' in ds
114+
)
130115

131116
# Update each PhysicalTableMap with all columns from athena views
132117
all_columns = []
@@ -135,12 +120,31 @@ def _athena_to_qs_type(col, athena_type):
135120
database = pt['RelationalTable']['Schema']
136121
columns = _get_athena_columns(table_name, database)
137122
logger.trace(f'columns = {columns}')
138-
139-
new_columns = [_athena_to_qs_type(name, athena_type) for name, athena_type in columns]
140-
#for col in new_columns:
141-
# if col['Name'] in [existing_col['Name'] for existing_col in all_columns]: #FIXME not all_columns so far but must be all cols before modification
142-
# col['Name'] = f'{col["Name"]}[{table_name}]'
143-
pt['RelationalTable']['InputColumns'] = _replace_columns(pt['RelationalTable']['InputColumns'], new_columns)
123+
athena_columns = [
124+
_athena_to_qs_type(name, athena_type.lower())
125+
for name, athena_type in columns
126+
]
127+
logger.trace(f'athena_columns = {columns}')
128+
athena_columns_names = [c['Name'].lower() for c in athena_columns]
129+
dataset_columns = pt['RelationalTable']['InputColumns']
130+
dataset_columns_names = [col['Name'].lower() for col in dataset_columns]
131+
dataset_columns_to_keep = [
132+
col for col in dataset_columns
133+
if col['Name'].lower() in athena_columns_names
134+
]
135+
new_columns = [
136+
col for col in athena_columns
137+
if col['Name'].lower() not in dataset_columns_names
138+
] # BTW what if col is there but another type?
139+
140+
for col in new_columns: # alter names for columns that already exist
141+
if col['Name'].lower() in projected_cols:
142+
col['Name'] = f"{col['Name']}[{table_name}]" # What if it is alrady there?
143+
144+
logger.trace(f'dataset_columns_to_keep = {dataset_columns_to_keep}')
145+
if new_columns:
146+
logger.trace(f'new_columns = {new_columns}')
147+
pt['RelationalTable']['InputColumns'] = dataset_columns_to_keep + new_columns
144148
all_columns += [col['Name'] for col in pt['RelationalTable']['InputColumns']]
145149

146150
# Add all needed calc fields

cid/utils.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def set_defaults(data: dict) -> None:
190190
if data:
191191
defaults.update(data)
192192

193-
def get_defaults() -> None:
193+
def get_defaults() -> dict:
194194
global defaults
195195
return dict(defaults)
196196

@@ -250,7 +250,7 @@ def get_parameter(param_name, message, choices=None, default=None, none_as_disab
250250
param_name = param_name.replace('_', '-')
251251

252252
# override defaults from code with outside defaults
253-
if param_name in defaults:
253+
if param_name in get_defaults():
254254
default = defaults.get(param_name)
255255
if multi and isinstance(default, str):
256256
default = split_respecting_quotes(default)
@@ -276,6 +276,7 @@ def get_parameter(param_name, message, choices=None, default=None, none_as_disab
276276
if multi:
277277
default = default or []
278278
default = default if isinstance(default, list) else [default]
279+
default = [c for c in defaults if c in choices]
279280
if not isatty():
280281
result = default
281282
else:

0 commit comments

Comments
 (0)