terra.api.api_responses

  1#  Copyright 2022 Terra Enabling Developers Limited
  2#
  3#  Licensed under the Apache License, Version 2.0 (the "License");
  4#  you may not use this file except in compliance with the License.
  5#  You may obtain a copy of the License at
  6#
  7#      http://www.apache.org/licenses/LICENSE-2.0
  8#
  9#  Unless required by applicable law or agreed to in writing, software
 10#  distributed under the License is distributed on an "AS IS" BASIS,
 11#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12#  See the License for the specific language governing permissions and
 13#  limitations under the License.
 14from __future__ import annotations
 15
 16__all__ = [
 17    "NoDataReturned",
 18    "DataReturned",
 19    "AuthenticationFailed",
 20    "TerraParsedApiResponse",
 21    "TerraWebhookResponse",
 22    "TerraApiResponse",
 23    "GenericMessage",
 24    "GoogleNoDataSourceHookResponse",
 25    "WidgetSession",
 26    "SentToWebhook",
 27    "SubscribedUsers",
 28    "UserDeauthResp",
 29    "AuthHookResponse",
 30    "AccessRevokedHookResponse",
 31    "UserAuthUrl",
 32    "UserDeauthHookResponse",
 33    "UserReauthHookResponse",
 34    "ConnectionDegraded",
 35    "ProvidersResponse",
 36    "RequestProcessingHookResponse",
 37    "ConnectionErrorHookResponse",
 38    "HookResponse",
 39    "RequestCompletedHookResponse",
 40    "UserInfo",
 41]
 42
 43import dataclasses
 44import json.decoder
 45import typing
 46
 47import requests
 48
 49from terra import exceptions
 50from terra import models
 51from terra.models import base_model
 52from terra.models import user as user_
 53
 54if typing.TYPE_CHECKING:
 55    from terra.base_client import Terra
 56
 57# TODO - should use a mixin/trait here instead of a redundant subclass probably
 58class TerraParsedApiResponse(base_model.TerraDataModel):
 59    pass
 60
 61
 62def _parse_api_body(
 63    dtype: typing.Optional[str],
 64    body: typing.Optional[typing.Dict[str, typing.Any]],
 65    user: typing.Optional[models.user.User],
 66) -> TerraParsedApiResponse:
 67
 68    if not body:
 69        raise exceptions.NoBodyException
 70
 71    Auser = user
 72    if "user" in body:
 73        Auser = models.user.User.from_dict(body["user"])
 74
 75    if ("status" in body) and (body["status"] in STATUS.keys()):
 76        response = STATUS[body["status"]]().from_dict(body)
 77
 78    elif dtype in USER_DATATYPES:
 79
 80        if not dtype:
 81            raise exceptions.NoDtypeException
 82
 83        return DataReturned(
 84            user=Auser,
 85            data=[MODEL_MAPPING[dtype]().from_dict(item) for item in body["data"]]
 86            if body.get("data") or body.get("data") == []
 87            else [],
 88            type=dtype,
 89        )
 90    elif dtype in DTYPE_TO_RESPONSE.keys():
 91
 92        if not dtype:
 93            raise exceptions.NoDtypeException
 94
 95        response = DTYPE_TO_RESPONSE[dtype]().from_dict(body)
 96
 97    elif dtype in HOOK_RESPONSE.keys():
 98
 99        if not dtype:
