keymint 2.2.0__tar.gz → 2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: keymint
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Official Python SDK for KeyMint license management with comprehensive API coverage.
5
5
  Home-page: https://github.com/keymint-dev/keymint-python
6
6
  Author: KeyMint
@@ -131,6 +131,16 @@ Keymint provides utilities to uniquely identify machines for node-locking:
131
131
  |-------------------------|--------------------------------------------------|
132
132
  | `verify_webhook_signature`| Verifies the signature of a webhook request payload. |
133
133
 
134
+ ## Idempotency
135
+
136
+ All mutating SDK methods support idempotency keys to safely retry requests in case of network drops. Pass a unique string (recommended: UUID v4) as the optional `idempotency_key` keyword argument:
137
+
138
+ ```python
139
+ result = sdk.create_key({
140
+ 'productId': product_id,
141
+ }, idempotency_key='9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d')
142
+ ```
143
+
134
144
  ## License
135
145
  MIT
136
146
 
@@ -82,6 +82,16 @@ Keymint provides utilities to uniquely identify machines for node-locking:
82
82
  |-------------------------|--------------------------------------------------|
83
83
  | `verify_webhook_signature`| Verifies the signature of a webhook request payload. |
84
84
 
85
+ ## Idempotency
86
+
87
+ All mutating SDK methods support idempotency keys to safely retry requests in case of network drops. Pass a unique string (recommended: UUID v4) as the optional `idempotency_key` keyword argument:
88
+
89
+ ```python
90
+ result = sdk.create_key({
91
+ 'productId': product_id,
92
+ }, idempotency_key='9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d')
93
+ ```
94
+
85
95
  ## License
86
96
  MIT
87
97
 
@@ -16,17 +16,21 @@ class KeyMint:
16
16
  'Content-Type': 'application/json'
17
17
  }
18
18
 
19
- def _handle_request(self, method: str, endpoint: str, params: dict = None, query_params: dict = None):
19
+ def _handle_request(self, method: str, endpoint: str, params: dict = None, query_params: dict = None, idempotency_key: str = None):
20
20
  url = f'{self.base_url}{endpoint}'
21
+ headers = self.headers.copy()
22
+ if idempotency_key:
23
+ headers['Idempotency-Key'] = idempotency_key
24
+
21
25
  try:
22
26
  if method.upper() == 'GET':
23
- response = requests.get(url, params=query_params, headers=self.headers)
27
+ response = requests.get(url, params=query_params, headers=headers)
24
28
  elif method.upper() == 'POST':
25
- response = requests.post(url, json=params, headers=self.headers)
29
+ response = requests.post(url, json=params, headers=headers)
26
30
  elif method.upper() == 'PUT':
27
- response = requests.put(url, json=params, headers=self.headers)
31
+ response = requests.put(url, json=params, headers=headers)
28
32
  elif method.upper() == 'DELETE':
29
- response = requests.delete(url, params=query_params, headers=self.headers)
33
+ response = requests.delete(url, params=query_params, headers=headers)
30
34
  else:
31
35
  raise ValueError(f"Unsupported HTTP method: {method}")
32
36
 
@@ -49,15 +53,16 @@ class KeyMint:
49
53
  except Exception as err:
50
54
  raise KeyMintApiError(message=str(err), code=-1)
51
55
 
52
- def create_key(self, params: CreateKeyParams) -> CreateKeyResponse:
56
+ def create_key(self, params: CreateKeyParams, idempotency_key: str = None) -> CreateKeyResponse:
53
57
  """
54
58
  Creates a new license key.
55
59
  :param params: Parameters for creating the key.
60
+ :param idempotency_key: Optional unique identifier to ensure request idempotency.
56
61
  :returns: The created key information.
57
62
  """
58
- return self._handle_request('POST', '/key', params)
63
+ return self._handle_request('POST', '/key', params, idempotency_key=idempotency_key)
59
64
 
60
- def activate_key(self, params: ActivateKeyParams) -> ActivateKeyResponse:
65
+ def activate_key(self, params: ActivateKeyParams, idempotency_key: str = None) -> ActivateKeyResponse:
61
66
  """
62
67
  Activates a license key for a specific device.
63
68
 
@@ -67,41 +72,46 @@ class KeyMint:
67
72
  MUST cache the validation result locally.
68
73
 
69
74
  :param params: Activation parameters including productId, licenseKey, and optional hostId.
75
+ :param idempotency_key: Optional unique identifier to ensure request idempotency.
70
76
  :return: Activation success message and licensee information.
71
77
  """
72
- return self._handle_request('POST', '/key/activate', params)
78
+ return self._handle_request('POST', '/key/activate', params, idempotency_key=idempotency_key)
73
79
 
