github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/clients/python/lakefs_sdk/configuration.py (about)

     1  # coding: utf-8
     2  
     3  """
     4      lakeFS API
     5  
     6      lakeFS HTTP API
     7  
     8      The version of the OpenAPI document: 1.0.0
     9      Contact: services@treeverse.io
    10      Generated by OpenAPI Generator (https://openapi-generator.tech)
    11  
    12      Do not edit the class manually.
    13  """  # noqa: E501
    14  
    15  
    16  import copy
    17  import logging
    18  import multiprocessing
    19  import sys
    20  import urllib3
    21  
    22  import http.client as httplib
    23  from lakefs_sdk.exceptions import ApiValueError
    24  
    25  JSON_SCHEMA_VALIDATION_KEYWORDS = {
    26      'multipleOf', 'maximum', 'exclusiveMaximum',
    27      'minimum', 'exclusiveMinimum', 'maxLength',
    28      'minLength', 'pattern', 'maxItems', 'minItems'
    29  }
    30  
    31  class Configuration(object):
    32      """This class contains various settings of the API client.
    33  
    34      :param host: Base url.
    35      :param api_key: Dict to store API key(s).
    36        Each entry in the dict specifies an API key.
    37        The dict key is the name of the security scheme in the OAS specification.
    38        The dict value is the API key secret.
    39      :param api_key_prefix: Dict to store API prefix (e.g. Bearer).
    40        The dict key is the name of the security scheme in the OAS specification.
    41        The dict value is an API key prefix when generating the auth data.
    42      :param username: Username for HTTP basic authentication.
    43      :param password: Password for HTTP basic authentication.
    44      :param access_token: Access token.
    45      :param server_index: Index to servers configuration.
    46      :param server_variables: Mapping with string values to replace variables in
    47        templated server configuration. The validation of enums is performed for
    48        variables with defined enum values before.
    49      :param server_operation_index: Mapping from operation ID to an index to server
    50        configuration.
    51      :param server_operation_variables: Mapping from operation ID to a mapping with
    52        string values to replace variables in templated server configuration.
    53        The validation of enums is performed for variables with defined enum values before.
    54      :param ssl_ca_cert: str - the path to a file of concatenated CA certificates
    55        in PEM format.
    56  
    57      :Example:
    58  
    59      API Key Authentication Example.
    60      Given the following security scheme in the OpenAPI specification:
    61        components:
    62          securitySchemes:
    63            cookieAuth:         # name for the security scheme
    64              type: apiKey
    65              in: cookie
    66              name: JSESSIONID  # cookie name
    67  
    68      You can programmatically set the cookie:
    69  
    70  conf = lakefs_sdk.Configuration(
    71      api_key={'cookieAuth': 'abc123'}
    72      api_key_prefix={'cookieAuth': 'JSESSIONID'}
    73  )
    74  
    75      The following cookie will be added to the HTTP request:
    76         Cookie: JSESSIONID abc123
    77  
    78      HTTP Basic Authentication Example.
    79      Given the following security scheme in the OpenAPI specification:
    80        components:
    81          securitySchemes:
    82            http_basic_auth:
    83              type: http
    84              scheme: basic
    85  
    86      Configure API client with HTTP basic authentication:
    87  
    88  conf = lakefs_sdk.Configuration(
    89      username='the-user',
    90      password='the-password',
    91  )
    92  
    93      """
    94  
    95      _default = None
    96  
    97      def __init__(self, host=None,
    98                   api_key=None, api_key_prefix=None,
    99                   username=None, password=None,
   100                   access_token=None,
   101                   server_index=None, server_variables=None,
   102                   server_operation_index=None, server_operation_variables=None,
   103                   ssl_ca_cert=None,
   104                   ):
   105          """Constructor
   106          """
   107          self._base_path = "/api/v1" if host is None else host
   108          """Default Base url
   109          """
   110          self.server_index = 0 if server_index is None and host is None else server_index
   111          self.server_operation_index = server_operation_index or {}
   112          """Default server index
   113          """
   114          self.server_variables = server_variables or {}
   115          self.server_operation_variables = server_operation_variables or {}
   116          """Default server variables
   117          """
   118          self.temp_folder_path = None
   119          """Temp file folder for downloading files
   120          """
   121          # Authentication Settings
   122          self.api_key = {}
   123          if api_key:
   124              self.api_key = api_key
   125          """dict to store API key(s)
   126          """
   127          self.api_key_prefix = {}
   128          if api_key_prefix:
   129              self.api_key_prefix = api_key_prefix
   130          """dict to store API prefix (e.g. Bearer)
   131          """
   132          self.refresh_api_key_hook = None
   133          """function hook to refresh API key if expired
   134          """
   135          self.username = username
   136          """Username for HTTP basic authentication
   137          """
   138          self.password = password
   139          """Password for HTTP basic authentication
   140          """
   141          self.access_token = access_token
   142          """Access token
   143          """
   144          self.logger = {}
   145          """Logging Settings
   146          """
   147          self.logger["package_logger"] = logging.getLogger("lakefs_sdk")
   148          self.logger["urllib3_logger"] = logging.getLogger("urllib3")
   149          self.logger_format = '%(asctime)s %(levelname)s %(message)s'
   150          """Log format
   151          """
   152          self.logger_stream_handler = None
   153          """Log stream handler
   154          """
   155          self.logger_file_handler = None
   156          """Log file handler
   157          """
   158          self.logger_file = None
   159          """Debug file location
   160          """
   161          self.debug = False
   162          """Debug switch
   163          """
   164  
   165          self.verify_ssl = True
   166          """SSL/TLS verification
   167             Set this to false to skip verifying SSL certificate when calling API
   168             from https server.
   169          """
   170          self.ssl_ca_cert = ssl_ca_cert
   171          """Set this to customize the certificate file to verify the peer.
   172          """
   173          self.cert_file = None
   174          """client certificate file
   175          """
   176          self.key_file = None
   177          """client key file
   178          """
   179          self.assert_hostname = None
   180          """Set this to True/False to enable/disable SSL hostname verification.
   181          """
   182          self.tls_server_name = None
   183          """SSL/TLS Server Name Indication (SNI)
   184             Set this to the SNI value expected by the server.
   185          """
   186  
   187          self.connection_pool_maxsize = multiprocessing.cpu_count() * 5
   188          """urllib3 connection pool's maximum number of connections saved
   189             per pool. urllib3 uses 1 connection as default value, but this is
   190             not the best value when you are making a lot of possibly parallel
   191             requests to the same host, which is often the case here.
   192             cpu_count * 5 is used as default value to increase performance.
   193          """
   194  
   195          self.proxy = None
   196          """Proxy URL
   197          """
   198          self.proxy_headers = None
   199          """Proxy headers
   200          """
   201          self.safe_chars_for_path_param = ''
   202          """Safe chars for path_param
   203          """
   204          self.retries = None
   205          """Adding retries to override urllib3 default value 3
   206          """
   207          # Enable client side validation
   208          self.client_side_validation = True
   209  
   210          self.socket_options = None
   211          """Options to pass down to the underlying urllib3 socket
   212          """
   213  
   214          self.datetime_format = "%Y-%m-%dT%H:%M:%S.%f%z"
   215          """datetime format
   216          """
   217  
   218          self.date_format = "%Y-%m-%d"
   219          """date format
   220          """
   221  
   222      def __deepcopy__(self, memo):
   223          cls = self.__class__
   224          result = cls.__new__(cls)
   225          memo[id(self)] = result
   226          for k, v in self.__dict__.items():
   227              if k not in ('logger', 'logger_file_handler'):
   228                  setattr(result, k, copy.deepcopy(v, memo))
   229          # shallow copy of loggers
   230          result.logger = copy.copy(self.logger)
   231          # use setters to configure loggers
   232          result.logger_file = self.logger_file
   233          result.debug = self.debug
   234          return result
   235  
   236      def __setattr__(self, name, value):
   237          object.__setattr__(self, name, value)
   238  
   239      @classmethod
   240      def set_default(cls, default):
   241          """Set default instance of configuration.
   242  
   243          It stores default configuration, which can be
   244          returned by get_default_copy method.
   245  
   246          :param default: object of Configuration
   247          """
   248          cls._default = default
   249  
   250      @classmethod
   251      def get_default_copy(cls):
   252          """Deprecated. Please use `get_default` instead.
   253  
   254          Deprecated. Please use `get_default` instead.
   255  
   256          :return: The configuration object.
   257          """
   258          return cls.get_default()
   259  
   260      @classmethod
   261      def get_default(cls):
   262          """Return the default configuration.
   263  
   264          This method returns newly created, based on default constructor,
   265          object of Configuration class or returns a copy of default
   266          configuration.
   267  
   268          :return: The configuration object.
   269          """
   270          if cls._default is None:
   271              cls._default = Configuration()
   272          return cls._default
   273  
   274      @property
   275      def logger_file(self):
   276          """The logger file.
   277  
   278          If the logger_file is None, then add stream handler and remove file
   279          handler. Otherwise, add file handler and remove stream handler.
   280  
   281          :param value: The logger_file path.
   282          :type: str
   283          """
   284          return self.__logger_file
   285  
   286      @logger_file.setter
   287      def logger_file(self, value):
   288          """The logger file.
   289  
   290          If the logger_file is None, then add stream handler and remove file
   291          handler. Otherwise, add file handler and remove stream handler.
   292  
   293          :param value: The logger_file path.
   294          :type: str
   295          """
   296          self.__logger_file = value
   297          if self.__logger_file:
   298              # If set logging file,
   299              # then add file handler and remove stream handler.
   300              self.logger_file_handler = logging.FileHandler(self.__logger_file)
   301              self.logger_file_handler.setFormatter(self.logger_formatter)
   302              for _, logger in self.logger.items():
   303                  logger.addHandler(self.logger_file_handler)
   304  
   305      @property
   306      def debug(self):
   307          """Debug status
   308  
   309          :param value: The debug status, True or False.
   310          :type: bool
   311          """
   312          return self.__debug
   313  
   314      @debug.setter
   315      def debug(self, value):
   316          """Debug status
   317  
   318          :param value: The debug status, True or False.
   319          :type: bool
   320          """
   321          self.__debug = value
   322          if self.__debug:
   323              # if debug status is True, turn on debug logging
   324              for _, logger in self.logger.items():
   325                  logger.setLevel(logging.DEBUG)
   326              # turn on httplib debug
   327              httplib.HTTPConnection.debuglevel = 1
   328          else:
   329              # if debug status is False, turn off debug logging,
   330              # setting log level to default `logging.WARNING`
   331              for _, logger in self.logger.items():
   332                  logger.setLevel(logging.WARNING)
   333              # turn off httplib debug
   334              httplib.HTTPConnection.debuglevel = 0
   335  
   336      @property
   337      def logger_format(self):
   338          """The logger format.
   339  
   340          The logger_formatter will be updated when sets logger_format.
   341  
   342          :param value: The format string.
   343          :type: str
   344          """
   345          return self.__logger_format
   346  
   347      @logger_format.setter
   348      def logger_format(self, value):
   349          """The logger format.
   350  
   351          The logger_formatter will be updated when sets logger_format.
   352  
   353          :param value: The format string.
   354          :type: str
   355          """
   356          self.__logger_format = value
   357          self.logger_formatter = logging.Formatter(self.__logger_format)
   358  
   359      def get_api_key_with_prefix(self, identifier, alias=None):
   360          """Gets API key (with prefix if set).
   361  
   362          :param identifier: The identifier of apiKey.
   363          :param alias: The alternative identifier of apiKey.
   364          :return: The token for api key authentication.
   365          """
   366          if self.refresh_api_key_hook is not None:
   367              self.refresh_api_key_hook(self)
   368          key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None)
   369          if key:
   370              prefix = self.api_key_prefix.get(identifier)
   371              if prefix:
   372                  return "%s %s" % (prefix, key)
   373              else:
   374                  return key
   375  
   376      def get_basic_auth_token(self):
   377          """Gets HTTP basic authentication header (string).
   378  
   379          :return: The token for basic HTTP authentication.
   380          """
   381          username = ""
   382          if self.username is not None:
   383              username = self.username
   384          password = ""
   385          if self.password is not None:
   386              password = self.password
   387          return urllib3.util.make_headers(
   388              basic_auth=username + ':' + password
   389          ).get('authorization')
   390  
   391      def auth_settings(self):
   392          """Gets Auth Settings dict for api client.
   393  
   394          :return: The Auth Settings information dict.
   395          """
   396          auth = {}
   397          if self.username is not None and self.password is not None:
   398              auth['basic_auth'] = {
   399                  'type': 'basic',
   400                  'in': 'header',
   401                  'key': 'Authorization',
   402                  'value': self.get_basic_auth_token()
   403              }
   404          if self.access_token is not None:
   405              auth['jwt_token'] = {
   406                  'type': 'bearer',
   407                  'in': 'header',
   408                  'format': 'JWT',
   409                  'key': 'Authorization',
   410                  'value': 'Bearer ' + self.access_token
   411              }
   412          if 'cookie_auth' in self.api_key:
   413              auth['cookie_auth'] = {
   414                  'type': 'api_key',
   415                  'in': 'cookie',
   416                  'key': 'internal_auth_session',
   417                  'value': self.get_api_key_with_prefix(
   418                      'cookie_auth',
   419                  ),
   420              }
   421          if 'oidc_auth' in self.api_key:
   422              auth['oidc_auth'] = {
   423                  'type': 'api_key',
   424                  'in': 'cookie',
   425                  'key': 'oidc_auth_session',
   426                  'value': self.get_api_key_with_prefix(
   427                      'oidc_auth',
   428                  ),
   429              }
   430          if 'saml_auth' in self.api_key:
   431              auth['saml_auth'] = {
   432                  'type': 'api_key',
   433                  'in': 'cookie',
   434                  'key': 'saml_auth_session',
   435                  'value': self.get_api_key_with_prefix(
   436                      'saml_auth',
   437                  ),
   438              }
   439          return auth
   440  
   441      def to_debug_report(self):
   442          """Gets the essential information for debugging.
   443  
   444          :return: The report for debugging.
   445          """
   446          return "Python SDK Debug Report:\n"\
   447                 "OS: {env}\n"\
   448                 "Python Version: {pyversion}\n"\
   449                 "Version of the API: 1.0.0\n"\
   450                 "SDK Package Version: 0.1.0-SNAPSHOT".\
   451                 format(env=sys.platform, pyversion=sys.version)
   452  
   453      def get_host_settings(self):
   454          """Gets an array of host settings
   455  
   456          :return: An array of host settings
   457          """
   458          return [
   459              {
   460                  'url': "/api/v1",
   461                  'description': "lakeFS server endpoint",
   462              }
   463          ]
   464  
   465      def get_host_from_settings(self, index, variables=None, servers=None):
   466          """Gets host URL based on the index and variables
   467          :param index: array index of the host settings
   468          :param variables: hash of variable and the corresponding value
   469          :param servers: an array of host settings or None
   470          :return: URL based on host settings
   471          """
   472          if index is None:
   473              return self._base_path
   474  
   475          variables = {} if variables is None else variables
   476          servers = self.get_host_settings() if servers is None else servers
   477  
   478          try:
   479              server = servers[index]
   480          except IndexError:
   481              raise ValueError(
   482                  "Invalid index {0} when selecting the host settings. "
   483                  "Must be less than {1}".format(index, len(servers)))
   484  
   485          url = server['url']
   486  
   487          # go through variables and replace placeholders
   488          for variable_name, variable in server.get('variables', {}).items():
   489              used_value = variables.get(
   490                  variable_name, variable['default_value'])
   491  
   492              if 'enum_values' in variable \
   493                      and used_value not in variable['enum_values']:
   494                  raise ValueError(
   495                      "The variable `{0}` in the host URL has invalid value "
   496                      "{1}. Must be {2}.".format(
   497                          variable_name, variables[variable_name],
   498                          variable['enum_values']))
   499  
   500              url = url.replace("{" + variable_name + "}", used_value)
   501  
   502          return url
   503  
   504      @property
   505      def host(self):
   506          """Return generated host."""
   507          return self.get_host_from_settings(self.server_index, variables=self.server_variables)
   508  
   509      @host.setter
   510      def host(self, value):
   511          """Fix base path."""
   512          self._base_path = value
   513          self.server_index = None