100            raise exceptions.NoDtypeException
101        response = HOOK_RESPONSE[dtype]().from_dict(body)
102
103    else:
104
105        response = GenericMessage().from_dict(body)
106
107    try:
108        setattr(response, "user", Auser)
109
110    finally:
111        try:
112            if "old_user" in body:
113                setattr(
114                    response,
115                    "old_user",
116                    models.user.User.from_dict(body["old_user"]),
117                )
118            if "new_user" in body:
119                setattr(
120                    response,
121                    "new_user",
122                    models.user.User.from_dict(body["new_user"]),
123                )
124        finally:
125            return typing.cast(TerraParsedApiResponse, response)
126
127
128class TerraApiResponse(TerraParsedApiResponse):
129    def __init__(
130        self,
131        resp: requests.Response,
132        dtype: str,
133        user: typing.Optional[user_.User] = None,
134        client: typing.Optional[Terra] = None,
135    ) -> None:
136        self.response_code: int = resp.status_code
137        self.raw_body: typing.Optional[str] = resp.text
138        self.json: typing.Optional[typing.Dict[str, typing.Any]] = None
139        self.dtype: typing.Optional[str] = dtype
140        try:
141            body: typing.Dict[str, typing.Any] = resp.json()
142            self.json = body
143            self.dtype = body.get("type", dtype)
144            self.parsed_response: TerraParsedApiResponse = _parse_api_body(self.dtype, body, user)
145            if client:
146
147                for e in typing.cast(SubscribedUsers, self.parsed_response).users:
148                    e._client = client
149        except json.decoder.JSONDecodeError:
150
151            resp.raise_for_status()
152
153    def get_parsed_response(self) -> TerraParsedApiResponse:
154        """
155        Get the parsed representation of the api response.
156
157        Returns:
158            :obj:`TerraParsedApiResponse`: Parsed representation of the data model.
159        """
160        return self.parsed_response
161
162    def get_json(self) -> typing.Optional[typing.Dict[str, typing.Any]]:
163        """
164        Get the dictionary (json) representation of the api response.
165
166        Returns:
167            :obj:`dict`: Dictionary representation of the api response.
168        """
169        return self.json
170
171
172class TerraWebhookResponse(TerraParsedApiResponse):
173    def __init__(
174        self,
175        resp: typing.Union[typing.Dict[str, typing.Any], requests.Response],
176        user: typing.Optional[user_.User] = None,
177        dtype: typing.Optional[str] = None,
178    ) -> None:
179        self.json: typing.Dict[str, typing.Any] = resp.json() if isinstance(resp, requests.Response) else resp
180        self.dtype: typing.Optional[str] = self.json.get("type", dtype)
181        self.parsed_response: TerraParsedApiResponse = _parse_api_body(self.dtype, self.json, user)
182
183    def get_parsed_response(self) -> TerraParsedApiResponse:
184        """
185        Get the parsed representation of the api response.
186
187        Returns:
188            :obj:`TerraParsedApiResponse`: Parsed representation of the data model.
189        """
190        return self.parsed_response
191
192    def get_json(self) -> typing.Dict[str, typing.Any]:
193        """
194        Get the dictionary (json) representation of the api response.
195
196        Returns:
197            :obj:`dict`: Dictionary representation of the api response.
198        """
199        return self.json
200
201
202@dataclasses.dataclass
203class GenericMessage(TerraParsedApiResponse):
204    message: typing.Optional[str] = dataclasses.field(default=None)
205    status: typing.Optional[str] = dataclasses.field(default=None)
206
207
208@dataclasses.dataclass
209class WidgetSession(TerraParsedApiResponse):
210    expires_in: int = dataclasses.field(default=900)
211    status: typing.Optional[str] = dataclasses.field(default=None)
212    session_id: typing.Optional[str] = dataclasses.field(default=None)
213    url: typing.Optional[str] = dataclasses.field(default=None)
214
215
216@dataclasses.dataclass
217class UserInfo(TerraParsedApiResponse):
218    status: typing.Optional[str] = dataclasses.field(default=None)
219    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
220    is_authenticated: bool = dataclasses.field(default=True)
221
222
223@dataclasses.dataclass
224class UserDeauthResp(TerraParsedApiResponse):
225    status: typing.Optional[str] = dataclasses.field(default="success")
226
227
228@dataclasses.dataclass
229class HookResponse(TerraParsedApiResponse):
230    status: typing.Optional[str] = dataclasses.field(default="success")
231    type: typing.Optional[str] = dataclasses.field(default=None)
232
233
234@dataclasses.dataclass
235class AuthHookResponse(HookResponse):
236    reference_id: typing.Optional[str] = dataclasses.field(default=None)
237    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
238    widget_session_id: typing.Optional[str] = dataclasses.field(default=None)
239
240
241@dataclasses.dataclass
242class UserReauthHookResponse(HookResponse):
243    message: typing.Optional[str] = dataclasses.field(default=None)
244    old_user: typing.Optional[models.user.User] = dataclasses.field(default=None)
245    new_user: typing.Optional[models.user.User] = dataclasses.field(default=None)
246
247
248@dataclasses.dataclass
249class UserDeauthHookResponse(HookResponse):
250    message: typing.Optional[str] = dataclasses.field(default=None)
251    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
252
253
254@dataclasses.dataclass
255class AccessRevokedHookResponse(HookResponse):
256    message: typing.Optional[str] = dataclasses.field(default=None)
257    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
258
259
260@dataclasses.dataclass
261class GoogleNoDataSourceHookResponse(HookResponse):
262    message: typing.Optional[str] = dataclasses.field(default=None)
263    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
264
265
266@dataclasses.dataclass
267class ConnectionErrorHookResponse(HookResponse):
268    message: typing.Optional[str] = dataclasses.field(default=None)
269    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
270
271
272@dataclasses.dataclass
273class RequestProcessingHookResponse(HookResponse):
274    message: typing.Optional[str] = dataclasses.field(default=None)
275    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
276
277
278@dataclasses.dataclass
279class RequestCompletedHookResponse(HookResponse):
280    message: typing.Optional[str] = dataclasses.field(default=None)
281    reference: typing.Optional[str] = dataclasses.field(default=None)
282    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
283
284
285@dataclasses.dataclass
286class SubscribedUsers(TerraParsedApiResponse):
287    users: typing.List[models.user.User] = dataclasses.field(default_factory=list)
288
289
290@dataclasses.dataclass
291class UserAuthUrl(TerraParsedApiResponse):
292    status: typing.Optional[str] = dataclasses.field(default=None)
293    expires_in: int = dataclasses.field(default=900)
294    auth_url: typing.Optional[str] = dataclasses.field(default=None)
295    user_id: typing.Optional[str] = dataclasses.field(default=None)
296
297
298@dataclasses.dataclass
299class NoDataReturned(TerraParsedApiResponse):
300    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
301    status: typing.Optional[str] = dataclasses.field(default="not_available")
302    message: typing.Optional[str] = dataclasses.field(default="Data type requested not available from provider")
303
304
305@dataclasses.dataclass
306class DataReturned(TerraParsedApiResponse):
307    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
308    type: typing.Optional[str] = dataclasses.field(default=None)
309    data: typing.List[TerraParsedApiResponse] = dataclasses.field(default_factory=list)
310
311
312@dataclasses.dataclass
313class AuthenticationFailed(TerraParsedApiResponse):
314    status: str = dataclasses.field(default="error")
315    widget_session_id: typing.Optional[str] = dataclasses.field(default=None)
316    reference_id: typing.Optional[str] = dataclasses.field(default=None)
317    message: typing.Optional[str] = dataclasses.field(default="User failed to authenticate and has been deleted")
318    type: str = dataclasses.field(default="auth")
319    reason: str = dataclasses.field(default="auth_cancelled")
320
321
322@dataclasses.dataclass
323class ConnectionDegraded(TerraParsedApiResponse):
324    status: typing.Optional[str] = dataclasses.field(default="warning")
325    message: typing.Optional[str] = dataclasses.field(default="User connection degraded")
326    type: typing.Optional[str] = dataclasses.field(default="connection_error")
327
328
329@dataclasses.dataclass
330class ProvidersResponse(TerraParsedApiResponse):
331    status: typing.Optional[str] = dataclasses.field(default="warning")
332    providers: typing.Optional[typing.List[str]] = dataclasses.field(default=None)
333    sdk_providers: typing.Optional[typing.List[str]] = dataclasses.field(default=None)
334
335
336@dataclasses.dataclass
337class SentToWebhook(TerraParsedApiResponse):
338    status: typing.Optional[str] = dataclasses.field(default=None)
339    message: typing.Optional[str] = dataclasses.field(default=None)
340
341
342USER_DATATYPES = [
343    "activity",
344    "athlete",
345    "body",
346    "daily",
347    "menstruation",
348    "sleep",
349    "nutrition",
350]
351MODEL_MAPPING = {
352    "activity": models.v2.activity.Activity,
353    "body": models.v2.body.Body,
354    "daily": models.v2.daily.Daily,
355    "sleep": models.v2.sleep.Sleep,
356    "menstruation": models.v2.menstruation.Menstruation,
357    "athlete": models.v2.athlete.Athlete,
358    "nutrition": models.v2.nutrition.Nutrition,
359}
360
361
362DTYPE_TO_RESPONSE = {
363    "widget_session": WidgetSession,
364    "auth_url": UserAuthUrl,
365    "user_info": UserInfo,
366    "subscriptions": SubscribedUsers,
367    "providers": ProvidersResponse,
368    "sent_to_webhook": SentToWebhook,
369}
370
371HOOK_TYPES = {
372    "auth",
373    "user_reauth",
374    "access_revoked",
375    "deauth",
376    "google_no_datasource",
377    "connexion_error",
378    "request_processing",
379    "request_completed",
380}
381
382HOOK_RESPONSE = {
383    "auth": AuthHookResponse,
384    "user_reauth": UserReauthHookResponse,
385    "access_revoked": AccessRevokedHookResponse,
386    "deauth": UserDeauthHookResponse,
387    "google_no_datasource": GoogleNoDataSourceHookResponse,
388    "connexion_error": ConnectionErrorHookResponse,
389    "request_completed": RequestCompletedHookResponse,
390    "request_processing": RequestProcessingHookResponse,
391}
392
393STATUS = {
394    "not_available": NoDataReturned,
395    "warning": ConnectionDegraded,
396    "error": ConnectionDegraded,
397}
@dataclasses.dataclass
class NoDataReturned(TerraParsedApiResponse):
299@dataclasses.dataclass
300class NoDataReturned(TerraParsedApiResponse):
301    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
302    status: typing.Optional[str] = dataclasses.field(default="not_available")
303    message: typing.Optional[str] = dataclasses.field(default="Data type requested not available from provider")

