Skip to content

base

base

Base authentication classes for Domo authentication.

DomoAuth

Bases: ABC

Abstract contract for Domo authentication implementations.

All concrete auth classes must inherit from this class and provide the required authentication surface used by routes and entities.

auth_header abstractmethod property

auth_header: dict

Generate authentication headers for API requests.

url_manual_login property

url_manual_login: str

Generate the manual login URL for the Domo instance.

cache_stats

cache_stats() -> dict

Get HTTP cache statistics.

Source code in src/crew_dcs/auth/base.py
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def cache_stats(self) -> dict:
    """Get HTTP cache statistics."""
    if hasattr(self, "_cached_session") and self._cached_session:
        transport = getattr(self._cached_session, "_transport", None)
        if transport and hasattr(transport, "get_stats"):
            return transport.get_stats()

    return {
        "request_cache": {
            "total_entries": 0,
            "hits": 0,
            "misses": 0,
            "hit_rate": 0.0,
        },
        "collection_cache": {
            "total_entries": 0,
            "hits": 0,
            "misses": 0,
            "hit_rate": 0.0,
        },
    }

clear_cache

clear_cache() -> None

Clear all HTTP caches for this auth instance.

Source code in src/crew_dcs/auth/base.py
198
199
200
201
202
203
def clear_cache(self) -> None:
    """Clear all HTTP caches for this auth instance."""
    if hasattr(self, "_cached_session") and self._cached_session:
        transport = getattr(self._cached_session, "_transport", None)
        if transport and hasattr(transport, "clear_cache"):
            transport.clear_cache()

close_session async

close_session() -> None

Close and drop the pooled session cached on this auth, if any.

A reused session is otherwise held open until the auth is GC'd. Call this (or use the auth as an async context manager) for clean teardown::

async with DomoTokenAuth(...) as auth:
    await DomoCard.create(auth=auth, ...)   # reuses one pooled session
# session closed here
Source code in src/crew_dcs/auth/base.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
async def close_session(self) -> None:
    """Close and drop the pooled session cached on this auth, if any.

    A reused session is otherwise held open until the auth is GC'd. Call this
    (or use the auth as an async context manager) for clean teardown::

        async with DomoTokenAuth(...) as auth:
            await DomoCard.create(auth=auth, ...)   # reuses one pooled session
        # session closed here
    """
    session = getattr(self, "_cached_session", None)
    if session is not None:
        try:
            if not getattr(session, "is_closed", False):
                await session.aclose()
        finally:
            self._cached_session = None

elevate_otp async

elevate_otp(
    one_time_password: str,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    debug_num_stacks_to_drop: int = 2,
) -> ResponseGetData

Elevate the authentication to include OTP (One-Time Password) if required.

Source code in src/crew_dcs/auth/base.py
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
@log_call(
    level_name="auth",
    config=LogDecoratorConfig(
        entity_extractor=DomoEntityExtractor(),
        result_processor=DomoEntityResultProcessor(),
    ),
)
async def elevate_otp(
    self,
    one_time_password: str,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    debug_num_stacks_to_drop: int = 2,
) -> ResponseGetData:
    """Elevate the authentication to include OTP (One-Time Password) if required."""
    if session is None:
        async with httpx.AsyncClient() as client_session:
            from ..routes import auth as auth_routes

            return await auth_routes.elevate_user_otp(
                auth=self,
                debug_api=debug_api,
                session=client_session,
                one_time_password=one_time_password,
                debug_num_stacks_to_drop=debug_num_stacks_to_drop,
            )
    from ..routes import auth as auth_routes

    return await auth_routes.elevate_user_otp(
        auth=self,
        debug_api=debug_api,
        session=session,
        one_time_password=one_time_password,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop,
    )

get_auth_token abstractmethod async

get_auth_token(**context_kwargs) -> str | ResponseGetData

Retrieve or generate an authentication token.

Source code in src/crew_dcs/auth/base.py
 97
 98
 99
100
101
102
103
@abstractmethod
async def get_auth_token(
    self,
    **context_kwargs,
) -> str | ResponseGetData:
    """Retrieve or generate an authentication token."""
    raise NotImplementedError("Subclasses must implement get_auth_token.")

invalidate_cache async

invalidate_cache(url_pattern: str) -> None

Manually invalidate cache entries matching URL pattern.

Source code in src/crew_dcs/auth/base.py
227
228
229
230
231
232
async def invalidate_cache(self, url_pattern: str) -> None:
    """Manually invalidate cache entries matching URL pattern."""
    if hasattr(self, "_cached_session") and self._cached_session:
        transport = getattr(self._cached_session, "_transport", None)
        if transport and hasattr(transport, "_invalidate_by_url"):
            await transport._invalidate_by_url(url_pattern)

print_is_token async

print_is_token(
    token_name: str | None = None, **context_kwargs
) -> bool

Print token status and return True if token is valid, otherwise False.

Source code in src/crew_dcs/auth/base.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
async def print_is_token(
    self,
    token_name: str | None = None,
    **context_kwargs,
) -> bool:
    """Print token status and return True if token is valid, otherwise False."""
    self.token_name = token_name or self.token_name

    if not self.token:
        await self.get_auth_token(**context_kwargs)

    if not self.is_valid_token:
        await self.who_am_i(**context_kwargs)

    if not self.is_valid_token:
        print(
            f"🚧 failed to retrieve {self.token_name} token from {self.domo_instance}"
        )
        return False

    try:
        print(f"🎉 {self.token_name} token retrieved from {self.domo_instance} ⚙️")
    except UnicodeEncodeError:
        print(
            f"[SUCCESS] {self.token_name} token retrieved from {self.domo_instance}"
        )
    return True

set_cache_strategy

set_cache_strategy(strategy) -> None

Change cache invalidation strategy.

Source code in src/crew_dcs/auth/base.py
234
235
236
237
238
239
def set_cache_strategy(self, strategy) -> None:
    """Change cache invalidation strategy."""
    if hasattr(self, "_cached_session") and self._cached_session:
        transport = getattr(self._cached_session, "_transport", None)
        if transport and hasattr(transport, "invalidator"):
            transport.invalidator.strategy = strategy

who_am_i async

who_am_i(**context_kwargs) -> ResponseGetData

Perform an API call to identify the user associated with the token.

Source code in src/crew_dcs/auth/base.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
@log_call(
    level_name="auth",
    config=LogDecoratorConfig(
        entity_extractor=DomoEntityExtractor(),
        result_processor=DomoEntityResultProcessor(),
    ),
    color="cyan",
)
async def who_am_i(
    self,
    **context_kwargs,
) -> ResponseGetData:
    """Perform an API call to identify the user associated with the token."""
    from ..routes import auth as auth_routes

    res = await auth_routes.who_am_i(
        auth=self,
        parent_class=self.__class__.__name__,
        **context_kwargs,
    )

    if res.is_success:
        self.is_valid_token = True

    if res.response and isinstance(res.response, dict):
        self.user_id = res.response.get("id")

    return res