3
3
import importlib .metadata
4
4
import logging
5
5
import os
6
+ from datetime import datetime
6
7
from typing import Any , Iterable , Literal , Mapping , Optional , Union , cast
7
8
8
9
import httpx
9
- from pydantic import BaseModel , Field
10
+ from pydantic import BaseModel , Field , field_validator
10
11
11
12
LOG = logging .getLogger (__name__ )
12
13
@@ -355,6 +356,48 @@ async def delete(
355
356
return await self .raw_client .delete (endpoint = endpoint )
356
357
357
358
359
+ class GlobalSearchResponse (BaseModel ):
360
+ """The SAPI global search response."""
361
+
362
+ class GlobalSearchResponseItem (BaseModel ):
363
+ id : str = Field (description = 'The id of the item.' )
364
+ name : str = Field (description = 'The name of the item.' )
365
+ type : GlobalSearchTypes = Field (description = 'The type of the item.' )
366
+ full_path : dict [str , Any ] = Field (
367
+ description = (
368
+ 'The full path of the item containing project, branch and other information depending on the '
369
+ 'type of the item.'
370
+ ),
371
+ alias = 'fullPath' ,
372
+ )
373
+ component_id : Optional [str ] = Field (
374
+ default = None , description = 'The id of the component the item belongs to.' , alias = 'componentId'
375
+ )
376
+ organization_id : int = Field (
377
+ description = 'The id of the organization the item belongs to.' , alias = 'organizationId'
378
+ )
379
+ project_id : int = Field (description = 'The id of the project the item belongs to.' , alias = 'projectId' )
380
+ project_name : str = Field (description = 'The name of the project the item belongs to.' , alias = 'projectName' )
381
+ created : datetime = Field (description = 'The date and time the item was created in ISO format.' )
382
+
383
+ all : int = Field (description = 'Total number of found results.' )
384
+ items : list [GlobalSearchResponseItem ] = Field (
385
+ description = 'List of search results containing the items of the GlobalSearchType.'
386
+ )
387
+ by_type : dict [str , int ] = Field (
388
+ description = 'Mapping of found types to the number of corresponding results.' , alias = 'byType'
389
+ )
390
+ by_project : dict [str , str ] = Field (description = 'Mapping of project id to project name.' , alias = 'byProject' )
391
+
392
+ @field_validator ('by_type' , 'by_project' , mode = 'before' )
393
+ @classmethod
394
+ def validate_dict_fields (cls , current_value : Any ) -> Any :
395
+ # If the value is empty-list/None, return an empty dictionary, otherwise return the value
396
+ if not current_value :
397
+ return dict ()
398
+ return current_value
399
+
400
+
358
401
class AsyncStorageClient (KeboolaServiceClient ):
359
402
360
403
def __init__ (self , raw_client : RawKeboolaClient , branch_id : str = 'default' ) -> None :
@@ -838,7 +881,7 @@ async def global_search(
838
881
limit : int = 100 ,
839
882
offset : int = 0 ,
840
883
types : list [GlobalSearchTypes ] | None = None ,
841
- ) -> JsonDict :
884
+ ) -> GlobalSearchResponse :
842
885
"""
843
886
Searches for items in the storage. It allows you to search for entities by name across all projects within an
844
887
organization, even those you do not have direct access to. The search is conducted only through entity names to
@@ -849,7 +892,7 @@ async def global_search(
849
892
:param offset: The offset to start from, pagination parameter.
850
893
:param types: The types of items to search for.
851
894
"""
852
- params : dict [str , Any ] = {
895
+ params : dict [str , Any ] = {
853
896
'query' : query ,
854
897
'projectIds[]' : [await self .project_id ()],
855
898
'branchTypes[]' : 'production' ,
@@ -858,7 +901,8 @@ async def global_search(
858
901
'offset' : offset ,
859
902
}
860
903
params = {k : v for k , v in params .items () if v }
861
- return cast (JsonDict , await self .get (endpoint = 'global-search' , params = params ))
904
+ raw_resp = await self .get (endpoint = 'global-search' , params = params )
905
+ return GlobalSearchResponse .model_validate (raw_resp )
862
906
863
907
async def table_detail (self , table_id : str ) -> JsonDict :
864
908
"""
0 commit comments