NoDataReturned(user: 'typing.Optional[models.user.User]' = None, status: 'typing.Optional[str]' = 'not_available', message: 'typing.Optional[str]' = 'Data type requested not available from provider')

NoDataReturned( user: Optional[terra.models.user.User] = None, status: Optional[str] = 'not_available', message: Optional[str] = 'Data type requested not available from provider')
user: Optional[terra.models.user.User] = None
status: Optional[str] = 'not_available'
message: Optional[str] = 'Data type requested not available from provider'
@dataclasses.dataclass
class DataReturned(TerraParsedApiResponse):
306@dataclasses.dataclass
307class DataReturned(TerraParsedApiResponse):
308    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
309    type: typing.Optional[str] = dataclasses.field(default=None)
310    data: typing.List[TerraParsedApiResponse] = dataclasses.field(default_factory=list)

DataReturned(user: 'typing.Optional[models.user.User]' = None, type: 'typing.Optional[str]' = None, data: 'typing.List[TerraParsedApiResponse]' = )

DataReturned( user: Optional[terra.models.user.User] = None, type: Optional[str] = None, data: List[terra.api.api_responses.TerraParsedApiResponse] = <factory>)
user: Optional[terra.models.user.User] = None
type: Optional[str] = None
@dataclasses.dataclass
class AuthenticationFailed(TerraParsedApiResponse):
313@dataclasses.dataclass
314class AuthenticationFailed(TerraParsedApiResponse):
315    status: str = dataclasses.field(default="error")
316    widget_session_id: typing.Optional[str] = dataclasses.field(default=None)
317    reference_id: typing.Optional[str] = dataclasses.field(default=None)
318    message: typing.Optional[str] = dataclasses.field(default="User failed to authenticate and has been deleted")
319    type: str = dataclasses.field(default="auth")
320    reason: str = dataclasses.field(default="auth_cancelled")

