mpt-extension-sdk 5.6.0__py3-none-any.whl → 5.7.0__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.
Files changed (40) hide show
  1. mpt_extension_sdk/airtable/wrap_http_error.py +7 -4
  2. mpt_extension_sdk/constants.py +2 -0
  3. mpt_extension_sdk/core/events/dataclasses.py +1 -0
  4. mpt_extension_sdk/core/events/registry.py +5 -1
  5. mpt_extension_sdk/core/extension.py +1 -0
  6. mpt_extension_sdk/core/security.py +33 -26
  7. mpt_extension_sdk/core/utils.py +2 -0
  8. mpt_extension_sdk/flows/context.py +11 -1
  9. mpt_extension_sdk/flows/pipeline.py +7 -1
  10. mpt_extension_sdk/key_vault/base.py +35 -25
  11. mpt_extension_sdk/mpt_http/base.py +7 -3
  12. mpt_extension_sdk/mpt_http/mpt.py +77 -24
  13. mpt_extension_sdk/mpt_http/utils.py +1 -0
  14. mpt_extension_sdk/mpt_http/wrap_http_error.py +13 -6
  15. mpt_extension_sdk/runtime/__init__.py +1 -0
  16. mpt_extension_sdk/runtime/commands/django.py +3 -4
  17. mpt_extension_sdk/runtime/commands/run.py +0 -2
  18. mpt_extension_sdk/runtime/djapp/apps.py +5 -1
  19. mpt_extension_sdk/runtime/djapp/conf/__init__.py +2 -3
  20. mpt_extension_sdk/runtime/djapp/conf/default.py +10 -10
  21. mpt_extension_sdk/runtime/djapp/management/commands/consume_events.py +11 -7
  22. mpt_extension_sdk/runtime/djapp/middleware.py +4 -3
  23. mpt_extension_sdk/runtime/errors.py +4 -0
  24. mpt_extension_sdk/runtime/events/dispatcher.py +19 -11
  25. mpt_extension_sdk/runtime/events/producers.py +17 -6
  26. mpt_extension_sdk/runtime/events/utils.py +4 -2
  27. mpt_extension_sdk/runtime/initializer.py +3 -2
  28. mpt_extension_sdk/runtime/logging.py +9 -2
  29. mpt_extension_sdk/runtime/master.py +24 -13
  30. mpt_extension_sdk/runtime/swoext.py +9 -7
  31. mpt_extension_sdk/runtime/tracer.py +1 -0
  32. mpt_extension_sdk/runtime/utils.py +33 -18
  33. mpt_extension_sdk/runtime/workers.py +11 -6
  34. mpt_extension_sdk/swo_rql/query_builder.py +15 -12
  35. {mpt_extension_sdk-5.6.0.dist-info → mpt_extension_sdk-5.7.0.dist-info}/METADATA +1 -1
  36. mpt_extension_sdk-5.7.0.dist-info/RECORD +54 -0
  37. mpt_extension_sdk-5.6.0.dist-info/RECORD +0 -53
  38. {mpt_extension_sdk-5.6.0.dist-info → mpt_extension_sdk-5.7.0.dist-info}/WHEEL +0 -0
  39. {mpt_extension_sdk-5.6.0.dist-info → mpt_extension_sdk-5.7.0.dist-info}/entry_points.txt +0 -0
  40. {mpt_extension_sdk-5.6.0.dist-info → mpt_extension_sdk-5.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -18,21 +18,22 @@ def _has_more_pages(page):
18
18
 
19
19
 
20
20
  def _paginated(mpt_client, url, limit=10):
21
- items = []
21
+ pages_items = []
22
22
  page = None
23
23
  offset = 0
24
24
  while _has_more_pages(page):
25
25
  response = mpt_client.get(f"{url}&limit={limit}&offset={offset}")
26
26
  response.raise_for_status()
27
27
  page = response.json()
28
- items.extend(page["data"])
28
+ pages_items.extend(page["data"])
29
29
  offset += limit
30
30
 
31
- return items
31
+ return pages_items
32
32
 
33
33
 
34
34
  @wrap_mpt_http_error
35
35
  def get_agreement(mpt_client, agreement_id):
36
+ """Retrieve an agreement by ID."""
36
37
  response = mpt_client.get(
37
38
  f"/commerce/agreements/{agreement_id}?select=seller,buyer,listing,product,subscriptions"
38
39
  )
@@ -42,6 +43,7 @@ def get_agreement(mpt_client, agreement_id):
42
43
 
43
44
  @wrap_mpt_http_error
44
45
  def get_licensee(mpt_client, licensee_id):
46
+ """Retrieve a licensee by ID."""
45
47
  response = mpt_client.get(f"/accounts/licensees/{licensee_id}")
46
48
  response.raise_for_status()
47
49
  return response.json()
@@ -49,6 +51,7 @@ def get_licensee(mpt_client, licensee_id):
49
51
 
50
52
  @wrap_mpt_http_error
51
53
  def update_order(mpt_client, order_id, **kwargs):
54
+ """Update an order."""
52
55
  response = mpt_client.put(
53
56
  f"/commerce/orders/{order_id}",
54
57
  json=kwargs,
@@ -59,6 +62,7 @@ def update_order(mpt_client, order_id, **kwargs):
59
62
 
60
63
  @wrap_mpt_http_error
61
64
  def query_order(mpt_client, order_id, **kwargs):
65
+ """Query an order."""
62
66
  response = mpt_client.post(
63
67
  f"/commerce/orders/{order_id}/query",
64
68
  json=kwargs,
@@ -69,6 +73,7 @@ def query_order(mpt_client, order_id, **kwargs):
69
73
 
70
74
  @wrap_mpt_http_error
71
75
  def fail_order(mpt_client, order_id, status_notes, **kwargs):
76
+ """Fail an order."""
72
77
  response = mpt_client.post(
73
78
  f"/commerce/orders/{order_id}/fail",
74
79
  json={
@@ -82,6 +87,7 @@ def fail_order(mpt_client, order_id, status_notes, **kwargs):
82
87
 
83
88
  @wrap_mpt_http_error
84
89
  def complete_order(mpt_client, order_id, template, **kwargs):
90
+ """Complete an order."""
85
91
  response = mpt_client.post(
86
92
  f"/commerce/orders/{order_id}/complete",
87
93
  json={"template": template, **kwargs},
@@ -92,6 +98,7 @@ def complete_order(mpt_client, order_id, template, **kwargs):
92
98
 
93
99
  @wrap_mpt_http_error
94
100
  def set_processing_template(mpt_client, order_id, template):
101
+ """Set the processing template for an order."""
95
102
  response = mpt_client.put(
96
103
  f"/commerce/orders/{order_id}",
97
104
  json={"template": template},
@@ -100,8 +107,25 @@ def set_processing_template(mpt_client, order_id, template):
100
107
  return response.json()
101
108
 
102
109
 
110
+ @wrap_mpt_http_error
111
+ def create_asset(mpt_client, order_id, asset):
112
+ """Create a new asset for an order."""
113
+ response = mpt_client.post(f"/commerce/orders/{order_id}/assets", json=asset)
114
+ response.raise_for_status()
115
+ return response.json()
116
+
117
+
118
+ @wrap_mpt_http_error
119
+ def update_asset(mpt_client, order_id, asset_id, **kwargs):
120
+ """Update an order asset."""
121
+ response = mpt_client.put(f"/commerce/orders/{order_id}/assets/{asset_id}", json=kwargs)
122
+ response.raise_for_status()
123
+ return response.json()
124
+
125
+
103
126
  @wrap_mpt_http_error
104
127
  def create_subscription(mpt_client, order_id, subscription):
128
+ """Create a new subscription for an order."""
105
129
  response = mpt_client.post(
106
130
  f"/commerce/orders/{order_id}/subscriptions",
107
131
  json=subscription,
@@ -112,6 +136,7 @@ def create_subscription(mpt_client, order_id, subscription):
112
136
 
113
137
  @wrap_mpt_http_error
114
138
  def update_subscription(mpt_client, order_id, subscription_id, **kwargs):
139
+ """Update an order subscription."""
115
140
  response = mpt_client.put(
116
141
  f"/commerce/orders/{order_id}/subscriptions/{subscription_id}",
117
142
  json=kwargs,
@@ -121,9 +146,8 @@ def update_subscription(mpt_client, order_id, subscription_id, **kwargs):
121
146
 
122
147
 
123
148
  @wrap_mpt_http_error
124
- def get_order_subscription_by_external_id(
125
- mpt_client, order_id, subscription_external_id
126
- ):
149
+ def get_order_subscription_by_external_id(mpt_client, order_id, subscription_external_id):
150
+ """Retrieve an order subscription by its external ID."""
127
151
  response = mpt_client.get(
128
152
  f"/commerce/orders/{order_id}/subscriptions?eq(externalIds.vendor,{subscription_external_id})&limit=1",
129
153
  )
@@ -131,13 +155,14 @@ def get_order_subscription_by_external_id(
131
155
  subscriptions = response.json()
132
156
  if subscriptions["$meta"]["pagination"]["total"] == 1:
133
157
  return subscriptions["data"][0]
158
+ return None
134
159
 
135
160
 
136
161
  @wrap_mpt_http_error
137
162
  def get_product_items_by_skus(mpt_client, product_id, skus):
138
- rql_query = (
139
- f"and(eq(product.id,{product_id}),in(externalIds.vendor,({','.join(skus)})))"
140
- )
163
+ """Retrieve product items by their SKUs."""
164
+ skus_str = ",".join(skus)
165
+ rql_query = f"and(eq(product.id,{product_id}),in(externalIds.vendor,({skus_str})))"
141
166
  url = f"/catalog/items?{rql_query}"
142
167
  return _paginated(mpt_client, url)
143
168
 
@@ -145,6 +170,7 @@ def get_product_items_by_skus(mpt_client, product_id, skus):
145
170
  @cache
146
171
  @wrap_mpt_http_error
147
172
  def get_webhook(mpt_client, webhook_id):
173
+ """Retrieve a webhook by ID."""
148
174
  response = mpt_client.get(f"/notifications/webhooks/{webhook_id}?select=criteria")
149
175
  response.raise_for_status()
150
176
 
@@ -153,6 +179,7 @@ def get_webhook(mpt_client, webhook_id):
153
179
 
154
180
  @wrap_mpt_http_error
155
181
  def get_product_template_or_default(mpt_client, product_id, status, name=None):
182
+ """Retrieve a product template by ID or return the default template."""
156
183
  name_or_default_filter = "eq(default,true)"
157
184
  if name:
158
185
  name_or_default_filter = f"or({name_or_default_filter},eq(name,{name}))"
@@ -164,8 +191,18 @@ def get_product_template_or_default(mpt_client, product_id, status, name=None):
164
191
  return templates["data"][0]
165
192
 
166
193
 
194
+ def get_template_by_name(mpt_client, product_id, template_name):
195
+ """Retrieve a product template by name."""
196
+ url = f"/catalog/products/{product_id}/templates?eq(name,{template_name})"
197
+ response = mpt_client.get(url)
198
+ response.raise_for_status()
199
+ templates = response.json()
200
+ return templates["data"][0]
201
+
202
+
167
203
  @wrap_mpt_http_error
168
204
  def update_agreement(mpt_client, agreement_id, **kwargs):
205
+ """Update an agreement."""
169
206
  response = mpt_client.put(
170
207
  f"/commerce/agreements/{agreement_id}",
171
208
  json=kwargs,
@@ -176,6 +213,7 @@ def update_agreement(mpt_client, agreement_id, **kwargs):
176
213
 
177
214
  @wrap_mpt_http_error
178
215
  def get_agreements_by_query(mpt_client: MPTClient, query: str, limit: int = 10):
216
+ """Retrieve agreements by query."""
179
217
  url = f"/commerce/agreements?{query}"
180
218
  return _paginated(mpt_client, url, limit=limit)
181
219
 
@@ -186,12 +224,14 @@ def get_subscriptions_by_query(
186
224
  query: str,
187
225
  limit: int = 10,
188
226
  ) -> list[dict]:
227
+ """Retrieve subscriptions by query."""
189
228
  url = f"/commerce/subscriptions?{query}"
190
229
  return _paginated(mpt_client, url, limit=limit)
191
230
 
192
231
 
193
232
  @wrap_mpt_http_error
194
233
  def update_agreement_subscription(mpt_client, subscription_id, **kwargs):
234
+ """Update an agreement subscription."""
195
235
  response = mpt_client.put(
196
236
  f"/commerce/subscriptions/{subscription_id}",
197
237
  json=kwargs,
@@ -202,6 +242,7 @@ def update_agreement_subscription(mpt_client, subscription_id, **kwargs):
202
242
 
203
243
  @wrap_mpt_http_error
204
244
  def get_agreement_subscription(mpt_client, subscription_id):
245
+ """Retrieve an agreement subscription by ID."""
205
246
  response = mpt_client.get(
206
247
  f"/commerce/subscriptions/{subscription_id}",
207
248
  )
@@ -211,6 +252,7 @@ def get_agreement_subscription(mpt_client, subscription_id):
211
252
 
212
253
  @wrap_mpt_http_error
213
254
  def get_rendered_template(mpt_client, order_id):
255
+ """Retrieve the rendered template for an order."""
214
256
  response = mpt_client.get(
215
257
  f"/commerce/orders/{order_id}/template",
216
258
  )
@@ -220,8 +262,10 @@ def get_rendered_template(mpt_client, order_id):
220
262
 
221
263
  @wrap_mpt_http_error
222
264
  def get_product_onetime_items_by_ids(mpt_client, product_id, item_ids):
265
+ """Retrieve one-time product items by their IDs."""
266
+ item_ids_str = ",".join(item_ids)
223
267
  product_cond = f"eq(product.id,{product_id})"
224
- items_cond = f"in(id,({','.join(item_ids)}))"
268
+ items_cond = f"in(id,({item_ids_str}))"
225
269
  rql_query = f"and({product_cond},{items_cond},eq(terms.period,one-time))"
226
270
  url = f"/catalog/items?{rql_query}"
227
271
 
@@ -229,8 +273,10 @@ def get_product_onetime_items_by_ids(mpt_client, product_id, item_ids):
229
273
 
230
274
 
231
275
  def get_agreements_by_ids(mpt_client, ids):
276
+ """Retrieve agreements by their IDs."""
277
+ ids_str = ",".join(ids)
232
278
  rql_query = (
233
- f"and(in(id,({','.join(ids)})),eq(status,Active))"
279
+ f"and(in(id,({ids_str})),eq(status,Active))"
234
280
  "&select=lines,parameters,subscriptions,product,listing"
235
281
  )
236
282
  return get_agreements_by_query(mpt_client, rql_query)
@@ -239,7 +285,9 @@ def get_agreements_by_ids(mpt_client, ids):
239
285
  def get_all_agreements(
240
286
  mpt_client,
241
287
  ):
242
- product_condition = f"in(product.id,({','.join(settings.MPT_PRODUCTS_IDS)}))"
288
+ """Retrieve all active agreements for specific products."""
289
+ product_ids_str = ",".join(settings.MPT_PRODUCTS_IDS)
290
+ product_condition = f"in(product.id,({product_ids_str}))"
243
291
 
244
292
  return get_agreements_by_query(
245
293
  mpt_client,
@@ -248,9 +296,8 @@ def get_all_agreements(
248
296
 
249
297
 
250
298
  @wrap_mpt_http_error
251
- def get_authorizations_by_currency_and_seller_id(
252
- mpt_client, product_id, currency, owner_id
253
- ):
299
+ def get_authorizations_by_currency_and_seller_id(mpt_client, product_id, currency, owner_id):
300
+ """Retrieve authorizations by product ID, currency, and owner ID."""
254
301
  authorization_filter = (
255
302
  f"eq(product.id,{product_id})&eq(currency,{currency})&eq(owner.id,{owner_id})"
256
303
  )
@@ -261,6 +308,7 @@ def get_authorizations_by_currency_and_seller_id(
261
308
 
262
309
  @wrap_mpt_http_error
263
310
  def get_gc_price_list_by_currency(mpt_client, product_id, currency):
311
+ """Retrieve a GC price list by product ID and currency."""
264
312
  response = mpt_client.get(
265
313
  f"/catalog/price-lists?eq(product.id,{product_id})&eq(currency,{currency})"
266
314
  )
@@ -272,6 +320,7 @@ def get_gc_price_list_by_currency(mpt_client, product_id, currency):
272
320
  def get_listings_by_price_list_and_seller_and_authorization(
273
321
  mpt_client, product_id, price_list_id, seller_id, authorization_id
274
322
  ):
323
+ """Retrieve listings by price list, seller, and authorization."""
275
324
  response = mpt_client.get(
276
325
  f"/catalog/listings?eq(product.id,{product_id})&eq(priceList.id,{price_list_id})"
277
326
  f"&eq(seller.id,{seller_id})"
@@ -283,6 +332,7 @@ def get_listings_by_price_list_and_seller_and_authorization(
283
332
 
284
333
  @wrap_mpt_http_error
285
334
  def create_listing(mpt_client, listing):
335
+ """Create a new listing."""
286
336
  response = mpt_client.post(
287
337
  "/catalog/listings",
288
338
  json=listing,
@@ -293,6 +343,7 @@ def create_listing(mpt_client, listing):
293
343
 
294
344
  @wrap_mpt_http_error
295
345
  def create_agreement(mpt_client, agreement):
346
+ """Create a new agreement."""
296
347
  response = mpt_client.post(
297
348
  "/commerce/agreements",
298
349
  json=agreement,
@@ -303,6 +354,7 @@ def create_agreement(mpt_client, agreement):
303
354
 
304
355
  @wrap_mpt_http_error
305
356
  def create_agreement_subscription(mpt_client, subscription):
357
+ """Create a new agreement subscription."""
306
358
  response = mpt_client.post(
307
359
  "/commerce/subscriptions",
308
360
  json=subscription,
@@ -313,15 +365,15 @@ def create_agreement_subscription(mpt_client, subscription):
313
365
 
314
366
  @wrap_mpt_http_error
315
367
  def get_listing_by_id(mpt_client, listing_id):
368
+ """Retrieve a listing by ID."""
316
369
  response = mpt_client.get(f"/catalog/listings/{listing_id}")
317
370
  response.raise_for_status()
318
371
  return response.json()
319
372
 
320
373
 
321
374
  @wrap_mpt_http_error
322
- def get_agreement_subscription_by_external_id(
323
- mpt_client, agreement_id, subscription_external_id
324
- ):
375
+ def get_agreement_subscription_by_external_id(mpt_client, agreement_id, subscription_external_id):
376
+ """Retrieve an agreement subscription by external ID."""
325
377
  response = mpt_client.get(
326
378
  f"/commerce/subscriptions?eq(externalIds.vendor,{subscription_external_id})"
327
379
  f"&eq(agreement.id,{agreement_id})"
@@ -336,6 +388,7 @@ def get_agreement_subscription_by_external_id(
336
388
 
337
389
  @wrap_mpt_http_error
338
390
  def get_agreements_by_external_id_values(mpt_client, external_id, display_values):
391
+ """Retrieve agreements by external ID and display values."""
339
392
  display_values_list = ",".join(display_values)
340
393
  rql_query = (
341
394
  f"any(parameters.fulfillment,and("
@@ -350,9 +403,8 @@ def get_agreements_by_external_id_values(mpt_client, external_id, display_values
350
403
 
351
404
 
352
405
  @wrap_mpt_http_error
353
- def get_agreements_by_customer_deployments(
354
- mpt_client, deployment_id_parameter, deployment_ids
355
- ):
406
+ def get_agreements_by_customer_deployments(mpt_client, deployment_id_parameter, deployment_ids):
407
+ """Retrieve agreements by customer deployments."""
356
408
  deployments_list = ",".join(deployment_ids)
357
409
  rql_query = (
358
410
  f"any(parameters.fulfillment,and("
@@ -368,6 +420,7 @@ def get_agreements_by_customer_deployments(
368
420
 
369
421
  @wrap_mpt_http_error
370
422
  def get_buyer(mpt_client, buyer_id):
423
+ """Retrieve a buyer by ID."""
371
424
  response = mpt_client.get(f"/accounts/buyers/{buyer_id}")
372
425
  response.raise_for_status()
373
426
  return response.json()
@@ -384,6 +437,8 @@ def notify(
384
437
  limit: int = 1000,
385
438
  ) -> None:
386
439
  """
440
+ Sends notification to multiple recipients for a specific buyer.
441
+
387
442
  Sends notifications to multiple recipients in batches for a specific buyer and
388
443
  category through the MPTClient service. The function retrieves recipients,
389
444
  groups them into manageable batches, and sends notifications using the provided
@@ -414,9 +469,7 @@ def notify(
414
469
 
415
470
 
416
471
  @wrap_mpt_http_error
417
- def terminate_subscription(
418
- mpt_client: MPTClient, subscription_id: str, reason: str
419
- ) -> dict:
472
+ def terminate_subscription(mpt_client: MPTClient, subscription_id: str, reason: str) -> dict:
420
473
  """
421
474
  Terminates a subscription by calling the MPT API.
422
475
 
@@ -1,2 +1,3 @@
1
1
  def find_first(func, iterable, default=None):
2
+ """Find the first item in an iterable that matches a predicate."""
2
3
  return next(filter(func, iterable), default)
@@ -5,10 +5,11 @@ from requests import HTTPError, JSONDecodeError
5
5
 
6
6
 
7
7
  class MPTError(Exception):
8
- pass
8
+ """Represents a generic MPT error."""
9
9
 
10
10
 
11
11
  class MPTHttpError(MPTError):
12
+ """Represents an HTTP error."""
12
13
  def __init__(self, status_code: int, content: str):
13
14
  self.status_code = status_code
14
15
  self.content = content
@@ -16,6 +17,7 @@ class MPTHttpError(MPTError):
16
17
 
17
18
 
18
19
  class MPTAPIError(MPTHttpError):
20
+ """Represents an API error."""
19
21
  def __init__(self, status_code, payload):
20
22
  super().__init__(status_code, json.dumps(payload))
21
23
  self.payload = payload
@@ -37,25 +39,30 @@ class MPTAPIError(MPTHttpError):
37
39
 
38
40
 
39
41
  def wrap_mpt_http_error(func):
42
+ """Wrap a function to catch MPT HTTP errors."""
40
43
  @wraps(func)
41
44
  def _wrapper(*args, **kwargs):
42
45
  try:
43
46
  return func(*args, **kwargs)
44
- except HTTPError as e:
47
+ except HTTPError as err:
48
+ response = err.response
45
49
  try:
46
- raise MPTAPIError(e.response.status_code, e.response.json())
50
+ payload = response.json()
47
51
  except JSONDecodeError:
48
- raise MPTHttpError(e.response.status_code, e.response.content.decode())
52
+ raise MPTHttpError(response.status_code, response.content.decode()) from err
53
+ raise MPTAPIError(response.status_code, payload) from err
49
54
 
50
55
  return _wrapper
51
56
 
52
57
 
53
58
  class ValidationError:
54
- def __init__(self, id, message):
55
- self.id = id
59
+ """Represents a validation error."""
60
+ def __init__(self, err_id, message):
61
+ self.id = err_id
56
62
  self.message = message
57
63
 
58
64
  def to_dict(self, **kwargs):
65
+ """Convert the validation error to a dictionary."""
59
66
  return {
60
67
  "id": self.id,
61
68
  "message": self.message.format(**kwargs),
@@ -7,4 +7,5 @@ except Exception: # pragma: no cover
7
7
 
8
8
 
9
9
  def get_version():
10
+ """Get the current version of the package."""
10
11
  return __version__
@@ -19,9 +19,8 @@ from mpt_extension_sdk.runtime.utils import initialize_extension
19
19
  @click.argument("management_args", nargs=-1, type=click.UNPROCESSED)
20
20
  @click.pass_context
21
21
  def django(ctx, management_args): # pragma: no cover
22
- "Execute Django subcommands."
23
-
24
- from django.conf import settings
22
+ """Execute Django subcommands."""
23
+ from django.conf import settings # noqa: PLC0415
25
24
 
26
25
  options = {
27
26
  "group": DEFAULT_APP_CONFIG_GROUP,
@@ -40,4 +39,4 @@ def django(ctx, management_args): # pragma: no cover
40
39
  tracer_context = nullcontext()
41
40
 
42
41
  with tracer_context:
43
- execute_from_command_line(argv=[ctx.command_path] + list(management_args))
42
+ execute_from_command_line(argv=[ctx.command_path, *management_args])
@@ -19,13 +19,11 @@ from mpt_extension_sdk.runtime.master import Master
19
19
  def run(component, color, debug, reload, debug_py):
20
20
  """Run the extension.
21
21
 
22
- \b
23
22
  COMPONENT is the the name of the component to run. Possible values:
24
23
  * all - run both API and Event Consumer threads (default)
25
24
  * api - run only API thread
26
25
  * consumer - run only Event Consumer thread
27
26
  """
28
-
29
27
  if debug_py:
30
28
  host, port = debug_py.split(":")
31
29
  debugpy.listen((host, int(port))) # noqa
@@ -8,9 +8,11 @@ ext = Extension()
8
8
 
9
9
 
10
10
  class DjAppConfig(AppConfig):
11
+ """Django AppConfig for the extension."""
11
12
  name = "mpt_extension_sdk.runtime.djapp"
12
13
 
13
14
  def ready(self):
15
+ """Check if the extension is properly configured."""
14
16
  if not hasattr(settings, "MPT_PRODUCTS_IDS") or not settings.MPT_PRODUCTS_IDS:
15
17
  raise ImproperlyConfigured(
16
18
  f"Extension {self.verbose_name} is not properly configured."
@@ -20,15 +22,17 @@ class DjAppConfig(AppConfig):
20
22
  self.extension_ready()
21
23
 
22
24
  def extension_ready(self):
23
- pass
25
+ """Perform actions when the extension is ready."""
24
26
 
25
27
 
26
28
  class ExtensionConfig(DjAppConfig):
29
+ """Configuration for the extension."""
27
30
  name = "mpt_extension_sdk"
28
31
  verbose_name = "SWO Extension SDK"
29
32
  extension = ext
30
33
 
31
34
  def extension_ready(self):
35
+ """Perform actions when the extension is ready."""
32
36
  error_msgs = []
33
37
 
34
38
  for product_id in settings.MPT_PRODUCTS_IDS:
@@ -2,11 +2,10 @@ from typing import Any
2
2
 
3
3
 
4
4
  def extract_product_ids(product_ids: str) -> list[str]:
5
+ """Extract product IDs from a comma-separated string."""
5
6
  return product_ids.split(",")
6
7
 
7
8
 
8
9
  def get_for_product(settings, variable_name: str, product_id: str) -> Any:
9
- """
10
- A shortcut to return product scoped variable from the extension settings.
11
- """
10
+ """A shortcut to return product scoped variable from the extension settings."""
12
11
  return settings.EXTENSION_CONFIG[variable_name][product_id]
@@ -14,9 +14,9 @@ import os
14
14
  from pathlib import Path
15
15
 
16
16
  from azure.monitor.opentelemetry.exporter import AzureMonitorLogExporter
17
- from opentelemetry._logs import set_logger_provider
18
- from opentelemetry.sdk._logs import LoggerProvider
19
- from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
17
+ from opentelemetry._logs import set_logger_provider # noqa: PLC2701
18
+ from opentelemetry.sdk._logs import LoggerProvider # noqa: PLC2701
19
+ from opentelemetry.sdk._logs.export import BatchLogRecordProcessor # noqa: PLC2701
20
20
 
21
21
  # Build paths inside the project like this: BASE_DIR / 'subdir'.
22
22
  BASE_DIR = Path(__file__).resolve().parent.parent
@@ -34,12 +34,12 @@ SECRET_KEY = os.getenv(
34
34
  # SECURITY WARNING: don't run with debug turned on in production!
35
35
  DEBUG = True
36
36
 
37
- ALLOWED_HOSTS = ["*"]
37
+ ALLOWED_HOSTS = ("*")
38
38
 
39
39
 
40
40
  # Application definition
41
41
 
42
- INSTALLED_APPS = [
42
+ INSTALLED_APPS = (
43
43
  "django.contrib.admin",
44
44
  "django.contrib.auth",
45
45
  "django.contrib.contenttypes",
@@ -47,9 +47,9 @@ INSTALLED_APPS = [
47
47
  "django.contrib.messages",
48
48
  "django.contrib.staticfiles",
49
49
  "mpt_extension_sdk.runtime.djapp.apps.DjAppConfig",
50
- ]
50
+ )
51
51
 
52
- MIDDLEWARE = [
52
+ MIDDLEWARE = (
53
53
  "django.middleware.security.SecurityMiddleware",
54
54
  "django.contrib.sessions.middleware.SessionMiddleware",
55
55
  "django.middleware.common.CommonMiddleware",
@@ -58,7 +58,7 @@ MIDDLEWARE = [
58
58
  "django.contrib.messages.middleware.MessageMiddleware",
59
59
  "django.middleware.clickjacking.XFrameOptionsMiddleware",
60
60
  "mpt_extension_sdk.runtime.djapp.middleware.MPTClientMiddleware",
61
- ]
61
+ )
62
62
 
63
63
  ROOT_URLCONF = "mpt_extension_sdk.runtime.djapp.conf.urls"
64
64
 
@@ -130,7 +130,7 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
130
130
  APPLICATIONINSIGHTS_CONNECTION_STRING = os.getenv(
131
131
  "APPLICATIONINSIGHTS_CONNECTION_STRING", ""
132
132
  )
133
- USE_APPLICATIONINSIGHTS = APPLICATIONINSIGHTS_CONNECTION_STRING != ""
133
+ USE_APPLICATIONINSIGHTS = bool(APPLICATIONINSIGHTS_CONNECTION_STRING)
134
134
 
135
135
 
136
136
  if USE_APPLICATIONINSIGHTS: # pragma: no cover
@@ -166,7 +166,7 @@ LOGGING = {
166
166
  "rich": {
167
167
  "class": "mpt_extension_sdk.runtime.logging.RichHandler",
168
168
  "formatter": "rich",
169
- "log_time_format": lambda x: x.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3],
169
+ "log_time_format": lambda log_time: log_time.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3],
170
170
  "rich_tracebacks": True,
171
171
  },
172
172
  "opentelemetry": {
@@ -1,5 +1,6 @@
1
1
  import signal # pragma: no cover
2
2
  from threading import Event # pragma: no cover
3
+ from typing import ClassVar # pragma: no cover
3
4
 
4
5
  from django.core.management.base import BaseCommand # pragma: no cover
5
6
 
@@ -11,22 +12,21 @@ from mpt_extension_sdk.runtime.events.producers import (
11
12
 
12
13
 
13
14
  class Command(BaseCommand): # pragma: no cover
15
+ """Command to consume events."""
14
16
  help = CONSUME_EVENTS_HELP_TEXT
15
- producer_classes = [
17
+ producer_classes: ClassVar[list] = [
16
18
  OrderEventProducer,
17
19
  ]
18
- producers = []
20
+ producers: ClassVar[list] = []
19
21
 
20
22
  def handle(self, *args, **options):
23
+ """Handle the command."""
21
24
  self.shutdown_event = Event()
22
25
  self.dispatcher = Dispatcher()
23
26
  self.dispatcher.start()
24
27
 
25
- def shutdown(signum, frame):
26
- self.shutdown_event.set()
27
-
28
- signal.signal(signal.SIGTERM, shutdown)
29
- signal.signal(signal.SIGINT, shutdown)
28
+ signal.signal(signal.SIGTERM, self.shutdown)
29
+ signal.signal(signal.SIGINT, self.shutdown)
30
30
  for producer_cls in self.producer_classes:
31
31
  producer = producer_cls(self.dispatcher)
32
32
  self.producers.append(producer)
@@ -36,3 +36,7 @@ class Command(BaseCommand): # pragma: no cover
36
36
  for producer in self.producers:
37
37
  producer.stop()
38
38
  self.dispatcher.stop()
39
+
40
+ def shutdown(self, signum, frame):
41
+ """Shutdown the command."""
42
+ self.shutdown_event.set()
@@ -6,16 +6,17 @@ _CLIENT = None
6
6
 
7
7
 
8
8
  class MPTClientMiddleware: # pragma: no cover
9
+ """Middleware to set up MPTClient for each request."""
9
10
  def __init__(self, get_response):
10
11
  self.get_response = get_response
11
12
 
12
13
  def __call__(self, request):
13
- global _CLIENT
14
+ """Set up MPTClient for the request."""
15
+ global _CLIENT # noqa: PLW0603
14
16
  if not _CLIENT:
15
17
  _CLIENT = MPTClient(
16
18
  f"{settings.MPT_API_BASE_URL}/v1/",
17
19
  settings.MPT_API_TOKEN,
18
20
  )
19
21
  request.client = _CLIENT
20
- response = self.get_response(request)
21
- return response
22
+ return self.get_response(request)
@@ -0,0 +1,4 @@
1
+ # Custom exception for variable format errors
2
+
3
+ class VariableNotWellFormedError(Exception):
4
+ """Raised when a variable is not well-formed."""