22Wrappers for adding custom properties to click parameters, e.g. SCS key.
33"""
44
5+ import getpass
6+ import os
7+ import re
8+ from abc import abstractmethod
9+ from typing import Any
10+
511import click
612
713from exasol .nb_connector .ai_lab_config import AILabConfig as CKey
14+ from exasol .nb_connector .cli import reporting as report
15+ from exasol .nb_connector .secret_store import Secrets
816
917
10- class ScsArgument :
18+ class ScsParam :
1119 """
12- Represents a CLI argument for the SCS command .
20+ Abstract base class for ScsArgument and ScsOption .
1321 """
1422
15- def __init__ (self , * args , scs_key : CKey | None = None , ** kwargs ):
16- self ._args = args
23+ def __init__ (self , scs_key : CKey | None = None , ** kwargs ):
1724 self .scs_key = scs_key
1825 self ._kwargs = kwargs
1926
27+ @property
28+ def arg_name (self ) -> str :
29+ return ""
30+
31+ def needs_entry (self , scs : Secrets ) -> bool :
32+ return False
33+
34+ @property
35+ def default (self ) -> Any :
36+ return self ._kwargs .get ("default" )
37+
38+ def displayed_value (self , scs : Secrets ) -> str | None :
39+ return None
40+
41+ @abstractmethod
42+ def decorate (self , func ):
43+ """
44+ This method is to be called when decorating the functions in the
45+ actual CLI declaration.
46+ """
47+ pass
48+
49+
50+ class ScsArgument (ScsParam ):
51+ """
52+ Represents a CLI argument for the SCS command.
53+ """
54+
55+ def __init__ (self , name : str , scs_key : CKey | None = None , ** kwargs ):
56+ super ().__init__ (scs_key , ** kwargs )
57+ self .name = name
58+
2059 def decorate (self , func ):
2160 """
2261 This method is to be called when decorating the functions in the
2362 actual CLI declaration. Hence, ScsArgument calls click.argument()
2463 under the hood.
2564 """
26- decorator = click .argument (* self ._args , ** self ._kwargs )
65+ decorator = click .argument (self .name , ** self ._kwargs )
2766 return decorator (func )
2867
2968
30- class ScsOption (ScsArgument ):
69+ class ScsOption (ScsParam ):
3170 """
3271 CLI option for saving and checking values to the Secure Configuration
3372 Storage (SCS).
@@ -53,30 +92,70 @@ class ScsOption(ScsArgument):
5392
5493 def __init__ (
5594 self ,
95+ cli_option ,
5696 * args ,
5797 scs_key : CKey | None = None ,
5898 scs_alternative_key : CKey | None = None ,
5999 scs_required : bool = True ,
60100 get_default_from : str | None = None ,
61101 ** kwargs ,
62102 ):
63- super ().__init__ (* args , scs_key = scs_key , ** kwargs )
103+ super ().__init__ (scs_key = scs_key , ** kwargs )
104+ self ._cli_option = cli_option
105+ self ._args = args
64106 self .scs_alternative_key = scs_alternative_key
65107 self .scs_required = scs_required
66108 self .get_default_from = get_default_from
67109
110+ def cli_option (self , full = False ) -> str :
111+ raw = self ._cli_option
112+ return raw if full else re .sub (r"/--.*$" , "" , raw )
113+
114+ @property
115+ def arg_name (self ) -> str :
116+ for arg in self ._args :
117+ if not arg .startswith ("--" ):
118+ return arg
119+ name = self .cli_option ()
120+ return name [2 :].replace ("-" , "_" )
121+
68122 def decorate (self , func ):
69123 """
70124 This method is to be called when decorating the functions in the
71125 actual CLI declaration. ScsOption calls click.option().
72126 """
73127 decorator = click .option (
128+ self ._cli_option ,
74129 * self ._args ,
75130 ** self ._kwargs ,
76131 show_default = True ,
77132 )
78133 return decorator (func )
79134
135+ def displayed_value (self , scs : Secrets ) -> str | None :
136+ return scs .get (self .scs_key ) if self .scs_key else None
137+
138+ def needs_entry (self , scs : Secrets ) -> bool :
139+ """
140+ Return True, if the current option is configured to be saved to
141+ the SCS but SCS does not yet contain a value.
142+ """
143+
144+ def has_value () -> bool :
145+ if not self .scs_key :
146+ return False
147+ if scs .get (self .scs_key ) is not None :
148+ return True
149+ if alt := self .scs_alternative_key :
150+ return scs .get (alt ) is not None
151+ return False
152+
153+ return bool (self .scs_key ) and self .scs_required and not has_value ()
154+
155+ def __repr__ (self ) -> str :
156+ cls_name = type (self ).__name__
157+ return f"{ cls_name } <{ self .cli_option (full = True )} >"
158+
80159
81160class ScsSecretOption (ScsOption ):
82161 """
@@ -107,6 +186,22 @@ def __init__(
107186 self .prompt = prompt
108187 self .name = name
109188
189+ def displayed_value (self , scs : Secrets ) -> str | None :
190+ return "****" if self .scs_key and scs .get (self .scs_key ) else None
191+
192+ def get_secret (self , interactive : Any ) -> str :
193+ """
194+ If interactive is True and the related environment variable is not
195+ set then ask for the secret interactively.
196+ """
197+ if value := os .getenv (self .envvar ):
198+ report .info (f"Reading { self .name } from environment variable { self .envvar } ." )
199+ return value
200+ if not interactive :
201+ return ""
202+ prompt = f"{ self .prompt } (option { self .name } ): "
203+ return getpass .getpass (prompt )
204+
110205
111206def add_params (scs_options : list [ScsArgument ]):
112207 def multi_decorator (func ):
0 commit comments