AuthenticationFailed(status: 'str' = 'error', widget_session_id: 'typing.Optional[str]' = None, reference_id: 'typing.Optional[str]' = None, message: 'typing.Optional[str]' = 'User failed to authenticate and has been deleted', type: 'str' = 'auth', reason: 'str' = 'auth_cancelled')

AuthenticationFailed( status: str = 'error', widget_session_id: Optional[str] = None, reference_id: Optional[str] = None, message: Optional[str] = 'User failed to authenticate and has been deleted', type: str = 'auth', reason: str = 'auth_cancelled')
status: str = 'error'
widget_session_id: Optional[str] = None
reference_id: Optional[str] = None
message: Optional[str] = 'User failed to authenticate and has been deleted'
type: str = 'auth'
reason: str = 'auth_cancelled'
class TerraParsedApiResponse(terra.models.base_model.TerraDataModel):
59class TerraParsedApiResponse(base_model.TerraDataModel):
60    pass

Base class for all data models that terra returns.

TerraParsedApiResponse()
class TerraWebhookResponse(TerraParsedApiResponse):
173class TerraWebhookResponse(TerraParsedApiResponse):
174    def __init__(
175        self,
176        resp: typing.Union[typing.Dict[str, typing.Any], requests.Response],
177        user: typing.Optional[user_.User] = None,
178        dtype: typing.Optional[str] = None,
179    ) -> None:
180        self.json: typing.Dict[str, typing.Any] = resp.json() if isinstance(resp, requests.Response) else resp
181        self.dtype: typing.Optional[str] = self.json.get("type", dtype)
182        self.parsed_response: TerraParsedApiResponse = _parse_api_body(self.dtype, self.json, user)
183
184    def get_parsed_response(self) -> TerraParsedApiResponse:
185        """
186        Get the parsed representation of the api response.
187
188        Returns:
189            :obj:`TerraParsedApiResponse`: Parsed representation of the data model.
190        """
191        return self.parsed_response
192
193    def get_json(self) -> typing.Dict[str, typing.Any]:
194        """
195        Get the dictionary (json) representation of the api response.
196
197        Returns:
198            :obj:`dict`: Dictionary representation of the api response.
199        """
200        return self.json

Base class for all data models that terra returns.