74
- def deactivate_key(self, params: DeactivateKeyParams) -> DeactivateKeyResponse:
80
+ def deactivate_key(self, params: DeactivateKeyParams, idempotency_key: str = None) -> DeactivateKeyResponse:
75
81
  """
76
82
  Deactivates a device from a license key.
77
83
  :param params: Parameters for deactivating the key.
84
+ :param idempotency_key: Optional unique identifier to ensure request idempotency.
78
85
  :returns: The deactivation confirmation.
79
86
  """
80
- return self._handle_request('POST', '/key/deactivate', params)
87
+ return self._handle_request('POST', '/key/deactivate', params, idempotency_key=idempotency_key)
81
88
 
82
- def floating_checkout(self, params: FloatingCheckoutParams) -> FloatingCheckoutResponse:
89
+ def floating_checkout(self, params: FloatingCheckoutParams, idempotency_key: str = None) -> FloatingCheckoutResponse:
83
90
  """
84
91
  Checks out a floating license seat.
85
92
  :param params: Parameters for checking out the license.
93
+ :param idempotency_key: Optional unique identifier to ensure request idempotency.
86
94
  :returns: The checkout response containing sessionId and sessionSecret.
87
95
  """
88
- return self._handle_request('POST', '/key/checkout', params)
96
+ return self._handle_request('POST', '/key/checkout', params, idempotency_key=idempotency_key)
89
97
 
90
- def floating_heartbeat(self, params: FloatingHeartbeatParams) -> FloatingHeartbeatResponse:
98
+ def floating_heartbeat(self, params: FloatingHeartbeatParams, idempotency_key: str = None) -> FloatingHeartbeatResponse:
91
99
  """
92
100
  Sends a heartbeat to keep a floating license session alive.
93
101
  :param params: Parameters for the heartbeat (includes rotating signature).
102
+ :param idempotency_key: Optional unique identifier to ensure request idempotency.
94
103
  :returns: The heartbeat response with extended expiry and new nonce.
95
104
  """
96
- return self._handle_request('POST', '/key/heartbeat', params)
105
+ return self._handle_request('POST', '/key/heartbeat', params, idempotency_key=idempotency_key)
97
106
 
98
- def floating_checkin(self, params: FloatingCheckinParams) -> FloatingCheckinResponse:
107
+ def floating_checkin(self, params: FloatingCheckinParams, idempotency_key: str = None) -> FloatingCheckinResponse:
99
108
  """
100
109
  Checks in a floating license session, releasing the seat.
101
110
  :param params: Parameters for checking in the license (includes rotating signature).
111
+ :param idempotency_key: Optional unique identifier to ensure request idempotency.
102
112
  :returns: The checkin confirmation.
103
113
  """
104
- return self._handle_request('POST', '/key/checkin', params)
114
+ return self._handle_request('POST', '/key/checkin', params, idempotency_key=idempotency_key)
105
115
 
106
116
  def get_key(self, params: GetKeyParams) -> GetKeyResponse:
107
117
  """
@@ -115,31 +125,34 @@ class KeyMint:
115
125
  }
116
126
  return self._handle_request('GET', '/key', query_params=query_params)
117
127
 
118
- def block_key(self, params: BlockKeyParams) -> BlockKeyResponse:
128
+ def block_key(self, params: BlockKeyParams, idempotency_key: str = None) -> BlockKeyResponse:
119
129
  """
120
130
  Blocks a specific license key.
121
131
  :param params: Parameters for blocking the key.
132
+ :param idempotency_key: Optional unique identifier to ensure request idempotency.
122
133
  :returns: The block confirmation.
123
134
  """
124
- return self._handle_request('POST', '/key/block', params)
135
+ return self._handle_request('POST', '/key/block', params, idempotency_key=idempotency_key)
125
136
 
126
- def unblock_key(self, params: UnblockKeyParams) -> UnblockKeyResponse:
137
+ def unblock_key(self, params: UnblockKeyParams, idempotency_key: str = None) -> UnblockKeyResponse:
127
138
  """
128
139
  Unblocks a previously blocked license key.
129
140
  :param params: Parameters for unblocking the key.
141
+ :param idempotency_key: Optional unique identifier to ensure request idempotency.
130
142
  :returns: The unblock confirmation.
131
143
  """
132
- return self._handle_request('POST', '/key/unblock', params)
144
+ return self._handle_request('POST', '/key/unblock', params, idempotency_key=idempotency_key)
133
145
 
134
146
  # Customer Management Methods
135
147
 
136
- def create_customer(self, params: CreateCustomerParams) -> CreateCustomerResponse:
148
+ def create_customer(self, params: CreateCustomerParams, idempotency_key: str = None) -> CreateCustomerResponse:
137
149
  """
138
150
  Creates a new customer.
139
151
  :param params: Parameters for creating the customer.
152
+ :param idempotency_key: Optional unique identifier to ensure request idempotency.
140
153
  :returns: The created customer information.
