mpt-extension-sdk 5.6.1__py3-none-any.whl → 5.8.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 +79 -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.8.0.dist-info}/METADATA +1 -1
- mpt_extension_sdk-5.8.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.8.0.dist-info}/WHEEL +0 -0
- {mpt_extension_sdk-5.6.1.dist-info → mpt_extension_sdk-5.8.0.dist-info}/entry_points.txt +0 -0
- {mpt_extension_sdk-5.6.1.dist-info → mpt_extension_sdk-5.8.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,17 +262,31 @@ 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
|
|
|
236
272
|
return _paginated(mpt_client, url)
|
|
237
273
|
|
|
238
274
|
|
|
275
|
+
@wrap_mpt_http_error
|
|
276
|
+
def get_product_items_by_period(mpt_client, product_id, period):
|
|
277
|
+
"""Fetches one-time items for a product."""
|
|
278
|
+
product_cond = f"eq(product.id,{product_id})"
|
|
279
|
+
rql_query = f"and({product_cond},eq(terms.period,{period}))"
|
|
280
|
+
url = f"/catalog/items?{rql_query}"
|
|
281
|
+
|
|
282
|
+
return _paginated(mpt_client, url)
|
|
283
|
+
|
|
284
|
+
|
|
239
285
|
def get_agreements_by_ids(mpt_client, ids):
|
|
286
|
+
"""Retrieve agreements by their IDs."""
|
|
287
|
+
ids_str = ",".join(ids)
|
|
240
288
|
rql_query = (
|
|
241
|
-
f"and(in(id,({
|
|
289
|
+
f"and(in(id,({ids_str})),eq(status,Active))"
|
|
242
290
|
"&select=lines,parameters,subscriptions,product,listing"
|
|
243
291
|
)
|
|
244
292
|
return get_agreements_by_query(mpt_client, rql_query)
|
|
@@ -247,7 +295,9 @@ def get_agreements_by_ids(mpt_client, ids):
|
|
|
247
295
|
def get_all_agreements(
|
|
248
296
|
mpt_client,
|
|
249
297
|
):
|
|
250
|
-
|
|
298
|
+
"""Retrieve all active agreements for specific products."""
|
|
299
|
+
product_ids_str = ",".join(settings.MPT_PRODUCTS_IDS)
|
|
300
|
+
product_condition = f"in(product.id,({product_ids_str}))"
|
|
251
301
|
|
|
252
302
|
return get_agreements_by_query(
|
|
253
303
|
mpt_client,
|
|
@@ -256,9 +306,8 @@ def get_all_agreements(
|
|
|
256
306
|
|
|
257
307
|
|
|
258
308
|
@wrap_mpt_http_error
|
|
259
|
-
def get_authorizations_by_currency_and_seller_id(
|
|
260
|
-
|
|
261
|
-
):
|
|
309
|
+
def get_authorizations_by_currency_and_seller_id(mpt_client, product_id, currency, owner_id):
|
|
310
|
+
"""Retrieve authorizations by product ID, currency, and owner ID."""
|
|
262
311
|
authorization_filter = (
|
|
263
312
|
f"eq(product.id,{product_id})&eq(currency,{currency})&eq(owner.id,{owner_id})"
|
|
264
313
|
)
|
|
@@ -269,6 +318,7 @@ def get_authorizations_by_currency_and_seller_id(
|
|
|
269
318
|
|
|
270
319
|
@wrap_mpt_http_error
|
|
271
320
|
def get_gc_price_list_by_currency(mpt_client, product_id, currency):
|
|
321
|
+
"""Retrieve a GC price list by product ID and currency."""
|
|
272
322
|
response = mpt_client.get(
|
|
273
323
|
f"/catalog/price-lists?eq(product.id,{product_id})&eq(currency,{currency})"
|
|
274
324
|
)
|
|
@@ -280,6 +330,7 @@ def get_gc_price_list_by_currency(mpt_client, product_id, currency):
|
|
|
280
330
|
def get_listings_by_price_list_and_seller_and_authorization(
|
|
281
331
|
mpt_client, product_id, price_list_id, seller_id, authorization_id
|
|
282
332
|
):
|
|
333
|
+
"""Retrieve listings by price list, seller, and authorization."""
|
|
283
334
|
response = mpt_client.get(
|
|
284
335
|
f"/catalog/listings?eq(product.id,{product_id})&eq(priceList.id,{price_list_id})"
|
|
285
336
|
f"&eq(seller.id,{seller_id})"
|
|
@@ -291,6 +342,7 @@ def get_listings_by_price_list_and_seller_and_authorization(
|
|
|
291
342
|
|
|
292
343
|
@wrap_mpt_http_error
|
|
293
344
|
def create_listing(mpt_client, listing):
|
|
345
|
+
"""Create a new listing."""
|
|
294
346
|
response = mpt_client.post(
|
|
295
347
|
"/catalog/listings",
|
|
296
348
|
json=listing,
|
|
@@ -301,6 +353,7 @@ def create_listing(mpt_client, listing):
|
|
|
301
353
|
|
|
302
354
|
@wrap_mpt_http_error
|
|
303
355
|
def create_agreement(mpt_client, agreement):
|
|
356
|
+
"""Create a new agreement."""
|
|
304
357
|
response = mpt_client.post(
|
|
305
358
|
"/commerce/agreements",
|
|
306
359
|
json=agreement,
|
|
@@ -311,6 +364,7 @@ def create_agreement(mpt_client, agreement):
|
|
|
311
364
|
|
|
312
365
|
@wrap_mpt_http_error
|
|
313
366
|
def create_agreement_subscription(mpt_client, subscription):
|
|
367
|
+
"""Create a new agreement subscription."""
|
|
314
368
|
response = mpt_client.post(
|
|
315
369
|
"/commerce/subscriptions",
|
|
316
370
|
json=subscription,
|
|
@@ -321,15 +375,15 @@ def create_agreement_subscription(mpt_client, subscription):
|
|
|
321
375
|
|
|
322
376
|
@wrap_mpt_http_error
|
|
323
377
|
def get_listing_by_id(mpt_client, listing_id):
|
|
378
|
+
"""Retrieve a listing by ID."""
|
|
324
379
|
response = mpt_client.get(f"/catalog/listings/{listing_id}")
|
|
325
380
|
response.raise_for_status()
|
|
326
381
|
return response.json()
|
|
327
382
|
|
|
328
383
|
|
|
329
384
|
@wrap_mpt_http_error
|
|
330
|
-
def get_agreement_subscription_by_external_id(
|
|
331
|
-
|
|
332
|
-
):
|
|
385
|
+
def get_agreement_subscription_by_external_id(mpt_client, agreement_id, subscription_external_id):
|
|
386
|
+
"""Retrieve an agreement subscription by external ID."""
|
|
333
387
|
response = mpt_client.get(
|
|
334
388
|
f"/commerce/subscriptions?eq(externalIds.vendor,{subscription_external_id})"
|
|
335
389
|
f"&eq(agreement.id,{agreement_id})"
|
|
@@ -344,6 +398,7 @@ def get_agreement_subscription_by_external_id(
|
|
|
344
398
|
|
|
345
399
|
@wrap_mpt_http_error
|
|
346
400
|
def get_agreements_by_external_id_values(mpt_client, external_id, display_values):
|
|
401
|
+
"""Retrieve agreements by external ID and display values."""
|
|
347
402
|
display_values_list = ",".join(display_values)
|
|
348
403
|
rql_query = (
|
|
349
404
|
f"any(parameters.fulfillment,and("
|
|
@@ -358,9 +413,8 @@ def get_agreements_by_external_id_values(mpt_client, external_id, display_values
|
|
|
358
413
|
|
|
359
414
|
|
|
360
415
|
@wrap_mpt_http_error
|
|
361
|
-
def get_agreements_by_customer_deployments(
|
|
362
|
-
|
|
363
|
-
):
|
|
416
|
+
def get_agreements_by_customer_deployments(mpt_client, deployment_id_parameter, deployment_ids):
|
|
417
|
+
"""Retrieve agreements by customer deployments."""
|
|
364
418
|
deployments_list = ",".join(deployment_ids)
|
|
365
419
|
rql_query = (
|
|
366
420
|
f"any(parameters.fulfillment,and("
|
|
@@ -376,6 +430,7 @@ def get_agreements_by_customer_deployments(
|
|
|
376
430
|
|
|
377
431
|
@wrap_mpt_http_error
|
|
378
432
|
def get_buyer(mpt_client, buyer_id):
|
|
433
|
+
"""Retrieve a buyer by ID."""
|
|
379
434
|
response = mpt_client.get(f"/accounts/buyers/{buyer_id}")
|
|
380
435
|
response.raise_for_status()
|
|
381
436
|
return response.json()
|
|
@@ -392,6 +447,8 @@ def notify(
|
|
|
392
447
|
limit: int = 1000,
|
|
393
448
|
) -> None:
|
|
394
449
|
"""
|
|
450
|
+
Sends notification to multiple recipients for a specific buyer.
|
|
451
|
+
|
|
395
452
|
Sends notifications to multiple recipients in batches for a specific buyer and
|
|
396
453
|
category through the MPTClient service. The function retrieves recipients,
|
|
397
454
|
groups them into manageable batches, and sends notifications using the provided
|
|
@@ -422,9 +479,7 @@ def notify(
|
|
|
422
479
|
|
|
423
480
|
|
|
424
481
|
@wrap_mpt_http_error
|
|
425
|
-
def terminate_subscription(
|
|
426
|
-
mpt_client: MPTClient, subscription_id: str, reason: str
|
|
427
|
-
) -> dict:
|
|
482
|
+
def terminate_subscription(mpt_client: MPTClient, subscription_id: str, reason: str) -> dict:
|
|
428
483
|
"""
|
|
429
484
|
Terminates a subscription by calling the MPT API.
|
|
430
485
|
|
|
@@ -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)
|