1+ from typing import Any
2+ from enum import Enum , auto
3+ from dataclasses import dataclass
4+ import json
5+ import pyexasol # type: ignore
16import exasol .bucketfs as bfs # type: ignore
27from exasol .saas .client .api_access import get_database_id # type: ignore
38
49from exasol .python_extension_common .cli .std_options import StdParams , check_params
10+ from exasol .python_extension_common .connections .pyexasol_connection import open_pyexasol_connection
511
612
7- def create_bucketfs_location (** kwargs ) -> bfs .path .PathLike :
8- """
9- Creates a BucketFS PathLike object using the data provided in the kwargs. These
10- can be parameters for the BucketFS either On-Prem or SaaS database. The parameters
11- should correspond to the CLI options defined in the cli/std_options.py.
13+ class _Backend (Enum ):
14+ onprem = auto ()
15+ saas = auto ()
1216
13- Raises a ValueError if the provided parameters are insufficient for either
14- On-Prem or SaaS cases.
15- """
1617
17- path_in_bucket = kwargs .get (StdParams .path_in_bucket .name , '' )
18+ def _infer_backend (bfs_params : dict [str , Any ]) -> _Backend :
19+ """
20+ Infers the backend from the provided dictionary of CLI parameters.
21+ Raises a ValueError if the collection of CLI parameters is insufficient to access
22+ the BucketFS on either of the backends.
23+ """
1824
19- # Infer where the database is - on-prem or SaaS.
2025 if check_params ([StdParams .bucketfs_host , StdParams .bucketfs_port ,
2126 StdParams .bucket , StdParams .bucketfs_user , StdParams .bucketfs_password ],
22- kwargs ):
23- net_service = ('https' if kwargs .get (StdParams .bucketfs_use_https .name , True )
24- else 'http' )
25- url = (f"{ net_service } ://"
26- f"{ kwargs [StdParams .bucketfs_host .name ]} :"
27- f"{ kwargs [StdParams .bucketfs_port .name ]} " )
28- return bfs .path .build_path (
29- backend = bfs .path .StorageBackend .onprem ,
30- url = url ,
31- username = kwargs [StdParams .bucketfs_user .name ],
32- password = kwargs [StdParams .bucketfs_password .name ],
33- service_name = kwargs .get (StdParams .bucketfs_name .name ),
34- bucket_name = kwargs [StdParams .bucket .name ],
35- verify = kwargs .get (StdParams .use_ssl_cert_validation .name , True ),
36- path = path_in_bucket
37- )
27+ bfs_params ):
28+ return _Backend .onprem
3829 elif check_params ([StdParams .saas_url , StdParams .saas_account_id , StdParams .saas_token ,
39- [StdParams .saas_database_id , StdParams .saas_database_name ]], kwargs ):
40- saas_url = kwargs [StdParams .saas_url .name ]
41- saas_account_id = kwargs [StdParams .saas_account_id .name ]
42- saas_token = kwargs [StdParams .saas_token .name ]
43- saas_database_id = (kwargs .get (StdParams .saas_database_id .name ) or
44- get_database_id (
45- host = saas_url ,
46- account_id = saas_account_id ,
47- pat = saas_token ,
48- database_name = kwargs [StdParams .saas_database_name .name ]
49- ))
50- return bfs .path .build_path (backend = bfs .path .StorageBackend .saas ,
51- url = saas_url ,
52- account_id = saas_account_id ,
53- database_id = saas_database_id ,
54- pat = saas_token ,
55- path = path_in_bucket )
30+ [StdParams .saas_database_id , StdParams .saas_database_name ]], bfs_params ):
31+ return _Backend .saas
5632
5733 raise ValueError (
5834 'Incomplete parameter list. Please either provide the parameters ['
@@ -64,3 +40,175 @@ def create_bucketfs_location(**kwargs) -> bfs.path.PathLike:
6440 f'{ StdParams .saas_database_name .name } , { StdParams .saas_token .name } ] for a '
6541 'SaaS database.'
6642 )
43+
44+
45+ def _convert_onprem_bfs_params (bfs_params : dict [str , Any ]) -> dict [str , Any ]:
46+ """
47+ Converts OnPrem BucketFS parameters from the CLI format to the format expected
48+ by the exasol.bucketfs.path.build_path.
49+ """
50+
51+ net_service = ('https' if bfs_params .get (StdParams .bucketfs_use_https .name , True )
52+ else 'http' )
53+ url = (f"{ net_service } ://"
54+ f"{ bfs_params [StdParams .bucketfs_host .name ]} :"
55+ f"{ bfs_params [StdParams .bucketfs_port .name ]} " )
56+ return {
57+ 'backend' : bfs .path .StorageBackend .onprem .name ,
58+ 'url' : url ,
59+ 'username' : bfs_params [StdParams .bucketfs_user .name ],
60+ 'password' : bfs_params [StdParams .bucketfs_password .name ],
61+ 'service_name' : bfs_params .get (StdParams .bucketfs_name .name ),
62+ 'bucket_name' : bfs_params [StdParams .bucket .name ],
63+ 'verify' : bfs_params .get (StdParams .use_ssl_cert_validation .name , True ),
64+ 'path' : bfs_params .get (StdParams .path_in_bucket .name , '' )
65+ }
66+
67+
68+ def _convert_saas_bfs_params (bfs_params : dict [str , Any ]) -> dict [str , Any ]:
69+ """
70+ Converts SaaS BucketFS parameters from the CLI format to the format expected
71+ by the exasol.bucketfs.path.build_path.
72+ """
73+
74+ saas_url = bfs_params [StdParams .saas_url .name ]
75+ saas_account_id = bfs_params [StdParams .saas_account_id .name ]
76+ saas_token = bfs_params [StdParams .saas_token .name ]
77+ saas_database_id = (bfs_params .get (StdParams .saas_database_id .name ) or
78+ get_database_id (
79+ host = saas_url ,
80+ account_id = saas_account_id ,
81+ pat = saas_token ,
82+ database_name = bfs_params [StdParams .saas_database_name .name ]
83+ ))
84+ return {
85+ 'backend' : bfs .path .StorageBackend .saas .name ,
86+ 'url' : saas_url ,
87+ 'account_id' : saas_account_id ,
88+ 'database_id' : saas_database_id ,
89+ 'pat' : saas_token ,
90+ 'path' : bfs_params .get (StdParams .path_in_bucket .name , '' )
91+ }
92+
93+
94+ def create_bucketfs_location (** kwargs ) -> bfs .path .PathLike :
95+ """
96+ Creates a BucketFS PathLike object using the data provided in the kwargs. These
97+ can be parameters for the BucketFS at either On-Prem or SaaS database. The input
98+ parameters should correspond to the CLI options defined in the cli/std_options.py.
99+
100+ Raises a ValueError if the provided parameters are insufficient for either
101+ On-Prem or SaaS cases.
102+ """
103+
104+ db_type = _infer_backend (kwargs )
105+ if db_type == _Backend .onprem :
106+ return bfs .path .build_path (** _convert_onprem_bfs_params (kwargs ))
107+ else :
108+ return bfs .path .build_path (** _convert_saas_bfs_params (kwargs ))
109+
110+
111+ @dataclass
112+ class ConnectionInfo :
113+ """
114+ This is not a connection object. It's just a structure to keep together the data
115+ required for creating a BucketFs connection object. Useful for testing.
116+ """
117+ address : str
118+ user : str
119+ password : str
120+
121+
122+ def _to_json_str (bucketfs_params : dict [str , Any ], selected : list [str ]) -> str :
123+ filtered_kwargs = {k : v for k , v in bucketfs_params .items ()
124+ if (k in selected ) and (v is not None )}
125+ return json .dumps (filtered_kwargs )
126+
127+
128+ def write_bucketfs_conn_object (pyexasol_connection : pyexasol .ExaConnection ,
129+ conn_name : str ,
130+ conn_obj : ConnectionInfo ) -> None :
131+
132+ query = (f"CREATE OR REPLACE CONNECTION { conn_name } "
133+ f"TO '{ conn_obj .address } ' "
134+ f"USER '{ conn_obj .user } ' "
135+ f"IDENTIFIED BY '{ conn_obj .password } '" )
136+ pyexasol_connection .execute (query )
137+
138+
139+ def create_bucketfs_conn_object_onprem (pyexasol_connection : pyexasol .ExaConnection ,
140+ conn_name : str ,
141+ bucketfs_params : dict [str , Any ]) -> None :
142+ """
143+ Creates in the database a connection object encapsulating the BucketFS parameters
144+ for an OnPrem backend.
145+
146+ Parameters:
147+ pyexasol_connection:
148+ DB connection.
149+ conn_name:
150+ Name for the connection object.
151+ bucketfs_params:
152+ OnPrem BucketFS parameters in the format of the exasol.bucketfs.path.build_path.
153+ """
154+ conn_to = _to_json_str (bucketfs_params , [
155+ 'backend' , 'url' , 'service_name' , 'bucket_name' , 'path' , 'verify' ])
156+ conn_user = _to_json_str (bucketfs_params , ['username' ])
157+ conn_password = _to_json_str (bucketfs_params , ['password' ])
158+
159+ write_bucketfs_conn_object (pyexasol_connection , conn_name ,
160+ ConnectionInfo (conn_to , conn_user , conn_password ))
161+
162+
163+ def create_bucketfs_conn_object_saas (pyexasol_connection : pyexasol .ExaConnection ,
164+ conn_name : str ,
165+ bucketfs_params : dict [str , Any ]) -> None :
166+ """
167+ Creates in the database a connection object encapsulating the BucketFS parameters
168+ for a SaaS backend.
169+
170+ Parameters:
171+ pyexasol_connection:
172+ DB connection.
173+ conn_name:
174+ Name for the connection object.
175+ bucketfs_params:
176+ SaaS BucketFS parameters in the format of the exasol.bucketfs.path.build_path.
177+ """
178+ conn_to = _to_json_str (bucketfs_params , ['backend' , 'url' , 'path' ])
179+ conn_user = _to_json_str (bucketfs_params , ['account_id' , 'database_id' ])
180+ conn_password = _to_json_str (bucketfs_params , ['pat' ])
181+
182+ write_bucketfs_conn_object (pyexasol_connection , conn_name ,
183+ ConnectionInfo (conn_to , conn_user , conn_password ))
184+
185+
186+ def create_bucketfs_conn_object (conn_name : str , ** kwargs ) -> None :
187+ """
188+ Creates in the database a connection object encapsulating the provided BucketFS
189+ parameters. These can be parameters for either On-Prem or SaaS database. They
190+ should correspond to the CLI options defined in the cli/std_options.py.
191+
192+ Raises a ValueError if the provided parameters are insufficient for either
193+ On-Prem or SaaS cases.
194+ """
195+ with open_pyexasol_connection (** kwargs ) as pyexasol_connection :
196+ db_type = _infer_backend (kwargs )
197+ if db_type == _Backend .onprem :
198+ create_bucketfs_conn_object_onprem (pyexasol_connection , conn_name ,
199+ _convert_onprem_bfs_params (kwargs ))
200+ else :
201+ create_bucketfs_conn_object_saas (pyexasol_connection , conn_name ,
202+ _convert_saas_bfs_params (kwargs ))
203+
204+
205+ def create_bucketfs_location_from_conn_object (conn_obj ) -> bfs .path .PathLike :
206+ """
207+ Creates a BucketFS PathLike object using data contained in the provided connection
208+ object.
209+ """
210+
211+ bfs_params = json .loads (conn_obj .address )
212+ bfs_params .update (json .loads (conn_obj .user ))
213+ bfs_params .update (json .loads (conn_obj .password ))
214+ return bfs .path .build_path (** bfs_params )
0 commit comments