141
154
  """
142
- return self._handle_request('POST', '/customer', params)
155
+ return self._handle_request('POST', '/customer', params, idempotency_key=idempotency_key)
143
156
 
144
157
  def get_all_customers(self, params: Optional[GetAllCustomersParams] = None) -> GetAllCustomersResponse:
145
158
  """
@@ -158,22 +171,24 @@ class KeyMint:
158
171
  query_params = {'customerId': params['customerId']}
159
172
  return self._handle_request('GET', '/customer/by-id', query_params=query_params)
160
173
 
161
- def update_customer(self, params: UpdateCustomerParams) -> UpdateCustomerResponse:
174
+ def update_customer(self, params: UpdateCustomerParams, idempotency_key: str = None) -> UpdateCustomerResponse:
162
175
  """
163
176
  Updates an existing customer's information.
164
177
  :param params: Parameters for updating the customer.
178
+ :param idempotency_key: Optional unique identifier to ensure request idempotency.
165
179
  :returns: The update confirmation.
166
180
  """
167
- return self._handle_request('PUT', '/customer/by-id', params)
181
+ return self._handle_request('PUT', '/customer/by-id', params, idempotency_key=idempotency_key)
168
182
 
169
- def delete_customer(self, params: DeleteCustomerParams) -> DeleteCustomerResponse:
183
+ def delete_customer(self, params: DeleteCustomerParams, idempotency_key: str = None) -> DeleteCustomerResponse:
170
184
  """
171
185
  Permanently deletes a customer and all associated license keys.
172
186
  :param params: Parameters containing the customer ID.
187
+ :param idempotency_key: Optional unique identifier to ensure request idempotency.
173
188
  :returns: The deletion confirmation.
174
189
  """
175
190
  query_params = {'customerId': params['customerId']}
176
- return self._handle_request('DELETE', '/customer/by-id', query_params=query_params)
191
+ return self._handle_request('DELETE', '/customer/by-id', query_params=query_params, idempotency_key=idempotency_key)
177
192
 
178
193
  def get_customer_with_keys(self, params: GetCustomerWithKeysParams) -> GetCustomerWithKeysResponse:
179
194
  """
@@ -184,14 +199,15 @@ class KeyMint:
184
199
  query_params = {'customerId': params['customerId']}
185
200
  return self._handle_request('GET', '/customer/keys', query_params=query_params)
186
201
 
187
- def toggle_customer_status(self, params: ToggleCustomerStatusParams) -> ToggleCustomerStatusResponse:
202
+ def toggle_customer_status(self, params: ToggleCustomerStatusParams, idempotency_key: str = None) -> ToggleCustomerStatusResponse:
188
203
  """
189
204
  Toggles the active status of a customer account (disable or enable).
190
205
  :param params: Parameters containing the customer ID.
206
+ :param idempotency_key: Optional unique identifier to ensure request idempotency.
191
207
  :returns: The status toggle confirmation.
192
208
  """
193
209
  query_params = {'customerId': params['customerId']}
194
- return self._handle_request('POST', '/customer/disable', params=None, query_params=query_params)
210
+ return self._handle_request('POST', '/customer/disable', params=None, query_params=query_params, idempotency_key=idempotency_key)
195
211
 
196
212
  @staticmethod
197
213
  def verify_webhook_signature(payload: str, header: str, secret: str, tolerance_seconds: int = 300) -> bool:
@@ -1,6 +1,6 @@
1
1
  """KeyMint Python SDK version information."""
2
2
 
3
- __version__ = "2.2.0"
3
+ __version__ = "2.3.0"
4
4
  __author__ = "KeyMint"
5
5
  __email__ = "cliff@keymint.dev"
6
6
  __url__ = "https://github.com/keymint-dev/keymint-python"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: keymint
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Official Python SDK for KeyMint license management with comprehensive API coverage.
5
5
  Home-page: https://github.com/keymint-dev/keymint-python
6
6
  Author: KeyMint
@@ -131,6 +131,16 @@ Keymint provides utilities to uniquely identify machines for node-locking:
131
131
  |-------------------------|--------------------------------------------------|
132
132
  | `verify_webhook_signature`| Verifies the signature of a webhook request payload. |
133
133
 
134
+ ## Idempotency
135
+
136
+ All mutating SDK methods support idempotency keys to safely retry requests in case of network drops. Pass a unique string (recommended: UUID v4) as the optional `idempotency_key` keyword argument:
137
+
138
+ ```python
139
+ result = sdk.create_key({
140
+ 'productId': product_id,
141
+ }, idempotency_key='9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d')
142
+ ```
143
+
134
144
  ## License
135
145
  MIT
136
146
 
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setup(
7
7
  name="keymint",
8
- version="2.2.0",
8
+ version="2.3.0",
9
9
  author="KeyMint",
10
10
  author_email="cliff@keymint.dev",
11
11
  description="Official Python SDK for KeyMint license management with comprehensive API coverage.",
File without changes
File without changes
File without changes
File without changes
File without changes