mpt-extension-sdk 5.6.1__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 +69 -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.1.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.1.dist-info/RECORD +0 -53
- {mpt_extension_sdk-5.6.1.dist-info → mpt_extension_sdk-5.7.0.dist-info}/WHEEL +0 -0
- {mpt_extension_sdk-5.6.1.dist-info → mpt_extension_sdk-5.7.0.dist-info}/entry_points.txt +0 -0
- {mpt_extension_sdk-5.6.1.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}))"
|
|
@@ -165,6 +192,7 @@ def get_product_template_or_default(mpt_client, product_id, status, name=None):
|
|
|
165
192
|
|
|
166
193
|
|
|
167
194
|
def get_template_by_name(mpt_client, product_id, template_name):
|
|
195
|
+
"""Retrieve a product template by name."""
|
|
168
196
|
url = f"/catalog/products/{product_id}/templates?eq(name,{template_name})"
|
|
169
197
|
response = mpt_client.get(url)
|
|
170
198
|
response.raise_for_status()
|
|
@@ -174,6 +202,7 @@ def get_template_by_name(mpt_client, product_id, template_name):
|
|
|
174
202
|
|
|
175
203
|
@wrap_mpt_http_error
|
|
176
204
|
def update_agreement(mpt_client, agreement_id, **kwargs):
|
|
205
|
+
"""Update an agreement."""
|
|
177
206
|
response = mpt_client.put(
|
|
178
207
|
f"/commerce/agreements/{agreement_id}",
|
|
179
208
|
json=kwargs,
|
|
@@ -184,6 +213,7 @@ def update_agreement(mpt_client, agreement_id, **kwargs):
|
|
|
184
213
|
|
|
185
214
|
@wrap_mpt_http_error
|
|
186
215
|
def get_agreements_by_query(mpt_client: MPTClient, query: str, limit: int = 10):
|
|
216
|
+
"""Retrieve agreements by query."""
|
|
187
217
|
url = f"/commerce/agreements?{query}"
|
|
188
218
|
return _paginated(mpt_client, url, limit=limit)
|
|
189
219
|
|
|
@@ -194,12 +224,14 @@ def get_subscriptions_by_query(
|
|
|
194
224
|
query: str,
|
|
195
225
|
limit: int = 10,
|
|
196
226
|
) -> list[dict]:
|
|
227
|
+
"""Retrieve subscriptions by query."""
|
|
197
228
|
url = f"/commerce/subscriptions?{query}"
|
|
198
229
|
return _paginated(mpt_client, url, limit=limit)
|
|
199
230
|
|
|
200
231
|
|
|
201
232
|
@wrap_mpt_http_error
|
|
202
233
|
def update_agreement_subscription(mpt_client, subscription_id, **kwargs):
|
|
234
|
+
"""Update an agreement subscription."""
|
|
203
235
|
response = mpt_client.put(
|
|
204
236
|
f"/commerce/subscriptions/{subscription_id}",
|
|
205
237
|
json=kwargs,
|
|
@@ -210,6 +242,7 @@ def update_agreement_subscription(mpt_client, subscription_id, **kwargs):
|
|
|
210
242
|
|
|
211
243
|
@wrap_mpt_http_error
|
|
212
244
|
def get_agreement_subscription(mpt_client, subscription_id):
|
|
245
|
+
"""Retrieve an agreement subscription by ID."""
|
|
213
246
|
response = mpt_client.get(
|
|
214
247
|
f"/commerce/subscriptions/{subscription_id}",
|
|
215
248
|
)
|
|
@@ -219,6 +252,7 @@ def get_agreement_subscription(mpt_client, subscription_id):
|
|
|
219
252
|
|
|
220
253
|
@wrap_mpt_http_error
|
|
221
254
|
def get_rendered_template(mpt_client, order_id):
|
|
255
|
+
"""Retrieve the rendered template for an order."""
|
|
222
256
|
response = mpt_client.get(
|
|
223
257
|
f"/commerce/orders/{order_id}/template",
|
|
224
258
|
)
|
|
@@ -228,8 +262,10 @@ def get_rendered_template(mpt_client, order_id):
|
|
|
228
262
|
|
|
229
263
|
@wrap_mpt_http_error
|
|
230
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)
|
|
231
267
|
product_cond = f"eq(product.id,{product_id})"
|
|
232
|
-
items_cond = f"in(id,({
|
|
268
|
+
items_cond = f"in(id,({item_ids_str}))"
|
|
233
269
|
rql_query = f"and({product_cond},{items_cond},eq(terms.period,one-time))"
|
|
234
270
|
url = f"/catalog/items?{rql_query}"
|
|
235
271
|
|
|
@@ -237,8 +273,10 @@ def get_product_onetime_items_by_ids(mpt_client, product_id, item_ids):
|
|
|
237
273
|
|
|
238
274
|
|
|
239
275
|
def get_agreements_by_ids(mpt_client, ids):
|
|
276
|
+
"""Retrieve agreements by their IDs."""
|
|
277
|
+
ids_str = ",".join(ids)
|
|
240
278
|
rql_query = (
|
|
241
|
-
f"and(in(id,({
|
|
279
|
+
f"and(in(id,({ids_str})),eq(status,Active))"
|
|
242
280
|
"&select=lines,parameters,subscriptions,product,listing"
|
|
243
281
|
)
|
|
244
282
|
return get_agreements_by_query(mpt_client, rql_query)
|
|
@@ -247,7 +285,9 @@ def get_agreements_by_ids(mpt_client, ids):
|
|
|
247
285
|
def get_all_agreements(
|
|
248
286
|
mpt_client,
|
|
249
287
|
):
|
|
250
|
-
|
|
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}))"
|
|
251
291
|
|
|
252
292
|
return get_agreements_by_query(
|
|
253
293
|
mpt_client,
|
|
@@ -256,9 +296,8 @@ def get_all_agreements(
|
|
|
256
296
|
|
|
257
297
|
|
|
258
298
|
@wrap_mpt_http_error
|
|
259
|
-
def get_authorizations_by_currency_and_seller_id(
|
|
260
|
-
|
|
261
|
-
):
|
|
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."""
|
|
262
301
|
authorization_filter = (
|
|
263
302
|
f"eq(product.id,{product_id})&eq(currency,{currency})&eq(owner.id,{owner_id})"
|
|
264
303
|
)
|
|
@@ -269,6 +308,7 @@ def get_authorizations_by_currency_and_seller_id(
|
|
|
269
308
|
|
|
270
309
|
@wrap_mpt_http_error
|
|
271
310
|
def get_gc_price_list_by_currency(mpt_client, product_id, currency):
|
|
311
|
+
"""Retrieve a GC price list by product ID and currency."""
|
|
272
312
|
response = mpt_client.get(
|
|
273
313
|
f"/catalog/price-lists?eq(product.id,{product_id})&eq(currency,{currency})"
|
|
274
314
|
)
|
|
@@ -280,6 +320,7 @@ def get_gc_price_list_by_currency(mpt_client, product_id, currency):
|
|
|
280
320
|
def get_listings_by_price_list_and_seller_and_authorization(
|
|
281
321
|
mpt_client, product_id, price_list_id, seller_id, authorization_id
|
|
282
322
|
):
|
|
323
|
+
"""Retrieve listings by price list, seller, and authorization."""
|
|
283
324
|
response = mpt_client.get(
|
|
284
325
|
f"/catalog/listings?eq(product.id,{product_id})&eq(priceList.id,{price_list_id})"
|
|
285
326
|
f"&eq(seller.id,{seller_id})"
|
|
@@ -291,6 +332,7 @@ def get_listings_by_price_list_and_seller_and_authorization(
|
|
|
291
332
|
|
|
292
333
|
@wrap_mpt_http_error
|
|
293
334
|
def create_listing(mpt_client, listing):
|
|
335
|
+
"""Create a new listing."""
|
|
294
336
|
response = mpt_client.post(
|
|
295
337
|
"/catalog/listings",
|
|
296
338
|
json=listing,
|
|
@@ -301,6 +343,7 @@ def create_listing(mpt_client, listing):
|
|
|
301
343
|
|
|
302
344
|
@wrap_mpt_http_error
|
|
303
345
|
def create_agreement(mpt_client, agreement):
|
|
346
|
+
"""Create a new agreement."""
|
|
304
347
|
response = mpt_client.post(
|
|
305
348
|
"/commerce/agreements",
|
|
306
349
|
json=agreement,
|
|
@@ -311,6 +354,7 @@ def create_agreement(mpt_client, agreement):
|
|
|
311
354
|
|
|
312
355
|
@wrap_mpt_http_error
|
|
313
356
|
def create_agreement_subscription(mpt_client, subscription):
|
|
357
|
+
"""Create a new agreement subscription."""
|
|
314
358
|
response = mpt_client.post(
|
|
315
359
|
"/commerce/subscriptions",
|
|
316
360
|
json=subscription,
|
|
@@ -321,15 +365,15 @@ def create_agreement_subscription(mpt_client, subscription):
|
|
|
321
365
|
|
|
322
366
|
@wrap_mpt_http_error
|
|
323
367
|
def get_listing_by_id(mpt_client, listing_id):
|
|
368
|
+
"""Retrieve a listing by ID."""
|
|
324
369
|
response = mpt_client.get(f"/catalog/listings/{listing_id}")
|
|
325
370
|
response.raise_for_status()
|
|
326
371
|
return response.json()
|
|
327
372
|
|
|
328
373
|
|
|
329
374
|
@wrap_mpt_http_error
|
|
330
|
-
def get_agreement_subscription_by_external_id(
|
|
331
|
-
|
|
332
|
-
):
|
|
375
|
+
def get_agreement_subscription_by_external_id(mpt_client, agreement_id, subscription_external_id):
|
|
376
|
+
"""Retrieve an agreement subscription by external ID."""
|
|
333
377
|
response = mpt_client.get(
|
|
334
378
|
f"/commerce/subscriptions?eq(externalIds.vendor,{subscription_external_id})"
|
|
335
379
|
f"&eq(agreement.id,{agreement_id})"
|
|
@@ -344,6 +388,7 @@ def get_agreement_subscription_by_external_id(
|
|
|
344
388
|
|
|
345
389
|
@wrap_mpt_http_error
|
|
346
390
|
def get_agreements_by_external_id_values(mpt_client, external_id, display_values):
|
|
391
|
+
"""Retrieve agreements by external ID and display values."""
|
|
347
392
|
display_values_list = ",".join(display_values)
|
|
348
393
|
rql_query = (
|
|
349
394
|
f"any(parameters.fulfillment,and("
|
|
@@ -358,9 +403,8 @@ def get_agreements_by_external_id_values(mpt_client, external_id, display_values
|
|
|
358
403
|
|
|
359
404
|
|
|
360
405
|
@wrap_mpt_http_error
|
|
361
|
-
def get_agreements_by_customer_deployments(
|
|
362
|
-
|
|
363
|
-
):
|
|
406
|
+
def get_agreements_by_customer_deployments(mpt_client, deployment_id_parameter, deployment_ids):
|
|
407
|
+
"""Retrieve agreements by customer deployments."""
|
|
364
408
|
deployments_list = ",".join(deployment_ids)
|
|
365
409
|
rql_query = (
|
|
366
410
|
f"any(parameters.fulfillment,and("
|
|
@@ -376,6 +420,7 @@ def get_agreements_by_customer_deployments(
|
|
|
376
420
|
|
|
377
421
|
@wrap_mpt_http_error
|
|
378
422
|
def get_buyer(mpt_client, buyer_id):
|
|
423
|
+
"""Retrieve a buyer by ID."""
|
|
379
424
|
response = mpt_client.get(f"/accounts/buyers/{buyer_id}")
|
|
380
425
|
response.raise_for_status()
|
|
381
426
|
return response.json()
|
|
@@ -392,6 +437,8 @@ def notify(
|
|
|
392
437
|
limit: int = 1000,
|
|
393
438
|
) -> None:
|
|
394
439
|
"""
|
|
440
|
+
Sends notification to multiple recipients for a specific buyer.
|
|
441
|
+
|
|
395
442
|
Sends notifications to multiple recipients in batches for a specific buyer and
|
|
396
443
|
category through the MPTClient service. The function retrieves recipients,
|
|
397
444
|
groups them into manageable batches, and sends notifications using the provided
|
|
@@ -422,9 +469,7 @@ def notify(
|
|
|
422
469
|
|
|
423
470
|
|
|
424
471
|
@wrap_mpt_http_error
|
|
425
|
-
def terminate_subscription(
|
|
426
|
-
mpt_client: MPTClient, subscription_id: str, reason: str
|
|
427
|
-
) -> dict:
|
|
472
|
+
def terminate_subscription(mpt_client: MPTClient, subscription_id: str, reason: str) -> dict:
|
|
428
473
|
"""
|
|
429
474
|
Terminates a subscription by calling the MPT API.
|
|
430
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)
|