11from dataclasses import dataclass , field , fields , is_dataclass
2+ from datetime import datetime , timezone
23from enum import Enum
34
45from .utils import update_dataclass_from_dict
89 MetadataConfig ,
910 ResourceConfigTemplate ,
1011)
11- from .top_level .utils import InlineList , bbox_from_list
12+ from .top_level .utils import InlineList
1213from .top_level .providers import ProviderTemplate
1314from .top_level .providers .records import ProviderTypes
15+ from .top_level .ResourceConfigTemplate import ResourceTypesEnum
1416
1517
1618@dataclass (kw_only = True )
@@ -23,7 +25,9 @@ class ConfigData:
2325 server : ServerConfig = field (default_factory = lambda : ServerConfig ())
2426 logging : LoggingConfig = field (default_factory = lambda : LoggingConfig ())
2527 metadata : MetadataConfig = field (default_factory = lambda : MetadataConfig ())
26- resources : dict [str , ResourceConfigTemplate ] = field (default_factory = lambda : {})
28+ resources : dict [str , ResourceConfigTemplate | dict ] = field (
29+ default_factory = lambda : {}
30+ )
2731
2832 def set_data_from_yaml (self , dict_content : dict ):
2933 """Parse YAML file content and overwride .config_data properties where available."""
@@ -69,30 +73,38 @@ def set_data_from_yaml(self, dict_content: dict):
6973 resource_instance_name = next (iter (res_config ))
7074 resource_data = res_config [resource_instance_name ]
7175
72- # Create a new ResourceConfigTemplate instance and update with available values
73- new_resource_item = ResourceConfigTemplate (
74- instance_name = resource_instance_name
75- )
76- defaults_resource , wrong_types_resource , all_missing_props_resource = (
77- update_dataclass_from_dict (
76+ # only cast to ResourceConfigTemplate, if it's supported Resource type (e.g. 'collection, stac-collection')
77+ if resource_data .get ("type" ) in [e .value for e in ResourceTypesEnum ]:
78+
79+ # Create a new ResourceConfigTemplate instance and update with available values
80+ new_resource_item = ResourceConfigTemplate .init_with_name (
81+ instance_name = resource_instance_name
82+ )
83+ (
84+ defaults_resource ,
85+ wrong_types_resource ,
86+ all_missing_props_resource ,
87+ ) = update_dataclass_from_dict (
7888 new_resource_item ,
7989 resource_data ,
8090 f"resources.{ resource_instance_name } " ,
8191 )
82- )
83- default_fields .extend (defaults_resource )
84- wrong_types .extend (wrong_types_resource )
85- all_missing_props .extend (all_missing_props_resource )
86-
87- # Exceptional check: verify that all list items of BBox are integers, and len(list)=4 or 6
88- if not new_resource_item .validate_reassign_bbox ():
89- wrong_types .append (
90- f"resources.{ resource_instance_name } .extents.spatial.bbox"
91- )
92-
93- # reorder providers to move read-only to the end of the list
94- # this is needed to not accidentally match read-only providers when deleting a provider
95- new_resource_item .providers .sort (key = lambda x : isinstance (x , dict ))
92+ default_fields .extend (defaults_resource )
93+ wrong_types .extend (wrong_types_resource )
94+ all_missing_props .extend (all_missing_props_resource )
95+
96+ # Exceptional check: verify that all list items of BBox are integers, and len(list)=4 or 6
97+ if not new_resource_item .validate_reassign_bbox ():
98+ wrong_types .append (
99+ f"resources.{ resource_instance_name } .extents.spatial.bbox"
100+ )
101+
102+ # reorder providers to move read-only to the end of the list
103+ # this is needed to not accidentally match read-only providers when deleting a provider
104+ new_resource_item .providers .sort (key = lambda x : isinstance (x , dict ))
105+ else :
106+ # keep as dict if unsopported resource type (e.g. 'process')
107+ new_resource_item = resource_data
96108
97109 self .resources [resource_instance_name ] = new_resource_item
98110
@@ -129,33 +141,52 @@ def all_missing_props(self):
129141 return self ._all_missing_props
130142 return []
131143
132- def asdict_enum_safe (self , obj ):
144+ def datetime_to_string (self , data : datetime ):
145+ # normalize to UTC and format with Z
146+ if data .tzinfo is None :
147+ data = data .replace (tzinfo = timezone .utc )
148+ else :
149+ data = data .astimezone (timezone .utc )
150+ return data .strftime ("%Y-%m-%dT%H:%M:%SZ" )
151+
152+ def asdict_enum_safe (self , obj , datetime_to_str = False ):
133153 """Overwriting dataclass 'asdict' fuction to replace Enums with strings."""
134154 if is_dataclass (obj ):
135155 result = {}
136156 for f in fields (obj ):
137157 value = getattr (obj , f .name )
158+
159+ key = f .name
160+ if key == "linked__data" :
161+ key = "linked-data"
138162 if value is not None :
139- result [f . name ] = self .asdict_enum_safe (value )
163+ result [key ] = self .asdict_enum_safe (value , datetime_to_str )
140164 return result
141165 elif isinstance (obj , Enum ):
142166 return obj .value
143167 elif isinstance (obj , InlineList ):
144168 return obj
145169 elif isinstance (obj , list ):
146- return [self .asdict_enum_safe (v ) for v in obj ]
170+ return [self .asdict_enum_safe (v , datetime_to_str ) for v in obj ]
147171 elif isinstance (obj , dict ):
148172 return {
149- self .asdict_enum_safe (k ): self .asdict_enum_safe (v )
173+ self .asdict_enum_safe (k , datetime_to_str ): self .asdict_enum_safe (
174+ v , datetime_to_str
175+ )
150176 for k , v in obj .items ()
151177 }
152178 else :
153- return obj
179+ if isinstance (obj , datetime ) and datetime_to_str :
180+ return self .datetime_to_string (obj )
181+ else :
182+ return obj
154183
155184 def add_new_resource (self ) -> str :
156185 """Add a placeholder resource."""
157186 new_name = "new_resource"
158- self .resources [new_name ] = ResourceConfigTemplate (instance_name = new_name )
187+ self .resources [new_name ] = ResourceConfigTemplate .init_with_name (
188+ instance_name = new_name
189+ )
159190 return new_name
160191
161192 def delete_resource (self , dialog ):
@@ -173,7 +204,7 @@ def set_validate_new_provider_data(
173204
174205 # initialize provider; assign ui_dict data to the provider instance
175206 new_provider = ProviderTemplate .init_provider_from_type (provider_type )
176- new_provider .assign_ui_dict_to_provider_data (values )
207+ new_provider .assign_ui_dict_to_provider_data_on_save (values )
177208
178209 # if incomplete data, remove Provider from ConfigData and show Warning
179210 invalid_props = new_provider .get_invalid_properties ()
@@ -192,9 +223,11 @@ def validate_config_data(self) -> int:
192223 invalid_props .extend (self .server .get_invalid_properties ())
193224 invalid_props .extend (self .metadata .get_invalid_properties ())
194225 for key , resource in self .resources .items ():
195- invalid_res_props = [
196- f"resources.{ key } .{ prop } " for prop in resource .get_invalid_properties ()
197- ]
198- invalid_props .extend (invalid_res_props )
226+ if not isinstance (resource , dict ):
227+ invalid_res_props = [
228+ f"resources.{ key } .{ prop } "
229+ for prop in resource .get_invalid_properties ()
230+ ]
231+ invalid_props .extend (invalid_res_props )
199232
200233 return invalid_props
0 commit comments