dimo-python-sdk 1.1.1__tar.gz → 1.3.0__tar.gz

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.
Files changed (29) hide show
  1. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/PKG-INFO +27 -4
  2. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/README.md +26 -3
  3. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/api/auth.py +6 -2
  4. dimo_python_sdk-1.3.0/dimo/api/token_exchange.py +85 -0
  5. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/dimo.py +6 -3
  6. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/graphql/identity.py +18 -0
  7. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/graphql/telemetry.py +19 -4
  8. dimo_python_sdk-1.3.0/dimo/permission_decoder.py +13 -0
  9. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo_python_sdk.egg-info/PKG-INFO +27 -4
  10. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo_python_sdk.egg-info/SOURCES.txt +1 -0
  11. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/pyproject.toml +1 -1
  12. dimo_python_sdk-1.1.1/dimo/api/token_exchange.py +0 -33
  13. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/LICENSE +0 -0
  14. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/__init__.py +0 -0
  15. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/api/__init__.py +0 -0
  16. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/api/attestation.py +0 -0
  17. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/api/device_definitions.py +0 -0
  18. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/api/trips.py +0 -0
  19. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/api/valuations.py +0 -0
  20. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/constants.py +0 -0
  21. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/environments.py +0 -0
  22. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/errors.py +0 -0
  23. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/eth_signer.py +0 -0
  24. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/graphql/__init__.py +0 -0
  25. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo/request.py +0 -0
  26. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo_python_sdk.egg-info/dependency_links.txt +0 -0
  27. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo_python_sdk.egg-info/requires.txt +0 -0
  28. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/dimo_python_sdk.egg-info/top_level.txt +0 -0
  29. {dimo_python_sdk-1.1.1 → dimo_python_sdk-1.3.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimo-python-sdk
3
- Version: 1.1.1
3
+ Version: 1.3.0
4
4
  Summary: DIMO SDK in Python
5
5
  Author-email: Barrett Kowalsky <barrettkowalsky@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/DIMO-Network/dimo-python-sdk
@@ -144,15 +144,38 @@ For the end users of your application, they will need to share their vehicle per
144
144
 
145
145
  Typically, any endpoints that uses a NFT `tokenId` in path parameters will require JWTs. You can use this flow to obtain a privilege token.
146
146
 
147
+ There are now two methods of invoking the token exchange to obtain a Vehicle JWT: a streamlined method and a more verbose method:
148
+
149
+ ##### Streamlined Method (Recommended):
150
+ This method uses your client_id to check the privileges for a specified token_id via a query to the Identity API. This strips away the need to provide a privileges list:
151
+
152
+ ```python
153
+ # Start by obtaining a Developer JWT
154
+ auth_header = dimo.auth.get_token(
155
+ client_id = '<client_id>',
156
+ domain = '<domain>',
157
+ private_key = '<private_key>'
158
+ )
159
+
160
+ dev_jwt = auth_header["access_token"]
161
+
162
+ # Then use the simplified method for getting a Vehicle JWT
163
+
164
+ get_vehicle_jwt = dimo.token_exchange.exchange(
165
+ developer_jwt = dev_jwt
166
+ token_id ="<token_id>"
167
+ )
168
+ vehicle_jwt = get_vehicle_jwt['token']
169
+
170
+ ```
171
+ ##### Verbose Method:
172
+ This method requires you to explicity provide the list of privileges that this token_id has granted to your developer license. For more information, review the [Permissions Contract (SACD) Documentation](https://docs.dimo.org/developer-platform/developer-guide/permissions-contract-sacd).
147
173
  ```python
148
174
 
149
175
  get_vehicle_jwt = dimo.token_exchange.exchange(
150
176
  developer_jwt = dev_jwt,
151
- # The Developer JWT you received using either the three step function calls, or the .get_token() shortcut
152
177
  privileges=[1, 3, 4, 5],
153
- # The privileges you've set for this vehicle, in list format (e.g. [1, 3, 4, 5])
154
178
  token_id="<token_id>"
155
- # The Vehicle NFT Token ID that you are requesting permission to
156
179
  )
157
180
  vehicle_jwt = get_vehicle_jwt['token']
158
181
  ```
@@ -126,15 +126,38 @@ For the end users of your application, they will need to share their vehicle per
126
126
 
127
127
  Typically, any endpoints that uses a NFT `tokenId` in path parameters will require JWTs. You can use this flow to obtain a privilege token.
128
128
 
129
+ There are now two methods of invoking the token exchange to obtain a Vehicle JWT: a streamlined method and a more verbose method:
130
+
131
+ ##### Streamlined Method (Recommended):
132
+ This method uses your client_id to check the privileges for a specified token_id via a query to the Identity API. This strips away the need to provide a privileges list:
133
+
134
+ ```python
135
+ # Start by obtaining a Developer JWT
136
+ auth_header = dimo.auth.get_token(
137
+ client_id = '<client_id>',
138
+ domain = '<domain>',
139
+ private_key = '<private_key>'
140
+ )
141
+
142
+ dev_jwt = auth_header["access_token"]
143
+
144
+ # Then use the simplified method for getting a Vehicle JWT
145
+
146
+ get_vehicle_jwt = dimo.token_exchange.exchange(
147
+ developer_jwt = dev_jwt
148
+ token_id ="<token_id>"
149
+ )
150
+ vehicle_jwt = get_vehicle_jwt['token']
151
+
152
+ ```
153
+ ##### Verbose Method:
154
+ This method requires you to explicity provide the list of privileges that this token_id has granted to your developer license. For more information, review the [Permissions Contract (SACD) Documentation](https://docs.dimo.org/developer-platform/developer-guide/permissions-contract-sacd).
129
155
  ```python
130
156
 
131
157
  get_vehicle_jwt = dimo.token_exchange.exchange(
132
158
  developer_jwt = dev_jwt,
133
- # The Developer JWT you received using either the three step function calls, or the .get_token() shortcut
134
159
  privileges=[1, 3, 4, 5],
135
- # The privileges you've set for this vehicle, in list format (e.g. [1, 3, 4, 5])
136
160
  token_id="<token_id>"
137
- # The Vehicle NFT Token ID that you are requesting permission to
138
161
  )
139
162
  vehicle_jwt = get_vehicle_jwt['token']
140
163
  ```
@@ -6,10 +6,11 @@ from typing import Dict, Optional
6
6
 
7
7
  class Auth:
8
8
 
9
- def __init__(self, request_method, get_auth_headers, env):
9
+ def __init__(self, request_method, get_auth_headers, env, dimo_instance):
10
10
  self._request = request_method
11
11
  self._get_auth_headers = get_auth_headers
12
12
  self.env = env
13
+ self._dimo = dimo_instance
13
14
 
14
15
  def generate_challenge(
15
16
  self,
@@ -86,7 +87,7 @@ class Auth:
86
87
  )
87
88
 
88
89
  # Requires client_id, domain, and private_key. Address defaults to client_id.
89
- def get_token(
90
+ def get_dev_jwt(
90
91
  self,
91
92
  client_id: str,
92
93
  domain: str,
@@ -95,11 +96,14 @@ class Auth:
95
96
  scope="openid email",
96
97
  response_type="code",
97
98
  ) -> Dict:
99
+
98
100
  check_type("client_id", client_id, str)
99
101
  check_type("domain", domain, str)
100
102
  check_type("private_key", private_key, str)
101
103
  check_optional_type("address", address, str)
102
104
 
105
+ self._dimo._client_id = client_id
106
+
103
107
  if address is None:
104
108
  address = client_id
105
109
 
@@ -0,0 +1,85 @@
1
+ from dimo.constants import dimo_constants
2
+ from dimo.errors import check_type, check_optional_type
3
+ from dimo.permission_decoder import PermissionDecoder
4
+
5
+
6
+ class TokenExchange:
7
+
8
+ def __init__(
9
+ self, request_method, get_auth_headers, identity_instance, dimo_instance
10
+ ):
11
+ self._request = request_method
12
+ self._get_auth_headers = get_auth_headers
13
+ self._identity = identity_instance
14
+ self._dimo = dimo_instance
15
+ self._permission_decoder = PermissionDecoder()
16
+
17
+ def _decode_vehicle_permissions(self, token_id: int, client_id: str) -> dict:
18
+ response = self._identity.check_vehicle_privileges(token_id)
19
+ try:
20
+ nodes = (
21
+ response.get("data", {})
22
+ .get("vehicle", {})
23
+ .get("sacds", {})
24
+ .get("nodes", [])
25
+ )
26
+ if not nodes or not isinstance(nodes, list):
27
+ raise ValueError("Invalid response from server")
28
+ filtered_sacd = next(
29
+ (
30
+ node
31
+ for node in nodes
32
+ if node.get("grantee").lower() == client_id.lower()
33
+ ),
34
+ None,
35
+ )
36
+
37
+ if not filtered_sacd:
38
+ raise ValueError(
39
+ f"No permissions found for developer license: {client_id}. "
40
+ "Has this vehicle been shared?"
41
+ )
42
+
43
+ return self._permission_decoder.decode_permission_bits(
44
+ filtered_sacd["permissions"]
45
+ )
46
+
47
+ except Exception as e:
48
+ raise ValueError(f"Failed to decode permissions: {str(e)}")
49
+
50
+ def exchange(
51
+ self,
52
+ developer_jwt: str,
53
+ token_id: int,
54
+ client_id: str = None,
55
+ env: str = "Production",
56
+ privileges: list = None,
57
+ ) -> dict:
58
+
59
+ if client_id is None:
60
+ client_id = self._dimo._client_id
61
+ if not client_id:
62
+ raise ValueError(
63
+ "No client_id found. Please make sure you've obtained a Developer JWT before calling token exchange."
64
+ )
65
+ check_type("developer_jwt", developer_jwt, str)
66
+ check_optional_type("privileges", privileges, list)
67
+ check_type("token_id", token_id, int)
68
+ check_type("client_id", client_id, str)
69
+
70
+ if privileges is None:
71
+ privileges = self._decode_vehicle_permissions(token_id, client_id)
72
+
73
+ body = {
74
+ "nftContractAddress": dimo_constants[env]["NFT_address"],
75
+ "privileges": privileges,
76
+ "tokenId": token_id,
77
+ }
78
+ response = self._request(
79
+ "POST",
80
+ "TokenExchange",
81
+ "/v1/tokens/exchange",
82
+ headers=self._get_auth_headers(developer_jwt),
83
+ data=body,
84
+ )
85
+ return response
@@ -18,15 +18,18 @@ class DIMO:
18
18
  def __init__(self, env="Production"):
19
19
  self.env = env
20
20
  self.urls = dimo_environment[env]
21
+ self._client_id = None
21
22
  self.attestation = Attestation(self.request, self._get_auth_headers)
22
- self.auth = Auth(self.request, self._get_auth_headers, self.env)
23
+ self.auth = Auth(self.request, self._get_auth_headers, self.env, self)
23
24
  self.device_definitions = DeviceDefinitions(
24
25
  self.request, self._get_auth_headers
25
26
  )
26
- self.token_exchange = TokenExchange(self.request, self._get_auth_headers)
27
+ self.identity = Identity(self)
28
+ self.token_exchange = TokenExchange(
29
+ self.request, self._get_auth_headers, self.identity, self
30
+ )
27
31
  self.trips = Trips(self.request, self._get_auth_headers)
28
32
  self.valuations = Valuations(self.request, self._get_auth_headers)
29
- self.identity = Identity(self)
30
33
  self.telemetry = Telemetry(self)
31
34
  self._session = Request.session
32
35
 
@@ -204,3 +204,21 @@ class Identity:
204
204
  variables = {"owner": address, "first": limit}
205
205
 
206
206
  return self.dimo.query("Identity", query, variables=variables)
207
+
208
+ # # Sample query - check the privileges granted to your app by token_id
209
+ def check_vehicle_privileges(self, token_id: int) -> dict:
210
+ query = """
211
+ query CheckPrivileges($tokenId: Int!) {
212
+ vehicle(tokenId: $tokenId) {
213
+ sacds(first:100) {
214
+ nodes {
215
+ permissions
216
+ grantee
217
+ }
218
+ }
219
+ }
220
+ }
221
+ """
222
+ variables = {"tokenId": token_id}
223
+
224
+ return self.dimo.query("Identity", query, variables=variables)
@@ -5,6 +5,18 @@ class Telemetry:
5
5
  # Primary query method
6
6
  def query(self, query, vehicle_jwt):
7
7
  return self.dimo.query("Telemetry", query, token=vehicle_jwt)
8
+
9
+ def available_signals(self, vehicle_jwt: str, token_id: int) -> dict:
10
+ query = """
11
+ query getAvailableSignals($tokenId: Int!) {
12
+ availableSignals (tokenId: $tokenId)
13
+ }
14
+ """
15
+ variables = {"tokenId": token_id}
16
+
17
+ return self.dimo.query(
18
+ "Telemetry", query, token=vehicle_jwt, variables=variables
19
+ )
8
20
 
9
21
  # Sample query - get signals latest
10
22
  def get_signals_latest(self, vehicle_jwt: str, token_id: int) -> dict:
@@ -133,7 +145,10 @@ class Telemetry:
133
145
  attestation_response = self.dimo.attestation.create_vin_vc(
134
146
  vehicle_jwt=vehicle_jwt, token_id=token_id
135
147
  )
136
- if attestation_response["message"] == 'VC generated successfully. Retrieve using the provided GQL URL and query parameter.':
148
+ if (
149
+ attestation_response["message"]
150
+ == "VC generated successfully. Retrieve using the provided GQL URL and query parameter."
151
+ ):
137
152
  query = """
138
153
  query GetLatestVinVC($tokenId: Int!) {
139
154
  vinVCLatest(tokenId: $tokenId) {
@@ -144,9 +159,9 @@ class Telemetry:
144
159
  variables = {"tokenId": token_id}
145
160
 
146
161
  return self.dimo.query(
147
- "Telemetry", query, token=vehicle_jwt, variables=variables
148
- )
149
- else:
162
+ "Telemetry", query, token=vehicle_jwt, variables=variables
163
+ )
164
+ else:
150
165
  return "There was an error generating a VIN VC. Please check your credentials and try again."
151
166
 
152
167
  except Exception as error:
@@ -0,0 +1,13 @@
1
+ class PermissionDecoder:
2
+ @staticmethod
3
+ def decode_permission_bits(permission_hex: str) -> list:
4
+ clean_hex = permission_hex.lower().replace("0x", "")
5
+ permission_bits = int(clean_hex, 16)
6
+
7
+ granted_permissions = []
8
+
9
+ for i in range(128):
10
+ bit_pair = (permission_bits >> i * 2) & 0b11
11
+ if bit_pair == 0b11:
12
+ granted_permissions.append(i)
13
+ return granted_permissions
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimo-python-sdk
3
- Version: 1.1.1
3
+ Version: 1.3.0
4
4
  Summary: DIMO SDK in Python
5
5
  Author-email: Barrett Kowalsky <barrettkowalsky@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/DIMO-Network/dimo-python-sdk
@@ -144,15 +144,38 @@ For the end users of your application, they will need to share their vehicle per
144
144
 
145
145
  Typically, any endpoints that uses a NFT `tokenId` in path parameters will require JWTs. You can use this flow to obtain a privilege token.
146
146
 
147
+ There are now two methods of invoking the token exchange to obtain a Vehicle JWT: a streamlined method and a more verbose method:
148
+
149
+ ##### Streamlined Method (Recommended):
150
+ This method uses your client_id to check the privileges for a specified token_id via a query to the Identity API. This strips away the need to provide a privileges list:
151
+
152
+ ```python
153
+ # Start by obtaining a Developer JWT
154
+ auth_header = dimo.auth.get_token(
155
+ client_id = '<client_id>',
156
+ domain = '<domain>',
157
+ private_key = '<private_key>'
158
+ )
159
+
160
+ dev_jwt = auth_header["access_token"]
161
+
162
+ # Then use the simplified method for getting a Vehicle JWT
163
+
164
+ get_vehicle_jwt = dimo.token_exchange.exchange(
165
+ developer_jwt = dev_jwt
166
+ token_id ="<token_id>"
167
+ )
168
+ vehicle_jwt = get_vehicle_jwt['token']
169
+
170
+ ```
171
+ ##### Verbose Method:
172
+ This method requires you to explicity provide the list of privileges that this token_id has granted to your developer license. For more information, review the [Permissions Contract (SACD) Documentation](https://docs.dimo.org/developer-platform/developer-guide/permissions-contract-sacd).
147
173
  ```python
148
174
 
149
175
  get_vehicle_jwt = dimo.token_exchange.exchange(
150
176
  developer_jwt = dev_jwt,
151
- # The Developer JWT you received using either the three step function calls, or the .get_token() shortcut
152
177
  privileges=[1, 3, 4, 5],
153
- # The privileges you've set for this vehicle, in list format (e.g. [1, 3, 4, 5])
154
178
  token_id="<token_id>"
155
- # The Vehicle NFT Token ID that you are requesting permission to
156
179
  )
157
180
  vehicle_jwt = get_vehicle_jwt['token']
158
181
  ```
@@ -7,6 +7,7 @@ dimo/dimo.py
7
7
  dimo/environments.py
8
8
  dimo/errors.py
9
9
  dimo/eth_signer.py
10
+ dimo/permission_decoder.py
10
11
  dimo/request.py
11
12
  dimo/api/__init__.py
12
13
  dimo/api/attestation.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dimo-python-sdk"
7
- version = "1.1.1"
7
+ version = "1.3.0"
8
8
  authors = [
9
9
  { name="Barrett Kowalsky", email="barrettkowalsky@gmail.com" },
10
10
  ]
@@ -1,33 +0,0 @@
1
- from dimo.constants import dimo_constants
2
- from dimo.errors import check_type
3
-
4
-
5
- class TokenExchange:
6
-
7
- def __init__(self, request_method, get_auth_headers):
8
- self._request = request_method
9
- self._get_auth_headers = get_auth_headers
10
-
11
- def exchange(
12
- self,
13
- developer_jwt: str,
14
- privileges: list,
15
- token_id: int,
16
- env: str = "Production",
17
- ) -> dict:
18
- check_type("developer_jwt", developer_jwt, str)
19
- check_type("privileges", privileges, list)
20
- check_type("token_id", token_id, int)
21
- body = {
22
- "nftContractAddress": dimo_constants[env]["NFT_address"],
23
- "privileges": privileges,
24
- "tokenId": token_id,
25
- }
26
- response = self._request(
27
- "POST",
28
- "TokenExchange",
29
- "/v1/tokens/exchange",
30
- headers=self._get_auth_headers(developer_jwt),
31
- data=body,
32
- )
33
- return response
File without changes