TerraWebhookResponse( resp: Union[Dict[str, Any], requests.models.Response], user: Optional[terra.models.user.User] = None, dtype: Optional[str] = None)
174    def __init__(
175        self,
176        resp: typing.Union[typing.Dict[str, typing.Any], requests.Response],
177        user: typing.Optional[user_.User] = None,
178        dtype: typing.Optional[str] = None,
179    ) -> None:
180        self.json: typing.Dict[str, typing.Any] = resp.json() if isinstance(resp, requests.Response) else resp
181        self.dtype: typing.Optional[str] = self.json.get("type", dtype)
182        self.parsed_response: TerraParsedApiResponse = _parse_api_body(self.dtype, self.json, user)
def get_parsed_response(self) -> terra.api.api_responses.TerraParsedApiResponse:
184    def get_parsed_response(self) -> TerraParsedApiResponse:
185        """
186        Get the parsed representation of the api response.
187
188        Returns:
189            :obj:`TerraParsedApiResponse`: Parsed representation of the data model.
190        """
191        return self.parsed_response

Get the parsed representation of the api response.

Returns: TerraParsedApiResponse: Parsed representation of the data model.

def get_json(self) -> Dict[str, Any]:
193    def get_json(self) -> typing.Dict[str, typing.Any]:
194        """
195        Get the dictionary (json) representation of the api response.
196
197        Returns:
198            :obj:`dict`: Dictionary representation of the api response.
199        """
200        return self.json

Get the dictionary (json) representation of the api response.

Returns: dict: Dictionary representation of the api response.

class TerraApiResponse(TerraParsedApiResponse):
129class TerraApiResponse(TerraParsedApiResponse):
130    def __init__(
131        self,
132        resp: requests.Response,
133        dtype: str,
134        user: typing.Optional[user_.User] = None,
135        client: typing.Optional[Terra] = None,
136    ) -> None:
137        self.response_code: int = resp.status_code
138        self.raw_body: typing.Optional[str] = resp.text
139        self.json: typing.Optional[typing.Dict[str, typing.Any]] = None
140        self.dtype: typing.Optional[str] = dtype
141        try:
142            body: typing.Dict[str, typing.Any] = resp.json()
143            self.json = body
144            self.dtype = body.get("type", dtype)
145            self.parsed_response: TerraParsedApiResponse = _parse_api_body(self.dtype, body, user)
146            if client:
147
148                for e in typing.cast(SubscribedUsers, self.parsed_response).users:
149                    e._client = client
150        except json.decoder.JSONDecodeError:
151
152            resp.raise_for_status()
153
154    def get_parsed_response(self) -> TerraParsedApiResponse:
155        """
156        Get the parsed representation of the api response.
157
158        Returns:
159            :obj:`TerraParsedApiResponse`: Parsed representation of the data model.
160        """
161        return self.parsed_response
162
163    def get_json(self) -> typing.Optional[typing.Dict[str, typing.Any]]:
164        """
165        Get the dictionary (json) representation of the api response.
166
167        Returns:
168            :obj:`dict`: Dictionary representation of the api response.
169        """
170        return self.json

Base class for all data models that terra returns.

TerraApiResponse( resp: requests.models.Response, dtype: str, user: Optional[terra.models.user.User] = None, client: Optional[terra.base_client.Terra] = None)
130    def __init__(
131        self,
132        resp: requests.Response,
133        dtype: str,
134        user: typing.Optional[user_.User] = None,
135        client: typing.Optional[Terra] = None,
136    ) -> None:
137        self.response_code: int = resp.status_code
138        self.raw_body: typing.Optional[str] = resp.text
139        self.json: typing.Optional[typing.Dict[str, typing.Any]] = None
140        self.dtype: typing.Optional[str] = dtype
141        try:
142            body: typing.Dict[str, typing.Any] = resp.json()
143            self.json = body
144            self.dtype = body.get("type", dtype)
145            self.parsed_response: TerraParsedApiResponse = _parse_api_body(self.dtype, body, user)
146            if client:
147
148                for e in typing.cast(SubscribedUsers, self.parsed_response).users:
149                    e._client = client
150        except json.decoder.JSONDecodeError:
151
152            resp.raise_for_status()
def get_parsed_response(self) -> terra.api.api_responses.TerraParsedApiResponse:
154    def get_parsed_response(self) -> TerraParsedApiResponse:
155        """
156        Get the parsed representation of the api response.
157
158        Returns:
159            :obj:`TerraParsedApiResponse`: Parsed representation of the data model.
160        """
161        return self.parsed_response

Get the parsed representation of the api response.

Returns: TerraParsedApiResponse: Parsed representation of the data model.

