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.
- mpt_extension_sdk/airtable/wrap_http_error.py +7 -4
- mpt_extension_sdk/constants.py +2 -0
- mpt_extension_sdk/core/events/dataclasses.py +1 -0
- mpt_extension_sdk/core/events/registry.py +5 -1
- mpt_extension_sdk/core/extension.py +1 -0
- mpt_extension_sdk/core/security.py +33 -26
- mpt_extension_sdk/core/utils.py +2 -0
- mpt_extension_sdk/flows/context.py +11 -1
- mpt_extension_sdk/flows/pipeline.py +7 -1
- mpt_extension_sdk/key_vault/base.py +35 -25
- mpt_extension_sdk/mpt_http/base.py +7 -3
- mpt_extension_sdk/mpt_http/mpt.py +77 -24
- mpt_extension_sdk/mpt_http/utils.py +1 -0
- mpt_extension_sdk/mpt_http/wrap_http_error.py +13 -6
- mpt_extension_sdk/runtime/__init__.py +1 -0
- mpt_extension_sdk/runtime/commands/django.py +3 -4
- mpt_extension_sdk/runtime/commands/run.py +0 -2
- mpt_extension_sdk/runtime/djapp/apps.py +5 -1
- mpt_extension_sdk/runtime/djapp/conf/__init__.py +2 -3
- mpt_extension_sdk/runtime/djapp/conf/default.py +10 -10
- mpt_extension_sdk/runtime/djapp/management/commands/consume_events.py +11 -7
- mpt_extension_sdk/runtime/djapp/middleware.py +4 -3
- mpt_extension_sdk/runtime/errors.py +4 -0
- mpt_extension_sdk/runtime/events/dispatcher.py +19 -11
- mpt_extension_sdk/runtime/events/producers.py +17 -6
- mpt_extension_sdk/runtime/events/utils.py +4 -2
- mpt_extension_sdk/runtime/initializer.py +3 -2
- mpt_extension_sdk/runtime/logging.py +9 -2
- mpt_extension_sdk/runtime/master.py +24 -13
- mpt_extension_sdk/runtime/swoext.py +9 -7
- mpt_extension_sdk/runtime/tracer.py +1 -0
- mpt_extension_sdk/runtime/utils.py +33 -18
- mpt_extension_sdk/runtime/workers.py +11 -6
- mpt_extension_sdk/swo_rql/query_builder.py +15 -12
- {mpt_extension_sdk-5.6.0.dist-info → mpt_extension_sdk-5.7.0.dist-info}/METADATA +1 -1
- mpt_extension_sdk-5.7.0.dist-info/RECORD +54 -0
- mpt_extension_sdk-5.6.0.dist-info/RECORD +0 -53
- {mpt_extension_sdk-5.6.0.dist-info → mpt_extension_sdk-5.7.0.dist-info}/WHEEL +0 -0
- {mpt_extension_sdk-5.6.0.dist-info → mpt_extension_sdk-5.7.0.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
|
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
|
-
|
|
28
|
+
pages_items.extend(page["data"])
|
|
29
29
|
offset += limit
|
|
30
30
|
|
|
31
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
139
|
-
|
|
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,({
|
|
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,({
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -5,10 +5,11 @@ from requests import HTTPError, JSONDecodeError
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class MPTError(Exception):
|
|
8
|
-
|
|
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
|
|
47
|
+
except HTTPError as err:
|
|
48
|
+
response = err.response
|
|
45
49
|
try:
|
|
46
|
-
|
|
50
|
+
payload = response.json()
|
|
47
51
|
except JSONDecodeError:
|
|
48
|
-
raise MPTHttpError(
|
|
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
|
-
|
|
55
|
-
|
|
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),
|
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
21
|
-
return response
|
|
22
|
+
return self.get_response(request)
|