11import asyncio
22import contextlib
33import logging
4+ import os
45import re
56import time
67from typing import Dict , Optional , Tuple
1617)
1718from ._logging import LogHandlerSumo
1819from ._retry_strategy import RetryStrategy
19- from .config import APP_REGISTRATION , AUTHORITY_HOST_URI , TENANT_ID
2020
2121logger = logging .getLogger ("sumo.wrapper" )
2222
2323DEFAULT_TIMEOUT = httpx .Timeout (30.0 )
2424
25+ WELL_KNOWN = os .environ .get (
26+ "SUMOCONNECTIONINFO" , "https://api.sumo.equinor.com/well-known"
27+ )
28+
2529
2630class SumoClient :
2731 """Authenticate and perform requests to the Sumo API."""
@@ -31,7 +35,7 @@ class SumoClient:
3135
3236 def __init__ (
3337 self ,
34- env : str ,
38+ env : str = "prod" ,
3539 token : Optional [str ] = None ,
3640 interactive : bool = True ,
3741 devicecode : bool = False ,
@@ -41,22 +45,46 @@ def __init__(
4145 case_uuid = None ,
4246 http_client = None ,
4347 async_http_client = None ,
48+ client_id : Optional [str ] = None ,
4449 ):
4550 """Initialize a new Sumo object
4651
4752 Args:
48- env: Sumo environment
49- token: Access token or refresh token.
50- interactive: Enable interactive authentication (in browser).
51- If not enabled, code grant flow will be used.
52- verbosity: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
53+ env (str): Sumo environment. Defaults to "prod".
54+ token (Optional[str]): Access token or refresh token. Defaults to None.
55+ interactive (bool): Enable interactive authentication (in browser).
56+ If not enabled, code grant flow will be used. Defaults to True.
57+ devicecode (bool): Enable device code flow. Defaults to False.
58+ verbosity (str): Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL).
59+ Defaults to "CRITICAL".
60+ retry_strategy (RetryStrategy): Retry strategy for HTTP requests.
61+ Defaults to RetryStrategy().
62+ timeout (int): Timeout for HTTP requests. Defaults to DEFAULT_TIMEOUT.
63+ case_uuid (Optional[str]): Case UUID for authentication. Defaults to None.
64+ http_client (Optional[httpx.Client]): HTTP client for synchronous requests.
65+ Defaults to None.
66+ async_http_client (Optional[httpx.AsyncClient]): HTTP client for asynchronous requests.
67+ Defaults to None.
68+ client_id (Optional[str]): Client ID for authentication. If None, will use
69+ AZURE_CLIENT_ID from environment variables or the config. Defaults to None.
5370 """
5471
5572 logger .setLevel (verbosity )
5673
57- if env not in APP_REGISTRATION :
74+ well_known = httpx .get (WELL_KNOWN ).json ()
75+ if env not in well_known ["envs" ]:
5876 raise ValueError (f"Invalid environment: { env } " )
5977
78+ tenant_id = well_known ["tenant_id" ]
79+ authority_host = well_known ["authority" ]
80+ config = well_known ["envs" ][env ]
81+ resource_id = config ["resource_id" ]
82+ base_url = config ["base_url" ]
83+ self .client_id = (
84+ client_id
85+ or os .environ .get ("AZURE_CLIENT_ID" )
86+ or config ["client_id" ]
87+ )
6088 self .env = env
6189 self ._verbosity = verbosity
6290
@@ -100,49 +128,19 @@ def __init__(
100128 pass
101129 pass
102130
103- if env == "prod" :
104- self .base_url = "https://api.sumo.equinor.com/api/v1"
105- elif env == "localhost" :
106- self .base_url = "http://localhost:8084/api/v1"
107- else :
108- self .base_url = (
109- f"https://main-sumo-core-{ env } .c3.radix.equinor.com/api/v1"
110- )
111131 cleanup_shared_keys ()
132+ self .auth = get_auth_provider (
133+ client_id = self .client_id ,
134+ authority = f"{ authority_host } { tenant_id } " ,
135+ resource_id = resource_id ,
136+ interactive = interactive ,
137+ refresh_token = refresh_token ,
138+ access_token = access_token ,
139+ devicecode = devicecode ,
140+ case_uuid = case_uuid ,
141+ )
112142
113- def _get_auth_provider ():
114- return get_auth_provider (
115- client_id = APP_REGISTRATION [env ]["CLIENT_ID" ],
116- authority = f"{ AUTHORITY_HOST_URI } /{ TENANT_ID } " ,
117- resource_id = APP_REGISTRATION [env ]["RESOURCE_ID" ],
118- interactive = interactive ,
119- refresh_token = refresh_token ,
120- access_token = access_token ,
121- devicecode = devicecode ,
122- case_uuid = case_uuid ,
123- )
124-
125- def _try_setup_auth_provider ():
126- self .auth = _get_auth_provider ()
127- response = httpx .get (
128- url = self .base_url + "/userpermissions" ,
129- headers = self .auth .get_authorization (),
130- )
131- if response .is_success :
132- return True , False
133-
134- elif response .status_code == 401 :
135- return False , self .auth .delete_token ()
136-
137- else :
138- raise httpx .HTTPStatusError
139-
140- ok , retry = _try_setup_auth_provider ()
141- if retry :
142- ok , retry = _try_setup_auth_provider ()
143- if retry :
144- ok , retry = _try_setup_auth_provider ()
145-
143+ self .base_url = base_url
146144 return
147145
148146 def __enter__ (self ):
0 commit comments