def get_json(self) -> Optional[Dict[str, Any]]:
163    def get_json(self) -> typing.Optional[typing.Dict[str, typing.Any]]:
164        """
165        Get the dictionary (json) representation of the api response.
166
167        Returns:
168            :obj:`dict`: Dictionary representation of the api response.
169        """
170        return self.json

Get the dictionary (json) representation of the api response.

Returns: dict: Dictionary representation of the api response.

@dataclasses.dataclass
class GenericMessage(TerraParsedApiResponse):
203@dataclasses.dataclass
204class GenericMessage(TerraParsedApiResponse):
205    message: typing.Optional[str] = dataclasses.field(default=None)
206    status: typing.Optional[str] = dataclasses.field(default=None)

GenericMessage(message: 'typing.Optional[str]' = None, status: 'typing.Optional[str]' = None)

GenericMessage(message: Optional[str] = None, status: Optional[str] = None)
message: Optional[str] = None
status: Optional[str] = None
@dataclasses.dataclass
class GoogleNoDataSourceHookResponse(HookResponse):
261@dataclasses.dataclass
262class GoogleNoDataSourceHookResponse(HookResponse):
263    message: typing.Optional[str] = dataclasses.field(default=None)
264    user: typing.Optional[models.user.User] = dataclasses.field(default=None)

GoogleNoDataSourceHookResponse(status: 'typing.Optional[str]' = 'success', type: 'typing.Optional[str]' = None, message: 'typing.Optional[str]' = None, user: 'typing.Optional[models.user.User]' = None)

GoogleNoDataSourceHookResponse( status: Optional[str] = 'success', type: Optional[str] = None, message: Optional[str] = None, user: Optional[terra.models.user.User] = None)
message: Optional[str] = None
user: Optional[terra.models.user.User] = None
@dataclasses.dataclass
class WidgetSession(TerraParsedApiResponse):
209@dataclasses.dataclass
210class WidgetSession(TerraParsedApiResponse):
211    expires_in: int = dataclasses.field(default=900)
212    status: typing.Optional[str] = dataclasses.field(default=None)
213    session_id: typing.Optional[str] = dataclasses.field(default=None)
214    url: typing.Optional[str] = dataclasses.field(default=None)

WidgetSession(expires_in: 'int' = 900, status: 'typing.Optional[str]' = None, session_id: 'typing.Optional[str]' = None, url: 'typing.Optional[str]' = None)

WidgetSession( expires_in: int = 900, status: Optional[str] = None, session_id: Optional[str] = None, url: Optional[str] = None)
expires_in: int = 900
status: Optional[str] = None
session_id: Optional[str] = None
url: Optional[str] = None
@dataclasses.dataclass
class SentToWebhook(TerraParsedApiResponse):
337@dataclasses.dataclass
338class SentToWebhook(TerraParsedApiResponse):
339    status: typing.Optional[str] = dataclasses.field(default=None)
340    message: typing.Optional[str] = dataclasses.field(default=None)

SentToWebhook(status: 'typing.Optional[str]' = None, message: 'typing.Optional[str]' = None)

SentToWebhook(status: Optional[str] = None, message: Optional[str] = None)
status: Optional[str] = None
message: Optional[str] = None
@dataclasses.dataclass
class SubscribedUsers(TerraParsedApiResponse):
286@dataclasses.dataclass
287class SubscribedUsers(TerraParsedApiResponse):
288    users: typing.List[models.user.User] = dataclasses.field(default_factory=list)

SubscribedUsers(users: 'typing.List[models.user.User]' = )

SubscribedUsers(users: List[terra.models.user.User] = <factory>)
@dataclasses.dataclass
class UserDeauthResp(TerraParsedApiResponse):
224@dataclasses.dataclass
225class UserDeauthResp(TerraParsedApiResponse):
226    status: typing.Optional[str] = dataclasses.field(default="success")

UserDeauthResp(status: 'typing.Optional[str]' = 'success')

UserDeauthResp(status: Optional[str] = 'success')
status: Optional[str] = 'success'
@dataclasses.dataclass
class AuthHookResponse(HookResponse):
235@dataclasses.dataclass
236class AuthHookResponse(HookResponse):
237    reference_id: typing.Optional[str] = dataclasses.field(default=None)
238    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
239    widget_session_id: typing.Optional[str] = dataclasses.field(default=None)

AuthHookResponse(status: 'typing.Optional[str]' = 'success', type: 'typing.Optional[str]' = None, reference_id: 'typing.Optional[str]' = None, user: 'typing.Optional[models.user.User]' = None, widget_session_id: 'typing.Optional[str]' = None)

