mpt-extension-sdk 4.0.6__py3-none-any.whl → 4.1.1__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.
@@ -74,7 +74,7 @@ class KeyVault(Session):
74
74
  )
75
75
  return None
76
76
 
77
- def _get_key_vault_url(self, key_vault_name: str):
77
+ def _get_key_vault_url(self, key_vault_name: str): # pragma: no cover
78
78
  """
79
79
  Construct the Key Vault URL using the provided Key Vault name.
80
80
 
@@ -88,7 +88,7 @@ class KeyVault(Session):
88
88
  # Construct the Key Vault URL
89
89
  return f"https://{key_vault_name}.vault.azure.net/"
90
90
 
91
- def _get_key_vault_client(self, key_vault_name: str):
91
+ def _get_key_vault_client(self, key_vault_name: str): # pragma: no cover
92
92
  """
93
93
  Create a Key Vault client using the provided Key Vault URL and secret name.
94
94
 
@@ -1,13 +1,18 @@
1
1
  import logging
2
2
  from datetime import date
3
+ from enum import Enum
3
4
  from functools import cache
5
+ from itertools import batched
4
6
 
5
7
  from django.conf import settings
6
8
 
9
+ from mpt_extension_sdk.mpt_http.base import MPTClient
7
10
  from mpt_extension_sdk.mpt_http.wrap_http_error import wrap_mpt_http_error
8
11
 
9
12
  logger = logging.getLogger(__name__)
10
13
 
14
+ NotifyCategories = Enum("NotifyCategories", settings.MPT_NOTIFY_CATEGORIES)
15
+
11
16
 
12
17
  def _has_more_pages(page):
13
18
  if not page:
@@ -16,10 +21,9 @@ def _has_more_pages(page):
16
21
  return pagination["total"] > pagination["limit"] + pagination["offset"]
17
22
 
18
23
 
19
- def _paginated(mpt_client, url):
24
+ def _paginated(mpt_client, url, limit=10):
20
25
  items = []
21
26
  page = None
22
- limit = 10
23
27
  offset = 0
24
28
  while _has_more_pages(page):
25
29
  response = mpt_client.get(f"{url}&limit={limit}&offset={offset}")
@@ -374,3 +378,56 @@ def get_buyer(mpt_client, buyer_id):
374
378
  response = mpt_client.get(f"/accounts/buyers/{buyer_id}")
375
379
  response.raise_for_status()
376
380
  return response.json()
381
+
382
+
383
+ @wrap_mpt_http_error
384
+ def notify(
385
+ mpt_client: MPTClient,
386
+ category_id: str,
387
+ account_id: str,
388
+ buyer_id: str,
389
+ subject: str,
390
+ message_body: str,
391
+ limit: int = 1000,
392
+ ):
393
+ """
394
+ Sends notifications to multiple recipients in batches for a specific buyer and
395
+ category through the MPTClient service. The function retrieves recipients,
396
+ groups them into manageable batches, and sends notifications using the provided
397
+ message details.
398
+
399
+ Args:
400
+ mpt_client (MPTClient): Client object for interacting with MPT service.
401
+ category_id (str): Identifier for the category of recipients or messages.
402
+ account_id (str): Identifier for the associated account.
403
+ buyer_id (str): Identifier for the buyer related to the notification.
404
+ subject (str): Subject/title of the notification to be sent.
405
+ message_body (str): Content/body of the notification message.
406
+ limit (int): Maximum number of recipients to process per batch. Defaults
407
+ to 1000.
408
+
409
+ Returns:
410
+ None
411
+ """
412
+ recipients = _paginated(
413
+ mpt_client,
414
+ url=(
415
+ f"notifications/accounts/{account_id}/categories/{category_id}/contacts?"
416
+ f"select=id,-email,-name,-status,-user&"
417
+ f"filter(group.buyers.id,{buyer_id})"
418
+ ),
419
+ limit=limit,
420
+ )
421
+
422
+ for contacts in batched(recipients, limit):
423
+ response = mpt_client.post(
424
+ "notifications/batches",
425
+ json={
426
+ "category": {"id": category_id},
427
+ "subject": subject,
428
+ "body": message_body,
429
+ "contacts": contacts,
430
+ "buyer": {"id": buyer_id},
431
+ },
432
+ )
433
+ response.raise_for_status()
@@ -10,6 +10,7 @@ For the full list of settings and their values, see
10
10
  https://docs.djangoproject.com/en/4.2/ref/settings/
