samsara-api 0.0.2__py3-none-any.whl → 0.0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- samsara/__init__.py +0 -2
- samsara/auth/__init__.py +3 -2
- samsara/auth/client.py +3 -93
- samsara/auth/raw_client.py +0 -115
- samsara/auth/{types → tokens}/__init__.py +2 -2
- samsara/auth/tokens/client.py +185 -0
- samsara/auth/tokens/raw_client.py +210 -0
- samsara/auth/tokens/types/__init__.py +7 -0
- samsara/auth/{types/get_token_response.py → tokens/types/create_tokens_response.py} +4 -2
- samsara/client.py +2 -2
- samsara/core/client_wrapper.py +2 -2
- samsara/types/work_order_item_object_request_body.py +1 -1
- samsara/types/work_order_item_object_request_body_type.py +10 -1
- samsara/types/work_order_item_object_response_body.py +1 -1
- samsara/types/work_order_item_object_response_body_type.py +10 -1
- samsara/utils/webhooks_helper.py +52 -0
- samsara_api-0.0.4.dist-info/METADATA +238 -0
- {samsara_api-0.0.2.dist-info → samsara_api-0.0.4.dist-info}/RECORD +20 -16
- samsara_api-0.0.2.dist-info/METADATA +0 -29
- {samsara_api-0.0.2.dist-info → samsara_api-0.0.4.dist-info}/LICENSE +0 -0
- {samsara_api-0.0.2.dist-info → samsara_api-0.0.4.dist-info}/WHEEL +0 -0
samsara/__init__.py
CHANGED
|
@@ -2347,7 +2347,6 @@ from .attributes import (
|
|
|
2347
2347
|
UpdateAttributeRequestAttributeValueQuantity,
|
|
2348
2348
|
UpdateAttributeRequestEntityType,
|
|
2349
2349
|
)
|
|
2350
|
-
from .auth import GetTokenResponse
|
|
2351
2350
|
from .client import AsyncSamsara, Samsara
|
|
2352
2351
|
from .documents import DocumentsPostDocumentRequestBodyState
|
|
2353
2352
|
from .driver_vehicle_assignments import (
|
|
@@ -3351,7 +3350,6 @@ __all__ = [
|
|
|
3351
3350
|
"GetDriverTrailerAssignmentsResponseBodyResponseBody",
|
|
3352
3351
|
"GetMediaRetrievalObjectResponseBody",
|
|
3353
3352
|
"GetResponseWorkflowConfigurationObjectResponseBody",
|
|
3354
|
-
"GetTokenResponse",
|
|
3355
3353
|
"GetWorkflowIncidentResponseObjectResponseBody",
|
|
3356
3354
|
"GoaAddressTinyResponseResponseBody",
|
|
3357
3355
|
"GoaAttributeTinyRequestBody",
|
samsara/auth/__init__.py
CHANGED
samsara/auth/client.py
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
# This file was auto-generated by Fern from our API Definition.
|
|
2
2
|
|
|
3
|
-
import typing
|
|
4
|
-
|
|
5
3
|
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
|
|
6
|
-
from ..core.request_options import RequestOptions
|
|
7
4
|
from .raw_client import AsyncRawAuthClient, RawAuthClient
|
|
8
|
-
from .
|
|
9
|
-
|
|
10
|
-
# this is used as the default value for optional parameters
|
|
11
|
-
OMIT = typing.cast(typing.Any, ...)
|
|
5
|
+
from .tokens.client import AsyncTokensClient, TokensClient
|
|
12
6
|
|
|
13
7
|
|
|
14
8
|
class AuthClient:
|
|
15
9
|
def __init__(self, *, client_wrapper: SyncClientWrapper):
|
|
16
10
|
self._raw_client = RawAuthClient(client_wrapper=client_wrapper)
|
|
11
|
+
self.tokens = TokensClient(client_wrapper=client_wrapper)
|
|
17
12
|
|
|
18
13
|
@property
|
|
19
14
|
def with_raw_response(self) -> RawAuthClient:
|
|
@@ -26,49 +21,11 @@ class AuthClient:
|
|
|
26
21
|
"""
|
|
27
22
|
return self._raw_client
|
|
28
23
|
|
|
29
|
-
def get_token(
|
|
30
|
-
self, *, client_id: str, client_secret: str, request_options: typing.Optional[RequestOptions] = None
|
|
31
|
-
) -> GetTokenResponse:
|
|
32
|
-
"""
|
|
33
|
-
Obtain an OAuth2 access token using client credentials
|
|
34
|
-
|
|
35
|
-
Parameters
|
|
36
|
-
----------
|
|
37
|
-
client_id : str
|
|
38
|
-
The client ID of the application
|
|
39
|
-
|
|
40
|
-
client_secret : str
|
|
41
|
-
The client secret of the application
|
|
42
|
-
|
|
43
|
-
request_options : typing.Optional[RequestOptions]
|
|
44
|
-
Request-specific configuration.
|
|
45
|
-
|
|
46
|
-
Returns
|
|
47
|
-
-------
|
|
48
|
-
GetTokenResponse
|
|
49
|
-
Successful response
|
|
50
|
-
|
|
51
|
-
Examples
|
|
52
|
-
--------
|
|
53
|
-
from samsara import Samsara
|
|
54
|
-
|
|
55
|
-
client = Samsara(
|
|
56
|
-
token="YOUR_TOKEN",
|
|
57
|
-
)
|
|
58
|
-
client.auth.get_token(
|
|
59
|
-
client_id="client_id",
|
|
60
|
-
client_secret="client_secret",
|
|
61
|
-
)
|
|
62
|
-
"""
|
|
63
|
-
_response = self._raw_client.get_token(
|
|
64
|
-
client_id=client_id, client_secret=client_secret, request_options=request_options
|
|
65
|
-
)
|
|
66
|
-
return _response.data
|
|
67
|
-
|
|
68
24
|
|
|
69
25
|
class AsyncAuthClient:
|
|
70
26
|
def __init__(self, *, client_wrapper: AsyncClientWrapper):
|
|
71
27
|
self._raw_client = AsyncRawAuthClient(client_wrapper=client_wrapper)
|
|
28
|
+
self.tokens = AsyncTokensClient(client_wrapper=client_wrapper)
|
|
72
29
|
|
|
73
30
|
@property
|
|
74
31
|
def with_raw_response(self) -> AsyncRawAuthClient:
|
|
@@ -80,50 +37,3 @@ class AsyncAuthClient:
|
|
|
80
37
|
AsyncRawAuthClient
|
|
81
38
|
"""
|
|
82
39
|
return self._raw_client
|
|
83
|
-
|
|
84
|
-
async def get_token(
|
|
85
|
-
self, *, client_id: str, client_secret: str, request_options: typing.Optional[RequestOptions] = None
|
|
86
|
-
) -> GetTokenResponse:
|
|
87
|
-
"""
|
|
88
|
-
Obtain an OAuth2 access token using client credentials
|
|
89
|
-
|
|
90
|
-
Parameters
|
|
91
|
-
----------
|
|
92
|
-
client_id : str
|
|
93
|
-
The client ID of the application
|
|
94
|
-
|
|
95
|
-
client_secret : str
|
|
96
|
-
The client secret of the application
|
|
97
|
-
|
|
98
|
-
request_options : typing.Optional[RequestOptions]
|
|
99
|
-
Request-specific configuration.
|
|
100
|
-
|
|
101
|
-
Returns
|
|
102
|
-
-------
|
|
103
|
-
GetTokenResponse
|
|
104
|
-
Successful response
|
|
105
|
-
|
|
106
|
-
Examples
|
|
107
|
-
--------
|
|
108
|
-
import asyncio
|
|
109
|
-
|
|
110
|
-
from samsara import AsyncSamsara
|
|
111
|
-
|
|
112
|
-
client = AsyncSamsara(
|
|
113
|
-
token="YOUR_TOKEN",
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
async def main() -> None:
|
|
118
|
-
await client.auth.get_token(
|
|
119
|
-
client_id="client_id",
|
|
120
|
-
client_secret="client_secret",
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
asyncio.run(main())
|
|
125
|
-
"""
|
|
126
|
-
_response = await self._raw_client.get_token(
|
|
127
|
-
client_id=client_id, client_secret=client_secret, request_options=request_options
|
|
128
|
-
)
|
|
129
|
-
return _response.data
|
samsara/auth/raw_client.py
CHANGED
|
@@ -1,128 +1,13 @@
|
|
|
1
1
|
# This file was auto-generated by Fern from our API Definition.
|
|
2
2
|
|
|
3
|
-
import typing
|
|
4
|
-
from json.decoder import JSONDecodeError
|
|
5
|
-
|
|
6
|
-
from ..core.api_error import ApiError
|
|
7
3
|
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
|
|
8
|
-
from ..core.http_response import AsyncHttpResponse, HttpResponse
|
|
9
|
-
from ..core.pydantic_utilities import parse_obj_as
|
|
10
|
-
from ..core.request_options import RequestOptions
|
|
11
|
-
from .types.get_token_response import GetTokenResponse
|
|
12
|
-
|
|
13
|
-
# this is used as the default value for optional parameters
|
|
14
|
-
OMIT = typing.cast(typing.Any, ...)
|
|
15
4
|
|
|
16
5
|
|
|
17
6
|
class RawAuthClient:
|
|
18
7
|
def __init__(self, *, client_wrapper: SyncClientWrapper):
|
|
19
8
|
self._client_wrapper = client_wrapper
|
|
20
9
|
|
|
21
|
-
def get_token(
|
|
22
|
-
self, *, client_id: str, client_secret: str, request_options: typing.Optional[RequestOptions] = None
|
|
23
|
-
) -> HttpResponse[GetTokenResponse]:
|
|
24
|
-
"""
|
|
25
|
-
Obtain an OAuth2 access token using client credentials
|
|
26
|
-
|
|
27
|
-
Parameters
|
|
28
|
-
----------
|
|
29
|
-
client_id : str
|
|
30
|
-
The client ID of the application
|
|
31
|
-
|
|
32
|
-
client_secret : str
|
|
33
|
-
The client secret of the application
|
|
34
|
-
|
|
35
|
-
request_options : typing.Optional[RequestOptions]
|
|
36
|
-
Request-specific configuration.
|
|
37
|
-
|
|
38
|
-
Returns
|
|
39
|
-
-------
|
|
40
|
-
HttpResponse[GetTokenResponse]
|
|
41
|
-
Successful response
|
|
42
|
-
"""
|
|
43
|
-
_response = self._client_wrapper.httpx_client.request(
|
|
44
|
-
"oauth/token",
|
|
45
|
-
method="POST",
|
|
46
|
-
json={
|
|
47
|
-
"client_id": client_id,
|
|
48
|
-
"client_secret": client_secret,
|
|
49
|
-
"audience": "https://api.samsara.com",
|
|
50
|
-
"grant_type": "authorization_code",
|
|
51
|
-
},
|
|
52
|
-
headers={
|
|
53
|
-
"content-type": "application/json",
|
|
54
|
-
},
|
|
55
|
-
request_options=request_options,
|
|
56
|
-
omit=OMIT,
|
|
57
|
-
)
|
|
58
|
-
try:
|
|
59
|
-
if 200 <= _response.status_code < 300:
|
|
60
|
-
_data = typing.cast(
|
|
61
|
-
GetTokenResponse,
|
|
62
|
-
parse_obj_as(
|
|
63
|
-
type_=GetTokenResponse, # type: ignore
|
|
64
|
-
object_=_response.json(),
|
|
65
|
-
),
|
|
66
|
-
)
|
|
67
|
-
return HttpResponse(response=_response, data=_data)
|
|
68
|
-
_response_json = _response.json()
|
|
69
|
-
except JSONDecodeError:
|
|
70
|
-
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
|
|
71
|
-
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
|
|
72
|
-
|
|
73
10
|
|
|
74
11
|
class AsyncRawAuthClient:
|
|
75
12
|
def __init__(self, *, client_wrapper: AsyncClientWrapper):
|
|
76
13
|
self._client_wrapper = client_wrapper
|
|
77
|
-
|
|
78
|
-
async def get_token(
|
|
79
|
-
self, *, client_id: str, client_secret: str, request_options: typing.Optional[RequestOptions] = None
|
|
80
|
-
) -> AsyncHttpResponse[GetTokenResponse]:
|
|
81
|
-
"""
|
|
82
|
-
Obtain an OAuth2 access token using client credentials
|
|
83
|
-
|
|
84
|
-
Parameters
|
|
85
|
-
----------
|
|
86
|
-
client_id : str
|
|
87
|
-
The client ID of the application
|
|
88
|
-
|
|
89
|
-
client_secret : str
|
|
90
|
-
The client secret of the application
|
|
91
|
-
|
|
92
|
-
request_options : typing.Optional[RequestOptions]
|
|
93
|
-
Request-specific configuration.
|
|
94
|
-
|
|
95
|
-
Returns
|
|
96
|
-
-------
|
|
97
|
-
AsyncHttpResponse[GetTokenResponse]
|
|
98
|
-
Successful response
|
|
99
|
-
"""
|
|
100
|
-
_response = await self._client_wrapper.httpx_client.request(
|
|
101
|
-
"oauth/token",
|
|
102
|
-
method="POST",
|
|
103
|
-
json={
|
|
104
|
-
"client_id": client_id,
|
|
105
|
-
"client_secret": client_secret,
|
|
106
|
-
"audience": "https://api.samsara.com",
|
|
107
|
-
"grant_type": "authorization_code",
|
|
108
|
-
},
|
|
109
|
-
headers={
|
|
110
|
-
"content-type": "application/json",
|
|
111
|
-
},
|
|
112
|
-
request_options=request_options,
|
|
113
|
-
omit=OMIT,
|
|
114
|
-
)
|
|
115
|
-
try:
|
|
116
|
-
if 200 <= _response.status_code < 300:
|
|
117
|
-
_data = typing.cast(
|
|
118
|
-
GetTokenResponse,
|
|
119
|
-
parse_obj_as(
|
|
120
|
-
type_=GetTokenResponse, # type: ignore
|
|
121
|
-
object_=_response.json(),
|
|
122
|
-
),
|
|
123
|
-
)
|
|
124
|
-
return AsyncHttpResponse(response=_response, data=_data)
|
|
125
|
-
_response_json = _response.json()
|
|
126
|
-
except JSONDecodeError:
|
|
127
|
-
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
|
|
128
|
-
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
|
|
6
|
+
from ...core.request_options import RequestOptions
|
|
7
|
+
from .raw_client import AsyncRawTokensClient, RawTokensClient
|
|
8
|
+
from .types.create_tokens_response import CreateTokensResponse
|
|
9
|
+
|
|
10
|
+
# this is used as the default value for optional parameters
|
|
11
|
+
OMIT = typing.cast(typing.Any, ...)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TokensClient:
|
|
15
|
+
def __init__(self, *, client_wrapper: SyncClientWrapper):
|
|
16
|
+
self._raw_client = RawTokensClient(client_wrapper=client_wrapper)
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def with_raw_response(self) -> RawTokensClient:
|
|
20
|
+
"""
|
|
21
|
+
Retrieves a raw implementation of this client that returns raw responses.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
RawTokensClient
|
|
26
|
+
"""
|
|
27
|
+
return self._raw_client
|
|
28
|
+
|
|
29
|
+
def create(
|
|
30
|
+
self, *, code: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None
|
|
31
|
+
) -> CreateTokensResponse:
|
|
32
|
+
"""
|
|
33
|
+
Exchange an authorization code for access and refresh tokens.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
code : typing.Optional[str]
|
|
38
|
+
The client secret of the application
|
|
39
|
+
|
|
40
|
+
request_options : typing.Optional[RequestOptions]
|
|
41
|
+
Request-specific configuration.
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
CreateTokensResponse
|
|
46
|
+
Successful response
|
|
47
|
+
|
|
48
|
+
Examples
|
|
49
|
+
--------
|
|
50
|
+
from samsara import Samsara
|
|
51
|
+
|
|
52
|
+
client = Samsara(
|
|
53
|
+
token="YOUR_TOKEN",
|
|
54
|
+
)
|
|
55
|
+
client.auth.tokens.create()
|
|
56
|
+
"""
|
|
57
|
+
_response = self._raw_client.create(code=code, request_options=request_options)
|
|
58
|
+
return _response.data
|
|
59
|
+
|
|
60
|
+
def revoke(self, *, token: str, request_options: typing.Optional[RequestOptions] = None) -> CreateTokensResponse:
|
|
61
|
+
"""
|
|
62
|
+
Invalidates access tokens and refresh tokens for that organization
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
token : str
|
|
67
|
+
The token to revoke
|
|
68
|
+
|
|
69
|
+
request_options : typing.Optional[RequestOptions]
|
|
70
|
+
Request-specific configuration.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
CreateTokensResponse
|
|
75
|
+
Successful response
|
|
76
|
+
|
|
77
|
+
Examples
|
|
78
|
+
--------
|
|
79
|
+
from samsara import Samsara
|
|
80
|
+
|
|
81
|
+
client = Samsara(
|
|
82
|
+
token="YOUR_TOKEN",
|
|
83
|
+
)
|
|
84
|
+
client.auth.tokens.revoke(
|
|
85
|
+
token="token",
|
|
86
|
+
)
|
|
87
|
+
"""
|
|
88
|
+
_response = self._raw_client.revoke(token=token, request_options=request_options)
|
|
89
|
+
return _response.data
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class AsyncTokensClient:
|
|
93
|
+
def __init__(self, *, client_wrapper: AsyncClientWrapper):
|
|
94
|
+
self._raw_client = AsyncRawTokensClient(client_wrapper=client_wrapper)
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def with_raw_response(self) -> AsyncRawTokensClient:
|
|
98
|
+
"""
|
|
99
|
+
Retrieves a raw implementation of this client that returns raw responses.
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
AsyncRawTokensClient
|
|
104
|
+
"""
|
|
105
|
+
return self._raw_client
|
|
106
|
+
|
|
107
|
+
async def create(
|
|
108
|
+
self, *, code: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None
|
|
109
|
+
) -> CreateTokensResponse:
|
|
110
|
+
"""
|
|
111
|
+
Exchange an authorization code for access and refresh tokens.
|
|
112
|
+
|
|
113
|
+
Parameters
|
|
114
|
+
----------
|
|
115
|
+
code : typing.Optional[str]
|
|
116
|
+
The client secret of the application
|
|
117
|
+
|
|
118
|
+
request_options : typing.Optional[RequestOptions]
|
|
119
|
+
Request-specific configuration.
|
|
120
|
+
|
|
121
|
+
Returns
|
|
122
|
+
-------
|
|
123
|
+
CreateTokensResponse
|
|
124
|
+
Successful response
|
|
125
|
+
|
|
126
|
+
Examples
|
|
127
|
+
--------
|
|
128
|
+
import asyncio
|
|
129
|
+
|
|
130
|
+
from samsara import AsyncSamsara
|
|
131
|
+
|
|
132
|
+
client = AsyncSamsara(
|
|
133
|
+
token="YOUR_TOKEN",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
async def main() -> None:
|
|
138
|
+
await client.auth.tokens.create()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
asyncio.run(main())
|
|
142
|
+
"""
|
|
143
|
+
_response = await self._raw_client.create(code=code, request_options=request_options)
|
|
144
|
+
return _response.data
|
|
145
|
+
|
|
146
|
+
async def revoke(
|
|
147
|
+
self, *, token: str, request_options: typing.Optional[RequestOptions] = None
|
|
148
|
+
) -> CreateTokensResponse:
|
|
149
|
+
"""
|
|
150
|
+
Invalidates access tokens and refresh tokens for that organization
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
token : str
|
|
155
|
+
The token to revoke
|
|
156
|
+
|
|
157
|
+
request_options : typing.Optional[RequestOptions]
|
|
158
|
+
Request-specific configuration.
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
CreateTokensResponse
|
|
163
|
+
Successful response
|
|
164
|
+
|
|
165
|
+
Examples
|
|
166
|
+
--------
|
|
167
|
+
import asyncio
|
|
168
|
+
|
|
169
|
+
from samsara import AsyncSamsara
|
|
170
|
+
|
|
171
|
+
client = AsyncSamsara(
|
|
172
|
+
token="YOUR_TOKEN",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
async def main() -> None:
|
|
177
|
+
await client.auth.tokens.revoke(
|
|
178
|
+
token="token",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
asyncio.run(main())
|
|
183
|
+
"""
|
|
184
|
+
_response = await self._raw_client.revoke(token=token, request_options=request_options)
|
|
185
|
+
return _response.data
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
from json.decoder import JSONDecodeError
|
|
5
|
+
|
|
6
|
+
from ...core.api_error import ApiError
|
|
7
|
+
from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
|
|
8
|
+
from ...core.http_response import AsyncHttpResponse, HttpResponse
|
|
9
|
+
from ...core.pydantic_utilities import parse_obj_as
|
|
10
|
+
from ...core.request_options import RequestOptions
|
|
11
|
+
from .types.create_tokens_response import CreateTokensResponse
|
|
12
|
+
|
|
13
|
+
# this is used as the default value for optional parameters
|
|
14
|
+
OMIT = typing.cast(typing.Any, ...)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RawTokensClient:
|
|
18
|
+
def __init__(self, *, client_wrapper: SyncClientWrapper):
|
|
19
|
+
self._client_wrapper = client_wrapper
|
|
20
|
+
|
|
21
|
+
def create(
|
|
22
|
+
self, *, code: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None
|
|
23
|
+
) -> HttpResponse[CreateTokensResponse]:
|
|
24
|
+
"""
|
|
25
|
+
Exchange an authorization code for access and refresh tokens.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
code : typing.Optional[str]
|
|
30
|
+
The client secret of the application
|
|
31
|
+
|
|
32
|
+
request_options : typing.Optional[RequestOptions]
|
|
33
|
+
Request-specific configuration.
|
|
34
|
+
|
|
35
|
+
Returns
|
|
36
|
+
-------
|
|
37
|
+
HttpResponse[CreateTokensResponse]
|
|
38
|
+
Successful response
|
|
39
|
+
"""
|
|
40
|
+
_response = self._client_wrapper.httpx_client.request(
|
|
41
|
+
"oauth2/token",
|
|
42
|
+
method="POST",
|
|
43
|
+
json={
|
|
44
|
+
"code": code,
|
|
45
|
+
"grant_type": "authorization_code",
|
|
46
|
+
},
|
|
47
|
+
headers={
|
|
48
|
+
"content-type": "application/json",
|
|
49
|
+
},
|
|
50
|
+
request_options=request_options,
|
|
51
|
+
omit=OMIT,
|
|
52
|
+
)
|
|
53
|
+
try:
|
|
54
|
+
if 200 <= _response.status_code < 300:
|
|
55
|
+
_data = typing.cast(
|
|
56
|
+
CreateTokensResponse,
|
|
57
|
+
parse_obj_as(
|
|
58
|
+
type_=CreateTokensResponse, # type: ignore
|
|
59
|
+
object_=_response.json(),
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
return HttpResponse(response=_response, data=_data)
|
|
63
|
+
_response_json = _response.json()
|
|
64
|
+
except JSONDecodeError:
|
|
65
|
+
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
|
|
66
|
+
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
|
|
67
|
+
|
|
68
|
+
def revoke(
|
|
69
|
+
self, *, token: str, request_options: typing.Optional[RequestOptions] = None
|
|
70
|
+
) -> HttpResponse[CreateTokensResponse]:
|
|
71
|
+
"""
|
|
72
|
+
Invalidates access tokens and refresh tokens for that organization
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
token : str
|
|
77
|
+
The token to revoke
|
|
78
|
+
|
|
79
|
+
request_options : typing.Optional[RequestOptions]
|
|
80
|
+
Request-specific configuration.
|
|
81
|
+
|
|
82
|
+
Returns
|
|
83
|
+
-------
|
|
84
|
+
HttpResponse[CreateTokensResponse]
|
|
85
|
+
Successful response
|
|
86
|
+
"""
|
|
87
|
+
_response = self._client_wrapper.httpx_client.request(
|
|
88
|
+
"oauth2/revoke",
|
|
89
|
+
method="POST",
|
|
90
|
+
json={
|
|
91
|
+
"token": token,
|
|
92
|
+
},
|
|
93
|
+
headers={
|
|
94
|
+
"content-type": "application/json",
|
|
95
|
+
},
|
|
96
|
+
request_options=request_options,
|
|
97
|
+
omit=OMIT,
|
|
98
|
+
)
|
|
99
|
+
try:
|
|
100
|
+
if 200 <= _response.status_code < 300:
|
|
101
|
+
_data = typing.cast(
|
|
102
|
+
CreateTokensResponse,
|
|
103
|
+
parse_obj_as(
|
|
104
|
+
type_=CreateTokensResponse, # type: ignore
|
|
105
|
+
object_=_response.json(),
|
|
106
|
+
),
|
|
107
|
+
)
|
|
108
|
+
return HttpResponse(response=_response, data=_data)
|
|
109
|
+
_response_json = _response.json()
|
|
110
|
+
except JSONDecodeError:
|
|
111
|
+
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
|
|
112
|
+
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class AsyncRawTokensClient:
|
|
116
|
+
def __init__(self, *, client_wrapper: AsyncClientWrapper):
|
|
117
|
+
self._client_wrapper = client_wrapper
|
|
118
|
+
|
|
119
|
+
async def create(
|
|
120
|
+
self, *, code: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None
|
|
121
|
+
) -> AsyncHttpResponse[CreateTokensResponse]:
|
|
122
|
+
"""
|
|
123
|
+
Exchange an authorization code for access and refresh tokens.
|
|
124
|
+
|
|
125
|
+
Parameters
|
|
126
|
+
----------
|
|
127
|
+
code : typing.Optional[str]
|
|
128
|
+
The client secret of the application
|
|
129
|
+
|
|
130
|
+
request_options : typing.Optional[RequestOptions]
|
|
131
|
+
Request-specific configuration.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
AsyncHttpResponse[CreateTokensResponse]
|
|
136
|
+
Successful response
|
|
137
|
+
"""
|
|
138
|
+
_response = await self._client_wrapper.httpx_client.request(
|
|
139
|
+
"oauth2/token",
|
|
140
|
+
method="POST",
|
|
141
|
+
json={
|
|
142
|
+
"code": code,
|
|
143
|
+
"grant_type": "authorization_code",
|
|
144
|
+
},
|
|
145
|
+
headers={
|
|
146
|
+
"content-type": "application/json",
|
|
147
|
+
},
|
|
148
|
+
request_options=request_options,
|
|
149
|
+
omit=OMIT,
|
|
150
|
+
)
|
|
151
|
+
try:
|
|
152
|
+
if 200 <= _response.status_code < 300:
|
|
153
|
+
_data = typing.cast(
|
|
154
|
+
CreateTokensResponse,
|
|
155
|
+
parse_obj_as(
|
|
156
|
+
type_=CreateTokensResponse, # type: ignore
|
|
157
|
+
object_=_response.json(),
|
|
158
|
+
),
|
|
159
|
+
)
|
|
160
|
+
return AsyncHttpResponse(response=_response, data=_data)
|
|
161
|
+
_response_json = _response.json()
|
|
162
|
+
except JSONDecodeError:
|
|
163
|
+
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
|
|
164
|
+
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
|
|
165
|
+
|
|
166
|
+
async def revoke(
|
|
167
|
+
self, *, token: str, request_options: typing.Optional[RequestOptions] = None
|
|
168
|
+
) -> AsyncHttpResponse[CreateTokensResponse]:
|
|
169
|
+
"""
|
|
170
|
+
Invalidates access tokens and refresh tokens for that organization
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
token : str
|
|
175
|
+
The token to revoke
|
|
176
|
+
|
|
177
|
+
request_options : typing.Optional[RequestOptions]
|
|
178
|
+
Request-specific configuration.
|
|
179
|
+
|
|
180
|
+
Returns
|
|
181
|
+
-------
|
|
182
|
+
AsyncHttpResponse[CreateTokensResponse]
|
|
183
|
+
Successful response
|
|
184
|
+
"""
|
|
185
|
+
_response = await self._client_wrapper.httpx_client.request(
|
|
186
|
+
"oauth2/revoke",
|
|
187
|
+
method="POST",
|
|
188
|
+
json={
|
|
189
|
+
"token": token,
|
|
190
|
+
},
|
|
191
|
+
headers={
|
|
192
|
+
"content-type": "application/json",
|
|
193
|
+
},
|
|
194
|
+
request_options=request_options,
|
|
195
|
+
omit=OMIT,
|
|
196
|
+
)
|
|
197
|
+
try:
|
|
198
|
+
if 200 <= _response.status_code < 300:
|
|
199
|
+
_data = typing.cast(
|
|
200
|
+
CreateTokensResponse,
|
|
201
|
+
parse_obj_as(
|
|
202
|
+
type_=CreateTokensResponse, # type: ignore
|
|
203
|
+
object_=_response.json(),
|
|
204
|
+
),
|
|
205
|
+
)
|
|
206
|
+
return AsyncHttpResponse(response=_response, data=_data)
|
|
207
|
+
_response_json = _response.json()
|
|
208
|
+
except JSONDecodeError:
|
|
209
|
+
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
|
|
210
|
+
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
|
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
import typing
|
|
4
4
|
|
|
5
5
|
import pydantic
|
|
6
|
-
from
|
|
6
|
+
from ....core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class CreateTokensResponse(UniversalBaseModel):
|
|
10
10
|
access_token: str
|
|
11
11
|
token_type: str
|
|
12
12
|
expires_in: int
|
|
13
|
+
refresh_token: str
|
|
14
|
+
scope: str
|
|
13
15
|
|
|
14
16
|
if IS_PYDANTIC_V2:
|
|
15
17
|
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
|
samsara/client.py
CHANGED
|
@@ -160,8 +160,8 @@ class Samsara:
|
|
|
160
160
|
self.users = UsersClient(client_wrapper=self._client_wrapper)
|
|
161
161
|
self.v_1_messages = V1MessagesClient(client_wrapper=self._client_wrapper)
|
|
162
162
|
self.webhooks = WebhooksClient(client_wrapper=self._client_wrapper)
|
|
163
|
-
self.auth = AuthClient(client_wrapper=self._client_wrapper)
|
|
164
163
|
self.alerts = AlertsClient(client_wrapper=self._client_wrapper)
|
|
164
|
+
self.auth = AuthClient(client_wrapper=self._client_wrapper)
|
|
165
165
|
self.cameras = CamerasClient(client_wrapper=self._client_wrapper)
|
|
166
166
|
self.coaching = CoachingClient(client_wrapper=self._client_wrapper)
|
|
167
167
|
self.ifta = IftaClient(client_wrapper=self._client_wrapper)
|
|
@@ -273,8 +273,8 @@ class AsyncSamsara:
|
|
|
273
273
|
self.users = AsyncUsersClient(client_wrapper=self._client_wrapper)
|
|
274
274
|
self.v_1_messages = AsyncV1MessagesClient(client_wrapper=self._client_wrapper)
|
|
275
275
|
self.webhooks = AsyncWebhooksClient(client_wrapper=self._client_wrapper)
|
|
276
|
-
self.auth = AsyncAuthClient(client_wrapper=self._client_wrapper)
|
|
277
276
|
self.alerts = AsyncAlertsClient(client_wrapper=self._client_wrapper)
|
|
277
|
+
self.auth = AsyncAuthClient(client_wrapper=self._client_wrapper)
|
|
278
278
|
self.cameras = AsyncCamerasClient(client_wrapper=self._client_wrapper)
|
|
279
279
|
self.coaching = AsyncCoachingClient(client_wrapper=self._client_wrapper)
|
|
280
280
|
self.ifta = AsyncIftaClient(client_wrapper=self._client_wrapper)
|
samsara/core/client_wrapper.py
CHANGED
|
@@ -20,10 +20,10 @@ class BaseClientWrapper:
|
|
|
20
20
|
|
|
21
21
|
def get_headers(self) -> typing.Dict[str, str]:
|
|
22
22
|
headers: typing.Dict[str, str] = {
|
|
23
|
-
"User-Agent": "samsara-api/0.0.
|
|
23
|
+
"User-Agent": "samsara-api/0.0.4",
|
|
24
24
|
"X-Fern-Language": "Python",
|
|
25
25
|
"X-Fern-SDK-Name": "samsara-api",
|
|
26
|
-
"X-Fern-SDK-Version": "0.0.
|
|
26
|
+
"X-Fern-SDK-Version": "0.0.4",
|
|
27
27
|
}
|
|
28
28
|
headers["Authorization"] = f"Bearer {self._get_token()}"
|
|
29
29
|
return headers
|
|
@@ -19,7 +19,7 @@ class WorkOrderItemObjectRequestBody(UniversalBaseModel):
|
|
|
19
19
|
|
|
20
20
|
type: WorkOrderItemObjectRequestBodyType = pydantic.Field()
|
|
21
21
|
"""
|
|
22
|
-
The type of item. Valid values: `DVIR`, `FAULT`, `FORM`, `ISSUE`, `ITEM_TYPE_UNSPECIFIED`, `SCHEDULED_MAINTENANCE`
|
|
22
|
+
The type of item. Valid values: `DVIR`, `FAULT`, `FORM`, `ISSUE`, `ITEM_TYPE_UNSPECIFIED`, `MAINTENANCE_PREDICTION_EVENT`, `SCHEDULED_MAINTENANCE`
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
25
|
if IS_PYDANTIC_V2:
|
|
@@ -3,5 +3,14 @@
|
|
|
3
3
|
import typing
|
|
4
4
|
|
|
5
5
|
WorkOrderItemObjectRequestBodyType = typing.Union[
|
|
6
|
-
typing.Literal[
|
|
6
|
+
typing.Literal[
|
|
7
|
+
"DVIR",
|
|
8
|
+
"FAULT",
|
|
9
|
+
"FORM",
|
|
10
|
+
"ISSUE",
|
|
11
|
+
"ITEM_TYPE_UNSPECIFIED",
|
|
12
|
+
"MAINTENANCE_PREDICTION_EVENT",
|
|
13
|
+
"SCHEDULED_MAINTENANCE",
|
|
14
|
+
],
|
|
15
|
+
typing.Any,
|
|
7
16
|
]
|
|
@@ -19,7 +19,7 @@ class WorkOrderItemObjectResponseBody(UniversalBaseModel):
|
|
|
19
19
|
|
|
20
20
|
type: WorkOrderItemObjectResponseBodyType = pydantic.Field()
|
|
21
21
|
"""
|
|
22
|
-
The type of item. Valid values: `DVIR`, `FAULT`, `FORM`, `ISSUE`, `ITEM_TYPE_UNSPECIFIED`, `SCHEDULED_MAINTENANCE`
|
|
22
|
+
The type of item. Valid values: `DVIR`, `FAULT`, `FORM`, `ISSUE`, `ITEM_TYPE_UNSPECIFIED`, `MAINTENANCE_PREDICTION_EVENT`, `SCHEDULED_MAINTENANCE`
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
25
|
if IS_PYDANTIC_V2:
|
|
@@ -3,5 +3,14 @@
|
|
|
3
3
|
import typing
|
|
4
4
|
|
|
5
5
|
WorkOrderItemObjectResponseBodyType = typing.Union[
|
|
6
|
-
typing.Literal[
|
|
6
|
+
typing.Literal[
|
|
7
|
+
"DVIR",
|
|
8
|
+
"FAULT",
|
|
9
|
+
"FORM",
|
|
10
|
+
"ISSUE",
|
|
11
|
+
"ITEM_TYPE_UNSPECIFIED",
|
|
12
|
+
"MAINTENANCE_PREDICTION_EVENT",
|
|
13
|
+
"SCHEDULED_MAINTENANCE",
|
|
14
|
+
],
|
|
15
|
+
typing.Any,
|
|
7
16
|
]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import hashlib
|
|
3
|
+
import hmac
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def verify_signature(
|
|
7
|
+
*,
|
|
8
|
+
request_body: str,
|
|
9
|
+
signature_header: str,
|
|
10
|
+
signature_key: str,
|
|
11
|
+
notification_url: str,
|
|
12
|
+
) -> bool:
|
|
13
|
+
"""
|
|
14
|
+
Verifies and validates an event notification. See the `documentation`_ for more details.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
request_body: The JSON body of the request.
|
|
18
|
+
signature_header: The value for the `x-samsara-hmacsha256-signature` header.
|
|
19
|
+
signature_key: The signature key from the Samsara Developer portal for the webhook subscription.
|
|
20
|
+
notification_url: The notification endpoint URL as defined in the Samsara Developer portal for the webhook
|
|
21
|
+
subscription.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
bool: True if the signature is valid, indicating that the event can be trusted as it came from Samsara.
|
|
25
|
+
False if the signature validation fails, indicating that the event did not come from Samsara, so it may
|
|
26
|
+
be malicious and should be discarded.
|
|
27
|
+
|
|
28
|
+
Raises:
|
|
29
|
+
ValueError: if `signature_key` or `notification_url` are empty.
|
|
30
|
+
"""
|
|
31
|
+
if not request_body:
|
|
32
|
+
return False
|
|
33
|
+
if not signature_key:
|
|
34
|
+
raise ValueError("signature_key is empty")
|
|
35
|
+
if not notification_url:
|
|
36
|
+
raise ValueError("notification_url is empty")
|
|
37
|
+
|
|
38
|
+
# Perform UTF-8 encoding to bytes
|
|
39
|
+
payload = notification_url + request_body
|
|
40
|
+
payload_bytes = payload.encode("utf-8")
|
|
41
|
+
signature_header_bytes = signature_header.encode("utf-8")
|
|
42
|
+
signature_key_bytes = signature_key.encode("utf-8")
|
|
43
|
+
|
|
44
|
+
# Compute the hash value
|
|
45
|
+
hashing_obj = hmac.new(
|
|
46
|
+
key=signature_key_bytes, msg=payload_bytes, digestmod=hashlib.sha256
|
|
47
|
+
)
|
|
48
|
+
hash_bytes = hashing_obj.digest()
|
|
49
|
+
|
|
50
|
+
# Compare the computed hash vs the value in the signature header
|
|
51
|
+
hash_base64 = base64.b64encode(hash_bytes)
|
|
52
|
+
return hmac.compare_digest(hash_base64, signature_header_bytes)
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: samsara-api
|
|
3
|
+
Version: 0.0.4
|
|
4
|
+
Summary:
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.8,<4.0
|
|
7
|
+
Classifier: Intended Audience :: Developers
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: MacOS
|
|
10
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Operating System :: POSIX
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Dist: httpx (>=0.21.2)
|
|
24
|
+
Requires-Dist: pydantic (>=1.9.2)
|
|
25
|
+
Requires-Dist: pydantic-core (>=2.18.2,<3.0.0)
|
|
26
|
+
Requires-Dist: typing_extensions (>=4.0.0)
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# Samsara Python Library
|
|
30
|
+
|
|
31
|
+
[](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=https%3A%2F%2Fgithub.com%2Fsamsarahq%2Fsamsara-python-sdk)
|
|
32
|
+
[](https://pypi.python.org/pypi/samsara-api)
|
|
33
|
+
|
|
34
|
+
The Samsara Python library provides convenient access to the Samsara API from Python.
|
|
35
|
+
|
|
36
|
+
## Documentation
|
|
37
|
+
|
|
38
|
+
API reference documentation is available [here](https://developers.samsara.com/reference/overview).
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
pip install samsara-api
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Reference
|
|
47
|
+
|
|
48
|
+
A full reference for this library is available [here](https://github.com/samsarahq/samsara-python-sdk/blob/HEAD/./reference.md).
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
Instantiate and use the client with the following:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from samsara import Samsara
|
|
56
|
+
|
|
57
|
+
client = Samsara(
|
|
58
|
+
token="YOUR_TOKEN",
|
|
59
|
+
)
|
|
60
|
+
response = client.vehicles.stats.list()
|
|
61
|
+
for item in response:
|
|
62
|
+
yield item
|
|
63
|
+
# alternatively, you can paginate page-by-page
|
|
64
|
+
for page in response.iter_pages():
|
|
65
|
+
yield page
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Async Client
|
|
69
|
+
|
|
70
|
+
The SDK also exports an `async` client so that you can make non-blocking calls to our API.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import asyncio
|
|
74
|
+
|
|
75
|
+
from samsara import AsyncSamsara
|
|
76
|
+
|
|
77
|
+
client = AsyncSamsara(
|
|
78
|
+
token="YOUR_TOKEN",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def main() -> None:
|
|
83
|
+
response = await client.vehicles.stats.list()
|
|
84
|
+
async for item in response:
|
|
85
|
+
yield item
|
|
86
|
+
|
|
87
|
+
# alternatively, you can paginate page-by-page
|
|
88
|
+
async for page in response.iter_pages():
|
|
89
|
+
yield page
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
asyncio.run(main())
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Exception Handling
|
|
96
|
+
|
|
97
|
+
When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error
|
|
98
|
+
will be thrown.
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from samsara.core.api_error import ApiError
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
client.vehicles.stats.list(...)
|
|
105
|
+
except ApiError as e:
|
|
106
|
+
print(e.status_code)
|
|
107
|
+
print(e.body)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Pagination
|
|
111
|
+
|
|
112
|
+
Paginated requests will return a `SyncPager` or `AsyncPager`, which can be used as generators for the underlying object.
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from samsara import Samsara
|
|
116
|
+
|
|
117
|
+
client = Samsara(
|
|
118
|
+
token="YOUR_TOKEN",
|
|
119
|
+
)
|
|
120
|
+
response = client.addresses.list()
|
|
121
|
+
for item in response:
|
|
122
|
+
yield item
|
|
123
|
+
# alternatively, you can paginate page-by-page
|
|
124
|
+
for page in response.iter_pages():
|
|
125
|
+
yield page
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Webhook Signature Verification
|
|
129
|
+
|
|
130
|
+
The SDK provides utility methods that allow you to verify webhook signatures and ensure
|
|
131
|
+
that all webhook events originate from Samsara. The `verify_signature` method will verify
|
|
132
|
+
the signature.
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from samsara.utils.webhooks_helper import verify_signature
|
|
136
|
+
|
|
137
|
+
is_valid = verify_signature(
|
|
138
|
+
request_body=request_body,
|
|
139
|
+
signature_header=request.headers['x-samsara-hmacsha256-signature'],
|
|
140
|
+
signature_key="YOUR_SIGNATURE_KEY",
|
|
141
|
+
notification_url="https://example.com/webhook", # The URL where event notifications are sent.
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Advanced
|
|
146
|
+
|
|
147
|
+
### Access Raw Response Data
|
|
148
|
+
|
|
149
|
+
The SDK provides access to raw response data, including headers, through the `.with_raw_response` property.
|
|
150
|
+
The `.with_raw_response` property returns a "raw" client that can be used to access the `.headers` and `.data` attributes.
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from samsara import Samsara
|
|
154
|
+
|
|
155
|
+
client = Samsara(
|
|
156
|
+
...,
|
|
157
|
+
)
|
|
158
|
+
response = client.vehicles.stats.with_raw_response.list(...)
|
|
159
|
+
print(response.headers) # access the response headers
|
|
160
|
+
print(response.data) # access the underlying object
|
|
161
|
+
pager = client.addresses.list(...)
|
|
162
|
+
print(pager.response.headers) # access the response headers for the first page
|
|
163
|
+
for item in pager:
|
|
164
|
+
print(item) # access the underlying object(s)
|
|
165
|
+
for page in pager.iter_pages():
|
|
166
|
+
print(page.response.headers) # access the response headers for each page
|
|
167
|
+
for item in page:
|
|
168
|
+
print(item) # access the underlying object(s)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Retries
|
|
172
|
+
|
|
173
|
+
The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long
|
|
174
|
+
as the request is deemed retryable and the number of retry attempts has not grown larger than the configured
|
|
175
|
+
retry limit (default: 2).
|
|
176
|
+
|
|
177
|
+
A request is deemed retryable when any of the following HTTP status codes is returned:
|
|
178
|
+
|
|
179
|
+
- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout)
|
|
180
|
+
- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests)
|
|
181
|
+
- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors)
|
|
182
|
+
|
|
183
|
+
Use the `max_retries` request option to configure this behavior.
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
client.vehicles.stats.list(..., request_options={
|
|
187
|
+
"max_retries": 1
|
|
188
|
+
})
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Timeouts
|
|
192
|
+
|
|
193
|
+
The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level.
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
|
|
197
|
+
from samsara import Samsara
|
|
198
|
+
|
|
199
|
+
client = Samsara(
|
|
200
|
+
...,
|
|
201
|
+
timeout=20.0,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# Override timeout for a specific method
|
|
206
|
+
client.vehicles.stats.list(..., request_options={
|
|
207
|
+
"timeout_in_seconds": 1
|
|
208
|
+
})
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Custom Client
|
|
212
|
+
|
|
213
|
+
You can override the `httpx` client to customize it for your use-case. Some common use-cases include support for proxies
|
|
214
|
+
and transports.
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
import httpx
|
|
218
|
+
from samsara import Samsara
|
|
219
|
+
|
|
220
|
+
client = Samsara(
|
|
221
|
+
...,
|
|
222
|
+
httpx_client=httpx.Client(
|
|
223
|
+
proxies="http://my.test.proxy.example.com",
|
|
224
|
+
transport=httpx.HTTPTransport(local_address="0.0.0.0"),
|
|
225
|
+
),
|
|
226
|
+
)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Contributing
|
|
230
|
+
|
|
231
|
+
While we value open-source contributions to this SDK, this library is generated programmatically.
|
|
232
|
+
Additions made directly to this library would have to be moved over to our generation code,
|
|
233
|
+
otherwise they would be overwritten upon the next generated release. Feel free to open a PR as
|
|
234
|
+
a proof of concept, but know that we will not be able to merge it as-is. We suggest opening
|
|
235
|
+
an issue first to discuss with us!
|
|
236
|
+
|
|
237
|
+
On the other hand, contributions to the README are always very welcome!
|
|
238
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
samsara/__init__.py,sha256=
|
|
1
|
+
samsara/__init__.py,sha256=92NvaQpSlOyKeuVpidF-8CqSBNqs-mUz8-ISlOKTWA4,243381
|
|
2
2
|
samsara/addresses/__init__.py,sha256=VwNk87ydCDVCvXs50iVyOtmRChWMryj-qaD37dmXEHE,271
|
|
3
3
|
samsara/addresses/client.py,sha256=NmZDd3Q5xZsNDD-NlBZ6dIxIxHNQUVs9DDZ-3b38qQQ,31432
|
|
4
4
|
samsara/addresses/raw_client.py,sha256=dF0VKXWrRMpZ_FYBwT5Z58StWm1nE7VCQVE4NouRvF0,39437
|
|
@@ -41,11 +41,14 @@ samsara/attributes/types/create_attribute_request_entity_type.py,sha256=YVWNyZIS
|
|
|
41
41
|
samsara/attributes/types/update_attribute_request_attribute_type.py,sha256=nmtbsmoh3lh6NkGW6UVDbrwLSf_Kyp0FmJJyzC60yfY,179
|
|
42
42
|
samsara/attributes/types/update_attribute_request_attribute_value_quantity.py,sha256=UIX4mHT8iNREa_H6CuXcpgIH42KKKQRpnUgxpoYy_P4,187
|
|
43
43
|
samsara/attributes/types/update_attribute_request_entity_type.py,sha256=O3P4cbLmICVFdTzcEUWq4HSpheSpWE-TZfz4HElX9bM,175
|
|
44
|
-
samsara/auth/__init__.py,sha256=
|
|
45
|
-
samsara/auth/client.py,sha256=
|
|
46
|
-
samsara/auth/raw_client.py,sha256=
|
|
47
|
-
samsara/auth/
|
|
48
|
-
samsara/auth/
|
|
44
|
+
samsara/auth/__init__.py,sha256=9yWTBw0ONdi-3tmC4kjbCpApiVniv2aK5csdVnHOI20,193
|
|
45
|
+
samsara/auth/client.py,sha256=iGRrTmTAyBspdf_-HcGqn0Pb0s1HQwco4PoSphDFwEU,1236
|
|
46
|
+
samsara/auth/raw_client.py,sha256=Xjah5juxJ0L8FfHyZBKMoOVHWxzvqTv4qtQixxA3jDc,405
|
|
47
|
+
samsara/auth/tokens/__init__.py,sha256=gbhdUVQ3-j2WiNIQN0-h_5RODBZ4gvJgteMmSdgtscQ,161
|
|
48
|
+
samsara/auth/tokens/client.py,sha256=g6AlGPqOt3gHPDrz2q9Glemm7jEGHFiGFnijUY4a66Q,4959
|
|
49
|
+
samsara/auth/tokens/raw_client.py,sha256=ZEnP5Xi8DkY-SEsiQkNAsB3b06AIoal5_cmRcW_Zuxo,7528
|
|
50
|
+
samsara/auth/tokens/types/__init__.py,sha256=XmB_JdsWYwBG-Tz8M8Qj3rfQJWmQtPXDtsFiqy_VuNs,178
|
|
51
|
+
samsara/auth/tokens/types/create_tokens_response.py,sha256=wcHMN9xNW0DfH4-wXKa1l1mY14BmMxl6UDLJGLwNDsY,615
|
|
49
52
|
samsara/cameras/__init__.py,sha256=6oUbRWZI3I0mSd4P2kGnp-bNVIQoyoR6J4_1OXzluws,361
|
|
50
53
|
samsara/cameras/client.py,sha256=HdYw4W_X5eIuR9zOYL9IyIrIyeYRIicK9RNdsKPnKJg,1259
|
|
51
54
|
samsara/cameras/media/__init__.py,sha256=bXxa72UiJqERML9R4PL4PCNWKFZhTCrly5ZvF7__7rA,634
|
|
@@ -65,7 +68,7 @@ samsara/cameras/raw_client.py,sha256=X7YzaI5nwgwczR-A4OwhSK54TQW6VfCzs00pgNTMuUU
|
|
|
65
68
|
samsara/carrier_proposed_assignments/__init__.py,sha256=_VhToAyIt_5axN6CLJwtxg3-CO7THa_23pbUzqhXJa4,85
|
|
66
69
|
samsara/carrier_proposed_assignments/client.py,sha256=UDeSIJ17UU_TboofUe-Y8uIY1zBNNtt9Ss7msqIiLac,17860
|
|
67
70
|
samsara/carrier_proposed_assignments/raw_client.py,sha256=NW9vhF3XwbYEK5nQDzhGYzxkEx4a_wIqL4gqU-npmn0,22836
|
|
68
|
-
samsara/client.py,sha256=
|
|
71
|
+
samsara/client.py,sha256=852-56uS8ECcrGvhCp-ykxHYJLSuy7VRe0F1iVPk4tw,17115
|
|
69
72
|
samsara/coaching/__init__.py,sha256=LfmwpXue7wsY-q1lfvX-AYcU4JdP1x0zVtofVAh4pAA,186
|
|
70
73
|
samsara/coaching/client.py,sha256=ULolm0GnWQmfOVzY1FE8vBMaLl-m0InJutgkQJ4lfps,1606
|
|
71
74
|
samsara/coaching/driver_coach_assignments/__init__.py,sha256=_VhToAyIt_5axN6CLJwtxg3-CO7THa_23pbUzqhXJa4,85
|
|
@@ -80,7 +83,7 @@ samsara/contacts/client.py,sha256=-DvPNxYQZboAYekVkG22JH8GLN5Pb441W0pD7amZue8,19
|
|
|
80
83
|
samsara/contacts/raw_client.py,sha256=nMFcdeS_I53QS01zxF2yeY6-t6cbQ7ZeZeqUxKcH7yI,27530
|
|
81
84
|
samsara/core/__init__.py,sha256=4POYbbRlgR7TwGFY5nmHpaa7nFgXOX_pYHcN1Ezh2p8,1643
|
|
82
85
|
samsara/core/api_error.py,sha256=44vPoTyWN59gonCIZMdzw7M1uspygiLnr3GNFOoVL2Q,614
|
|
83
|
-
samsara/core/client_wrapper.py,sha256=
|
|
86
|
+
samsara/core/client_wrapper.py,sha256=9ANX-olJY3Lv-BmJeGdRh408Cu9mhWO74esp7VjH7jI,2236
|
|
84
87
|
samsara/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
|
85
88
|
samsara/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
|
|
86
89
|
samsara/core/force_multipart.py,sha256=awxh5MtcRYe74ehY8U76jzv6fYM_w_D3Rur7KQQzSDk,429
|
|
@@ -2514,10 +2517,10 @@ samsara/types/work_order_attachment_object_response_body.py,sha256=Ce66d8VXVdpVL
|
|
|
2514
2517
|
samsara/types/work_order_attachment_object_response_body_processing_status.py,sha256=w82JZVehbfKHNKnX6yGJ4ZioPLQeU-Yl5g5-LDdVe80,220
|
|
2515
2518
|
samsara/types/work_order_discount_object_request_body.py,sha256=ypiPPoF0rkmdpYsBjq0rF-Q9e7w4uYEGnyXK9fspt5s,1074
|
|
2516
2519
|
samsara/types/work_order_discount_object_response_body.py,sha256=Z2Xy7CmOcUAduOpIKSVR2l_WqqJIys1zxH_-e8M2-NA,1078
|
|
2517
|
-
samsara/types/work_order_item_object_request_body.py,sha256=
|
|
2518
|
-
samsara/types/work_order_item_object_request_body_type.py,sha256=
|
|
2519
|
-
samsara/types/work_order_item_object_response_body.py,sha256=
|
|
2520
|
-
samsara/types/work_order_item_object_response_body_type.py,sha256=
|
|
2520
|
+
samsara/types/work_order_item_object_request_body.py,sha256=IR7-f72UooE1Sz5NZtihDMmdZFwsrlj0nhKonrWfx_o,965
|
|
2521
|
+
samsara/types/work_order_item_object_request_body_type.py,sha256=vguZe7JH6HLX_7I0qaCErNPllbGiPw1COKxVnoBtYEg,348
|
|
2522
|
+
samsara/types/work_order_item_object_response_body.py,sha256=1amSCQKCs6IrqLv9UsSJmaQT4LprDgNVGA688RyqVxg,969
|
|
2523
|
+
samsara/types/work_order_item_object_response_body_type.py,sha256=aHS_rnbm5qvc3IghV3sGvE6qOV6057U6X8OLJ3YjOhM,349
|
|
2521
2524
|
samsara/types/work_order_money_object_request_body.py,sha256=Urpvg5aIH9fx6VStIclHqVhUVp8brDi8_YgD2fb1JUE,816
|
|
2522
2525
|
samsara/types/work_order_money_object_response_body.py,sha256=7u8dbgEpMRr7s4Yxe2RCEx7l676q7DR-rOkMoNsjkC4,817
|
|
2523
2526
|
samsara/types/work_order_object_response_body.py,sha256=NXgUY522IAzRXhINXnDSc7L8PYh0Q1qRBAWyntbEEvI,5341
|
|
@@ -2610,6 +2613,7 @@ samsara/users/raw_client.py,sha256=aeoDOnJ4dMB4O3WocJXATsCiK-zNHodq09tBBYgaD8w,3
|
|
|
2610
2613
|
samsara/users/types/__init__.py,sha256=yVRLnN-wUYfYcYWwrgJSM8GIRnVdqk207hLlDqal0GM,293
|
|
2611
2614
|
samsara/users/types/create_user_request_auth_type.py,sha256=rdNs3jjJv7IY0koJ6vpgdE79OskOYbA6zUrYMijKmMw,168
|
|
2612
2615
|
samsara/users/types/update_user_request_auth_type.py,sha256=E2r4bzBS0lwPCWrUwC37GfMlt8Hv-YGw_3abv6wNN-Q,168
|
|
2616
|
+
samsara/utils/webhooks_helper.py,sha256=oosLIOouDmGa7a6xj4sI_EfUSvtzfqB6uXXaUKaagP8,1911
|
|
2613
2617
|
samsara/v_1_messages/__init__.py,sha256=_VhToAyIt_5axN6CLJwtxg3-CO7THa_23pbUzqhXJa4,85
|
|
2614
2618
|
samsara/v_1_messages/client.py,sha256=rhUwhh0L3IcgD3sglfNtZMyeLM2j1ogrpMe4902yTos,9734
|
|
2615
2619
|
samsara/v_1_messages/raw_client.py,sha256=3_5CVjrIRu2VREzYtcV7sQBz0K0F_tOwPDNcMSGDBOw,11896
|
|
@@ -2654,7 +2658,7 @@ samsara/webhooks/types/__init__.py,sha256=gZew8TwQaEiQWvSAewo9htvyM3UqVSZc1UJ2Aa
|
|
|
2654
2658
|
samsara/webhooks/types/webhooks_patch_webhook_request_body_version.py,sha256=WaQrAnZGbaz7eQ6fpnX77L5nb8dLwuUkEQdvx5mCGAw,224
|
|
2655
2659
|
samsara/webhooks/types/webhooks_post_webhooks_request_body_event_types_item.py,sha256=-whKIc9UjBEIXZUxwkDULLSxR0Dg_-6KxRUjcfyvTuQ,927
|
|
2656
2660
|
samsara/webhooks/types/webhooks_post_webhooks_request_body_version.py,sha256=1HR1l6_Qc_jS_mf_UkguxgMiiIeAiwaX7jjOTQfG1bw,224
|
|
2657
|
-
samsara_api-0.0.
|
|
2658
|
-
samsara_api-0.0.
|
|
2659
|
-
samsara_api-0.0.
|
|
2660
|
-
samsara_api-0.0.
|
|
2661
|
+
samsara_api-0.0.4.dist-info/LICENSE,sha256=uX-nl5hOEHp4uvQPCcpde-doNbQhV5uRQNwkB474IoQ,1064
|
|
2662
|
+
samsara_api-0.0.4.dist-info/METADATA,sha256=9uB-xryoMS0LeZ1qLICrPttG2gNMHa6AQ_nNQiyyI28,6831
|
|
2663
|
+
samsara_api-0.0.4.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
|
2664
|
+
samsara_api-0.0.4.dist-info/RECORD,,
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: samsara-api
|
|
3
|
-
Version: 0.0.2
|
|
4
|
-
Summary:
|
|
5
|
-
License: MIT
|
|
6
|
-
Requires-Python: >=3.8,<4.0
|
|
7
|
-
Classifier: Intended Audience :: Developers
|
|
8
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
-
Classifier: Operating System :: MacOS
|
|
10
|
-
Classifier: Operating System :: Microsoft :: Windows
|
|
11
|
-
Classifier: Operating System :: OS Independent
|
|
12
|
-
Classifier: Operating System :: POSIX
|
|
13
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
-
Classifier: Programming Language :: Python
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
-
Classifier: Typing :: Typed
|
|
23
|
-
Requires-Dist: httpx (>=0.21.2)
|
|
24
|
-
Requires-Dist: pydantic (>=1.9.2)
|
|
25
|
-
Requires-Dist: pydantic-core (>=2.18.2,<3.0.0)
|
|
26
|
-
Requires-Dist: typing_extensions (>=4.0.0)
|
|
27
|
-
Description-Content-Type: text/markdown
|
|
28
|
-
|
|
29
|
-
|
|
File without changes
|
|
File without changes
|