AuthHookResponse( status: Optional[str] = 'success', type: Optional[str] = None, reference_id: Optional[str] = None, user: Optional[terra.models.user.User] = None, widget_session_id: Optional[str] = None)
reference_id: Optional[str] = None
user: Optional[terra.models.user.User] = None
widget_session_id: Optional[str] = None
@dataclasses.dataclass
class AccessRevokedHookResponse(HookResponse):
255@dataclasses.dataclass
256class AccessRevokedHookResponse(HookResponse):
257    message: typing.Optional[str] = dataclasses.field(default=None)
258    user: typing.Optional[models.user.User] = dataclasses.field(default=None)

AccessRevokedHookResponse(status: 'typing.Optional[str]' = 'success', type: 'typing.Optional[str]' = None, message: 'typing.Optional[str]' = None, user: 'typing.Optional[models.user.User]' = None)

AccessRevokedHookResponse( status: Optional[str] = 'success', type: Optional[str] = None, message: Optional[str] = None, user: Optional[terra.models.user.User] = None)
message: Optional[str] = None
user: Optional[terra.models.user.User] = None
@dataclasses.dataclass
class UserAuthUrl(TerraParsedApiResponse):
291@dataclasses.dataclass
292class UserAuthUrl(TerraParsedApiResponse):
293    status: typing.Optional[str] = dataclasses.field(default=None)
294    expires_in: int = dataclasses.field(default=900)
295    auth_url: typing.Optional[str] = dataclasses.field(default=None)
296    user_id: typing.Optional[str] = dataclasses.field(default=None)

UserAuthUrl(status: 'typing.Optional[str]' = None, expires_in: 'int' = 900, auth_url: 'typing.Optional[str]' = None, user_id: 'typing.Optional[str]' = None)

UserAuthUrl( status: Optional[str] = None, expires_in: int = 900, auth_url: Optional[str] = None, user_id: Optional[str] = None)
status: Optional[str] = None
expires_in: int = 900
auth_url: Optional[str] = None
user_id: Optional[str] = None
@dataclasses.dataclass
class UserDeauthHookResponse(HookResponse):
249@dataclasses.dataclass
250class UserDeauthHookResponse(HookResponse):
251    message: typing.Optional[str] = dataclasses.field(default=None)
252    user: typing.Optional[models.user.User] = dataclasses.field(default=None)

UserDeauthHookResponse(status: 'typing.Optional[str]' = 'success', type: 'typing.Optional[str]' = None, message: 'typing.Optional[str]' = None, user: 'typing.Optional[models.user.User]' = None)

UserDeauthHookResponse( status: Optional[str] = 'success', type: Optional[str] = None, message: Optional[str] = None, user: Optional[terra.models.user.User] = None)
message: Optional[str] = None
user: Optional[terra.models.user.User] = None
@dataclasses.dataclass
class UserReauthHookResponse(HookResponse):
242@dataclasses.dataclass
243class UserReauthHookResponse(HookResponse):
244    message: typing.Optional[str] = dataclasses.field(default=None)
245    old_user: typing.Optional[models.user.User] = dataclasses.field(default=None)
246    new_user: typing.Optional[models.user.User] = dataclasses.field(default=None)

UserReauthHookResponse(status: 'typing.Optional[str]' = 'success', type: 'typing.Optional[str]' = None, message: 'typing.Optional[str]' = None, old_user: 'typing.Optional[models.user.User]' = None, new_user: 'typing.Optional[models.user.User]' = None)

UserReauthHookResponse( status: Optional[str] = 'success', type: Optional[str] = None, message: Optional[str] = None, old_user: Optional[terra.models.user.User] = None, new_user: Optional[terra.models.user.User] = None)
message: Optional[str] = None
old_user: Optional[terra.models.user.User] = None
new_user: Optional[terra.models.user.User] = None
@dataclasses.dataclass
class ConnectionDegraded(TerraParsedApiResponse):
323@dataclasses.dataclass
324class ConnectionDegraded(TerraParsedApiResponse):
325    status: typing.Optional[str] = dataclasses.field(default="warning")
326    message: typing.Optional[str] = dataclasses.field(default="User connection degraded")
327    type: typing.Optional[str] = dataclasses.field(default="connection_error")

ConnectionDegraded(status: 'typing.Optional[str]' = 'warning', message: 'typing.Optional[str]' = 'User connection degraded', type: 'typing.Optional[str]' = 'connection_error')

ConnectionDegraded( status: Optional[str] = 'warning', message: Optional[str] = 'User connection degraded', type: Optional[str] = 'connection_error')
status: Optional[str] = 'warning'
message: Optional[str] = 'User connection degraded'
type: Optional[str] = 'connection_error'
@dataclasses.dataclass
class ProvidersResponse(TerraParsedApiResponse):
330@dataclasses.dataclass
331class ProvidersResponse(TerraParsedApiResponse):
332    status: typing.Optional[str] = dataclasses.field(default="warning")
333    providers: typing.Optional[typing.List[str]] = dataclasses.field(default=None)
334    sdk_providers: typing.Optional[typing.List[str]] = dataclasses.field(default=None)