11
11
  """
12
12
 
13
+ import json
13
14
  import os
14
15
  from pathlib import Path
15
16
 
@@ -25,12 +26,6 @@ BASE_DIR = Path(__file__).resolve().parent.parent
25
26
  # Quick-start development settings - unsuitable for production
26
27
  # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
27
28
 
28
- # SECURITY WARNING: keep the secret key used in production secret!
29
- SECRET_KEY = os.getenv(
30
- "MPT_DJANGO_SECRET_KEY",
31
- "django-insecure-6r_%9ku+bg0=@xw1ah$wh+liwbsyhwpn#6alt*ppjn8t_uyp-u",
32
- )
33
-
34
29
  # SECURITY WARNING: don't run with debug turned on in production!
35
30
  DEBUG = True
36
31
 
@@ -213,6 +208,11 @@ MPT_ORDERS_API_POLLING_INTERVAL_SECS = int(
213
208
  os.getenv("MPT_ORDERS_API_POLLING_INTERVAL_SECS", "120")
214
209
  )
215
210
 
211
+ # TODO: Should be synced with the initializer.py::initialize function
212
+ MPT_NOTIFY_CATEGORIES = json.loads(
213
+ os.getenv("MPT_NOTIFY_CATEGORIES", '{"ORDERS": "NTC-0000-0006"}')
214
+ )
215
+
216
216
  EXTENSION_CONFIG = {
217
217
  "DUE_DATE_DAYS": "30",
218
218
  }
@@ -8,7 +8,7 @@ from mpt_extension_sdk.runtime.events.dispatcher import Dispatcher
8
8
  from mpt_extension_sdk.runtime.events.producers import OrderEventProducer
9
9
 
10
10
 
11
- class Command(BaseCommand):
11
+ class Command(BaseCommand): # pragma: no cover
12
12
  help = CONSUME_EVENTS_HELP_TEXT
13
13
  producer_classes = [
14
14
  OrderEventProducer,
@@ -5,7 +5,7 @@ from mpt_extension_sdk.mpt_http.base import MPTClient
5
5
  _CLIENT = None
6
6
 
7
7
 
8
- class MPTClientMiddleware:
8
+ class MPTClientMiddleware: # pragma: no cover
9
9
  def __init__(self, get_response):
10
10
  self.get_response = get_response
11
11
 
@@ -32,7 +32,7 @@ class EventProducer(ABC):
32
32
  self.producer.join()
33
33
 
34
34
  @contextmanager
35
- def sleep(self, secs, interval=0.5):
35
+ def sleep(self, secs, interval=0.5): # pragma: no cover
36
36
  yield
37
37
  sleeped = 0
38
38
  while sleeped < secs and self.running_event.is_set():
@@ -61,7 +61,7 @@ class OrderEventProducer(EventProducer):
61
61
  for order in orders:
62
62
  self.dispatcher.dispatch_event(Event(order["id"], "orders", order))
63
63
 
64
- def produce_events_with_context(self):
64
+ def produce_events_with_context(self): # pragma: no cover
65
65
  while self.running:
66
66
  with self.sleep(settings.MPT_ORDERS_API_POLLING_INTERVAL_SECS):
67
67
  orders = self.get_processing_orders()
@@ -89,7 +89,6 @@ class OrderEventProducer(EventProducer):
89
89
  except requests.RequestException:
90
90
  logger.exception("Cannot retrieve orders")
91
91
  return []
92
-
93
92
  if response.status_code == 200:
94
93
  page = response.json()
95
94
  orders.extend(page["data"])
@@ -79,7 +79,7 @@ class Master:
79
79
  self.workers[worker_type] = p
80
80
  logger.info(f"{worker_type.capitalize()} worker pid: {p.pid}")
81
81
 
82
- def monitor_processes(self):
82
+ def monitor_processes(self): # pragma: no cover
83
83
  while self.monitor_event.is_set():
84
84
  exited_workers = []
85
85
  for worker_type, p in self.workers.items():
@@ -112,16 +112,16 @@ class Master:
112
112
  self.stop()
113
113
  self.start()
114
114
 
115
- def __iter__(self):
115
+ def __iter__(self): # pragma: no cover
116
116
  return self
117
117
 
118
- def __next__(self):
118
+ def __next__(self): # pragma: no cover
119
119
  changes = next(self.watcher)
120
120
  if changes:
121
121
  return list({Path(c[1]) for c in changes})
122
122
  return None
123
123
 
124
- def run(self):
124
+ def run(self): # pragma: no cover
125
125
  self.start()
126
126
  if self.options.get("reload"):
127
127
  for files_changed in self:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: mpt-extension-sdk
3
- Version: 4.0.6
3
+ Version: 4.1.1
4
4
  Summary: Extensions SDK for SoftwareONE Marketplace Platform
5
5
  License: Apache-2.0
6
6
  Author: SoftwareOne AG
@@ -12,10 +12,10 @@ mpt_extension_sdk/flows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
12
12
  mpt_extension_sdk/flows/context.py,sha256=J_U9nTtY89mUvrcOqdgpk-21F8f6YENSmjxgpVK6XWs,964
13
13
  mpt_extension_sdk/flows/pipeline.py,sha256=3BO5X3jvuYnRTWrXRzImjVCVOCut8dSvkT2LZwfpaZk,1374
14
14
  mpt_extension_sdk/key_vault/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- mpt_extension_sdk/key_vault/base.py,sha256=0aXH5c5n9-rUCwmBVY_HtIFUVB6JUC0137Duyh-basw,3321
15
+ mpt_extension_sdk/key_vault/base.py,sha256=RVG_Wiq-MrPyngJ4gfUd8oxoh1LUop1SvtCM2xdlx8M,3361
16
16
  mpt_extension_sdk/mpt_http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  mpt_extension_sdk/mpt_http/base.py,sha256=3qgo9BW-uTRAbex26urMvLZU0fwnYnBprDRIJCHvTOk,1284
18
- mpt_extension_sdk/mpt_http/mpt.py,sha256=c00ouqvK6temaVVND4XNVbKAeJ_7epMLL6BEBBB6WAs,10572
18
+ mpt_extension_sdk/mpt_http/mpt.py,sha256=HIPsoeaHJX3Zl9t14EvhIIcVuEjXWuMAuwF1wW0BHr0,12504
19
19
  mpt_extension_sdk/mpt_http/utils.py,sha256=3wJTT84CXYGjZw6FksNDX8tIHijkL3Wcmld9r6Iz0xc,95
20
20
  mpt_extension_sdk/mpt_http/wrap_http_error.py,sha256=j8K6Ddx7NiM-FPRMQi25vuULAW8LXxzZ4EwuIJufxis,1801
21
21
  mpt_extension_sdk/runtime/__init__.py,sha256=PQSAz9qvXHbxrn4KvuXSHB8y-A4Jpgbm6ZV-wPiNPyw,194
@@ -25,23 +25,23 @@ mpt_extension_sdk/runtime/commands/run.py,sha256=1Qrqr2Hq1J8JvXtmEdgUNU9u3JLd_92
25
25
  mpt_extension_sdk/runtime/djapp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  mpt_extension_sdk/runtime/djapp/apps.py,sha256=YA1G5HaRqFBS-0DfYQuFpMgzUCYpu5T8x5LEC6MwI_M,1398
27
27
  mpt_extension_sdk/runtime/djapp/conf/__init__.py,sha256=_sHo76rGeXSTH8bW3pacyGFfNYSln8oLRwOSstYOmco,350
28
- mpt_extension_sdk/runtime/djapp/conf/default.py,sha256=qlgOaX7hcAyt52lAZD5JufQq898yWnWR-qUzxhycXjk,6126
28
+ mpt_extension_sdk/runtime/djapp/conf/default.py,sha256=vOtxAZkIJ78D1p1mua3Ls6BwmNybW7NZl2G7F3jb33k,6120
29
29
  mpt_extension_sdk/runtime/djapp/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  mpt_extension_sdk/runtime/djapp/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- mpt_extension_sdk/runtime/djapp/management/commands/consume_events.py,sha256=r5aW4Sratsmnu6ccI7kHYZMBGL8P697wKts5iZPoaHc,1080
32
- mpt_extension_sdk/runtime/djapp/middleware.py,sha256=MES6-bK64pX1B1XGXC3nGPzPSld6xrQfmF1vYQv2Eeg,535
31
+ mpt_extension_sdk/runtime/djapp/management/commands/consume_events.py,sha256=1rfldJpnKgpgAhW15wkJ_TLb_TKimkC5hryn1TPEfuk,1100
32
+ mpt_extension_sdk/runtime/djapp/middleware.py,sha256=oAA5khXW6xeUjnOV63pNCF4D7QFVD6PayIa_489KRmg,555
33
33
  mpt_extension_sdk/runtime/events/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  mpt_extension_sdk/runtime/events/dispatcher.py,sha256=GYICBuA9bO38TgDk0FKS_Z9D-8YB-tmNf4O_pUxuomc,2801
35
- mpt_extension_sdk/runtime/events/producers.py,sha256=EiR3MgwEw6Z5cwMh4xBo9JBsd4oSEYi0U1iNYNxK7P4,3551
35
+ mpt_extension_sdk/runtime/events/producers.py,sha256=zrJJzdjqMZeqotL1FZY_1sKo_7vX9InWG7HH9t6H4V4,3590
36
36
  mpt_extension_sdk/runtime/events/utils.py,sha256=bxFo-iQUqEJDqcIotC88Ty3Xt0K2FyYjA9UMWFEyMKI,2663
37
37
  mpt_extension_sdk/runtime/initializer.py,sha256=3gX9OAeTp2lsgsP0h3HVzkEUvCTa9fBIsBGVo7E6JFY,2229
38
38
  mpt_extension_sdk/runtime/logging.py,sha256=wJ6KyvnuvMZELI1Hd0fYbuHr7YiP3GIXFBvvSdmd7nQ,885
39
- mpt_extension_sdk/runtime/master.py,sha256=oBI3WHpf2YkTRsRyT1SFIyvCM7gGQpbtXvI1lYe6-jg,4390
39
+ mpt_extension_sdk/runtime/master.py,sha256=O0zH_mrDotoPO8ytTcUhaHaBi0X3B2OAbTSXqudJoJc,4470
40
40
  mpt_extension_sdk/runtime/swoext.py,sha256=LS0YZXwQsHDaYjalDGfYQi5cAL3aKQhphf3chrNmNBk,1665
41
41
  mpt_extension_sdk/runtime/utils.py,sha256=ZnRWIBwYzOerx30KFmBXLUe19qzwE4JFNkNMGVqjuyk,3365
42
42
  mpt_extension_sdk/runtime/workers.py,sha256=k25jFlh-NdHVqeMV2AgoC6IHIlbRHcDoRB5xqWwMPFg,2411
43
- mpt_extension_sdk-4.0.6.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
44
- mpt_extension_sdk-4.0.6.dist-info/METADATA,sha256=mqCgiEGwkxt89Rra5o9q0o3qBO8ZVPT8kRs3PtqdUGQ,1666
45
- mpt_extension_sdk-4.0.6.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
46
- mpt_extension_sdk-4.0.6.dist-info/entry_points.txt,sha256=8uZQihFseK4Kp5XWHl9EDYneg-CLS8BFO-ygp0h34nc,143
47
- mpt_extension_sdk-4.0.6.dist-info/RECORD,,
43
+ mpt_extension_sdk-4.1.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
44
+ mpt_extension_sdk-4.1.1.dist-info/METADATA,sha256=2tNSNBV4FeKANlNZ8qgO1FP4yIxDh2gSgY0gkg1Qa58,1666
45
+ mpt_extension_sdk-4.1.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
46
+ mpt_extension_sdk-4.1.1.dist-info/entry_points.txt,sha256=8uZQihFseK4Kp5XWHl9EDYneg-CLS8BFO-ygp0h34nc,143
47
+ mpt_extension_sdk-4.1.1.dist-info/RECORD,,