ProvidersResponse(status: 'typing.Optional[str]' = 'warning', providers: 'typing.Optional[typing.List[str]]' = None, sdk_providers: 'typing.Optional[typing.List[str]]' = None)

ProvidersResponse( status: Optional[str] = 'warning', providers: Optional[List[str]] = None, sdk_providers: Optional[List[str]] = None)
status: Optional[str] = 'warning'
providers: Optional[List[str]] = None
sdk_providers: Optional[List[str]] = None
@dataclasses.dataclass
class RequestProcessingHookResponse(HookResponse):
273@dataclasses.dataclass
274class RequestProcessingHookResponse(HookResponse):
275    message: typing.Optional[str] = dataclasses.field(default=None)
276    user: typing.Optional[models.user.User] = dataclasses.field(default=None)

RequestProcessingHookResponse(status: 'typing.Optional[str]' = 'success', type: 'typing.Optional[str]' = None, message: 'typing.Optional[str]' = None, user: 'typing.Optional[models.user.User]' = None)

RequestProcessingHookResponse( status: Optional[str] = 'success', type: Optional[str] = None, message: Optional[str] = None, user: Optional[terra.models.user.User] = None)
message: Optional[str] = None
user: Optional[terra.models.user.User] = None
@dataclasses.dataclass
class ConnectionErrorHookResponse(HookResponse):
267@dataclasses.dataclass
268class ConnectionErrorHookResponse(HookResponse):
269    message: typing.Optional[str] = dataclasses.field(default=None)
270    user: typing.Optional[models.user.User] = dataclasses.field(default=None)

ConnectionErrorHookResponse(status: 'typing.Optional[str]' = 'success', type: 'typing.Optional[str]' = None, message: 'typing.Optional[str]' = None, user: 'typing.Optional[models.user.User]' = None)

ConnectionErrorHookResponse( status: Optional[str] = 'success', type: Optional[str] = None, message: Optional[str] = None, user: Optional[terra.models.user.User] = None)
message: Optional[str] = None
user: Optional[terra.models.user.User] = None
@dataclasses.dataclass
class HookResponse(TerraParsedApiResponse):
229@dataclasses.dataclass
230class HookResponse(TerraParsedApiResponse):
231    status: typing.Optional[str] = dataclasses.field(default="success")
232    type: typing.Optional[str] = dataclasses.field(default=None)

HookResponse(status: 'typing.Optional[str]' = 'success', type: 'typing.Optional[str]' = None)

HookResponse(status: Optional[str] = 'success', type: Optional[str] = None)
status: Optional[str] = 'success'
type: Optional[str] = None
@dataclasses.dataclass
class RequestCompletedHookResponse(HookResponse):
279@dataclasses.dataclass
280class RequestCompletedHookResponse(HookResponse):
281    message: typing.Optional[str] = dataclasses.field(default=None)
282    reference: typing.Optional[str] = dataclasses.field(default=None)
283    user: typing.Optional[models.user.User] = dataclasses.field(default=None)

RequestCompletedHookResponse(status: 'typing.Optional[str]' = 'success', type: 'typing.Optional[str]' = None, message: 'typing.Optional[str]' = None, reference: 'typing.Optional[str]' = None, user: 'typing.Optional[models.user.User]' = None)

RequestCompletedHookResponse( status: Optional[str] = 'success', type: Optional[str] = None, message: Optional[str] = None, reference: Optional[str] = None, user: Optional[terra.models.user.User] = None)
message: Optional[str] = None
reference: Optional[str] = None
user: Optional[terra.models.user.User] = None
@dataclasses.dataclass
class UserInfo(TerraParsedApiResponse):
217@dataclasses.dataclass
218class UserInfo(TerraParsedApiResponse):
219    status: typing.Optional[str] = dataclasses.field(default=None)
220    user: typing.Optional[models.user.User] = dataclasses.field(default=None)
221    is_authenticated: bool = dataclasses.field(default=True)

UserInfo(status: 'typing.Optional[str]' = None, user: 'typing.Optional[models.user.User]' = None, is_authenticated: 'bool' = True)

UserInfo( status: Optional[str] = None, user: Optional[terra.models.user.User] = None, is_authenticated: bool = True)
status: Optional[str] = None
user: Optional[terra.models.user.User] = None
is_authenticated: bool = True