mpt-extension-sdk 5.2.3__py3-none-any.whl → 5.2.4__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.
@@ -9,6 +9,7 @@ from django.conf import settings
9
9
 
10
10
  from mpt_extension_sdk.core.events.dataclasses import Event
11
11
  from mpt_extension_sdk.core.utils import setup_client
12
+ from mpt_extension_sdk.swo_rql import RQLQuery
12
13
 
13
14
  logger = logging.getLogger(__name__)
14
15
 
@@ -73,9 +74,10 @@ class OrderEventProducer(EventProducer):
73
74
  )
74
75
 
75
76
  def get_processing_orders(self):
76
- products = ",".join(settings.MPT_PRODUCTS_IDS)
77
77
  orders = []
78
- rql_query = f"and(in(agreement.product.id,({products})),eq(status,processing))"
78
+ rql_query = RQLQuery().agreement.product.id.in_(
79
+ settings.MPT_PRODUCTS_IDS
80
+ ) and RQLQuery(status="processing")
79
81
  url = (
80
82
  f"/commerce/orders?{rql_query}&select=audit,parameters,lines,subscriptions,"
81
83
  f"subscriptions.lines,agreement,buyer,seller&order=audit.created.at"
@@ -0,0 +1,5 @@
1
+ from mpt_extension_sdk.swo_rql.query_builder import RQLQuery
2
+
3
+ R = RQLQuery
4
+
5
+ __all__ = ["R", "RQLQuery"]
@@ -0,0 +1,7 @@
1
+ COMP = ("eq", "ne", "lt", "le", "gt", "ge")
2
+ SEARCH = ("like", "ilike")
3
+ LIST = ("in", "out")
4
+ NULL = "null"
5
+ EMPTY = "empty"
6
+
7
+ KEYWORDS = (*COMP, *SEARCH, *LIST, NULL, EMPTY)
@@ -0,0 +1,392 @@
1
+ from mpt_extension_sdk.swo_rql import constants
2
+
3
+
4
+ def parse_kwargs(query_dict):
5
+ query = []
6
+ for lookup, value in query_dict.items():
7
+ tokens = lookup.split("__")
8
+ if len(tokens) == 1:
9
+ # field=value
10
+ field = tokens[0]
11
+ value = rql_encode("eq", value)
12
+ query.append(f"eq({field},{value})")
13
+ continue
14
+ op = tokens[-1]
15
+ if op not in constants.KEYWORDS:
16
+ # field__nested=value
17
+ field = ".".join(tokens)
18
+ value = rql_encode("eq", value)
19
+ query.append(f"eq({field},{value})")
20
+ continue
21
+ field = ".".join(tokens[:-1])
22
+ if op in constants.COMP or op in constants.SEARCH:
23
+ value = rql_encode(op, value)
24
+ query.append(f"{op}({field},{value})")
25
+ continue
26
+ if op in constants.LIST:
27
+ value = rql_encode(op, value)
28
+ query.append(f"{op}({field},({value}))")
29
+ continue
30
+
31
+ cmpop = "eq" if value is True else "ne"
32
+ expr = "null()" if op == constants.NULL else "empty()"
33
+ query.append(f"{cmpop}({field},{expr})")
34
+
35
+ return query
36
+
37
+
38
+ def rql_encode(op, value):
39
+ from datetime import date, datetime
40
+ from decimal import Decimal
41
+
42
+ if op not in constants.LIST:
43
+ if isinstance(value, str):
44
+ return value
45
+ if isinstance(value, bool):
46
+ return "true" if value else "false"
47
+ if isinstance(value, int | float | Decimal):
48
+ return str(value)
49
+ if isinstance(value, date | datetime):
50
+ return value.isoformat()
51
+ if op in constants.LIST and isinstance(value, list | tuple):
52
+ return ",".join(value)
53
+ raise TypeError(f"the `{op}` operator doesn't support the {type(value)} type.")
54
+
55
+
56
+ class RQLQuery:
57
+ """
58
+ Helper class to construct complex RQL queries.
59
+
60
+ Usage:
61
+
62
+ ```py3
63
+ rql = R(field='value', field2__in=('v1', 'v2'), field3__empty=True)
64
+ ```
65
+ !!! note
66
+ All the lookups expressed as keyword arguments are combined together with a logical `and`.
67
+
68
+
69
+ Using the ``n`` method:
70
+
71
+ ```py3
72
+ rql = (
73
+ R().n('field').eq('value')
74
+ & R().n('field2').anyof(('v1', 'v2'))
75
+ & R().n('field3').empty(True)
76
+ )
77
+ ```
78
+
79
+ The previous query can be expressed in a more concise form like:
80
+
81
+ ```py3
82
+ rql = R().field.eq('value') & R().field2.anyof(('v1', 'v2')) & r.field3.empty(True)
83
+ ```
84
+
85
+ ```py3
86
+ rql = R("field").eq("value")
87
+ ```
88
+
89
+ The R object support the bitwise operators `&`, `|` and `~`.
90
+
91
+ Nested fields can be expressed using dot notation:
92
+
93
+ ```py3
94
+ rql = R().n('nested.field').eq('value')
95
+ ```
96
+
97
+ or
98
+
99
+ ```py3
100
+ rql = R().nested.field.eq('value')
101
+ ```
102
+ """
103
+
104
+ AND = "and"
105
+ OR = "or"
106
+ EXPRESSION = "expr"
107
+
108
+ def __init__(
109
+ self,
110
+ _field=None,
111
+ *,
112
+ _op=EXPRESSION,
113
+ _children=None,
114
+ _negated=False,
115
+ _expr=None,
116
+ **kwargs,
117
+ ):
118
+ self.op = _op
119
+ self.children = _children or []
120
+ self.negated = _negated
121
+ self.expr = _expr
122
+ self._path = []
123
+ self._field = None
124
+ if _field:
125
+ self.n(_field)
126
+ if len(kwargs) == 1:
127
+ self.op = self.EXPRESSION
128
+ self.expr = parse_kwargs(kwargs)[0]
129
+ if len(kwargs) > 1:
130
+ self.op = self.AND
131
+ for token in parse_kwargs(kwargs):
132
+ self.children.append(RQLQuery(_expr=token))
133
+
134
+ def __len__(self):
135
+ if self.op == self.EXPRESSION:
136
+ if self.expr:
137
+ return 1
138
+ return 0
139
+ return len(self.children)
140
+
141
+ def __bool__(self):
142
+ return bool(self.children) or bool(self.expr)
143
+
144
+ def __eq__(self, other):
145
+ return (
146
+ self.op == other.op
147
+ and self.children == other.children
148
+ and self.negated == other.negated
149
+ and self.expr == other.expr
150
+ )
151
+
152
+ def __hash__(self):
153
+ return hash(
154
+ (
155
+ self.op,
156
+ self.expr,
157
+ self.negated,
158
+ *(hash(value) for value in self.children),
159
+ ),
160
+ )
161
+
162
+ def __repr__(self):
163
+ if self.op == self.EXPRESSION:
164
+ return f"<R({self.op}) {self.expr}>"
165
+ return f"<R({self.op})>"
166
+
167
+ def __and__(self, other):
168
+ return self._join(other, self.AND)
169
+
170
+ def __or__(self, other):
171
+ return self._join(other, self.OR)
172
+
173
+ def __invert__(self):
174
+ query = RQLQuery(_op=self.AND, _expr=self.expr, _negated=True)
175
+ query._append(self)
176
+ return query
177
+
178
+ def __getattr__(self, name):
179
+ return self.n(name)
180
+
181
+ def __str__(self):
182
+ return self._to_string(self)
183
+
184
+ def n(self, name):
185
+ """
186
+ Set the current field for this `R` object.
187
+
188
+ Args:
189
+ name (str): Name of the field.
190
+ """
191
+ if self._field:
192
+ raise AttributeError("Already evaluated")
193
+
194
+ self._path.extend(name.split("."))
195
+ return self
196
+
197
+ def ne(self, value):
198
+ """
199
+ Apply the `ne` operator to the field this `R` object refers to.
200
+
201
+ Args:
202
+ value (str): The value to which compare the field.
203
+ """
204
+ return self._bin("ne", value)
205
+
206
+ def eq(self, value):
207
+ """
208
+ Apply the `eq` operator to the field this `R` object refers to.
209
+
210
+ Args:
211
+ value (str): The value to which compare the field.
212
+ """
213
+ return self._bin("eq", value)
214
+
215
+ def lt(self, value):
216
+ """
217
+ Apply the `lt` operator to the field this `R` object refers to.
218
+
219
+ Args:
220
+ value (str): The value to which compare the field.
221
+ """
222
+ return self._bin("lt", value)
223
+
224
+ def le(self, value):
225
+ """
226
+ Apply the `le` operator to the field this `R` object refers to.
227
+
228
+ Args:
229
+ value (str): The value to which compare the field.
230
+ """
231
+ return self._bin("le", value)
232
+
233
+ def gt(self, value):
234
+ """
235
+ Apply the `gt` operator to the field this `R` object refers to.
236
+
237
+ Args:
238
+ value (str): The value to which compare the field.
239
+ """
240
+ return self._bin("gt", value)
241
+
242
+ def ge(self, value):
243
+ """
244
+ Apply the `ge` operator to the field this `R` object refers to.
245
+
246
+ Args:
247
+ value (str): The value to which compare the field.
248
+ """
249
+ return self._bin("ge", value)
250
+
251
+ def out(self, value: list[str]):
252
+ """
253
+ Apply the `out` operator to the field this `R` object refers to.
254
+
255
+ Args:
256
+ value (list[str]): The list of values to which compare the field.
257
+ """
258
+ return self._list("out", value)
259
+
260
+ def in_(self, value):
261
+ return self._list("in", value)
262
+
263
+ def oneof(self, value: list[str]):
264
+ """
265
+ Apply the `in` operator to the field this `R` object refers to.
266
+
267
+ Args:
268
+ value (list[str]): The list of values to which compare the field.
269
+ """
270
+ return self._list("in", value)
271
+
272
+ def null(self, value: list[str]):
273
+ """
274
+ Apply the `null` operator to the field this `R` object refers to.
275
+
276
+ Args:
277
+ value (list[str]): The value to which compare the field.
278
+ """
279
+ return self._bool("null", value)
280
+
281
+ def empty(self, value: bool = True):
282
+ """
283
+ Apply the `empty` operator to the field this `R` object refers to.
284
+
285
+ Usage: `R().field.empty()
286
+
287
+ For not empty: `R().field.empty(False)` or `R().field.not_empty()`
288
+ """
289
+ return self._bool("empty", value)
290
+
291
+ def not_empty(self):
292
+ """
293
+ Apply the `not_empty` operator to the field this `R` object refers to.
294
+ """
295
+ query = self._bool("empty", False)
296
+ return query
297
+
298
+ def like(self, value: list[str]):
299
+ """
300
+ Apply the `like` operator to the field this `R` object refers to.
301
+
302
+ Args:
303
+ value (list[str]): The value to which compare the field.
304
+ """
305
+ return self._bin("like", value)
306
+
307
+ def ilike(self, value: list[str]):
308
+ """
309
+ Apply the `ilike` operator to the field this `R` object refers to.
310
+
311
+ Args:
312
+ value (list[str]): The value to which compare the field.
313
+ """
314
+ return self._bin("ilike", value)
315
+
316
+ def _bin(self, op, value):
317
+ self._field = ".".join(self._path)
318
+ value = rql_encode(op, value)
319
+ self.expr = f"{op}({self._field},{value})"
320
+ return self
321
+
322
+ def _list(self, op, value):
323
+ self._field = ".".join(self._path)
324
+ value = rql_encode(op, value)
325
+ self.expr = f"{op}({self._field},({value}))"
326
+ return self
327
+
328
+ def _bool(self, expr, value):
329
+ self._field = ".".join(self._path)
330
+ if bool(value) is False:
331
+ self.expr = f"ne({self._field},{expr}())"
332
+ return self
333
+ self.expr = f"eq({self._field},{expr}())"
334
+ return self
335
+
336
+ def _to_string(self, query):
337
+ tokens = []
338
+ if query.expr:
339
+ if query.negated:
340
+ return f"not({query.expr})"
341
+ return query.expr
342
+ for c in query.children:
343
+ if c.expr:
344
+ if c.negated:
345
+ tokens.append(f"not({c.expr})")
346
+ else:
347
+ tokens.append(c.expr)
348
+ continue
349
+ tokens.append(self._to_string(c))
350
+
351
+ if not tokens:
352
+ return ""
353
+
354
+ if query.negated:
355
+ return f'not({query.op}({",".join(tokens)}))'
356
+ return f'{query.op}({",".join(tokens)})'
357
+
358
+ def _copy(self, other):
359
+ return RQLQuery(
360
+ _op=other.op,
361
+ _children=other.children[:],
362
+ _expr=other.expr,
363
+ )
364
+
365
+ def _join(self, other, op):
366
+ if self == other:
367
+ return self._copy(self)
368
+ if not other:
369
+ return self._copy(self)
370
+ if not self:
371
+ return self._copy(other)
372
+
373
+ query = RQLQuery(_op=op)
374
+ query._append(self)
375
+ query._append(other)
376
+ return query
377
+
378
+ def _append(self, other):
379
+ if other in self.children:
380
+ return other
381
+
382
+ if (
383
+ other.op == self.op or (len(other) == 1 and other.op != self.EXPRESSION)
384
+ ) and not other.negated:
385
+ self.children.extend(other.children)
386
+ return self
387
+
388
+ self.children.append(other)
389
+ return self
390
+
391
+
392
+ R = RQLQuery
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mpt-extension-sdk
3
- Version: 5.2.3
3
+ Version: 5.2.4
4
4
  Summary: Extensions SDK for SoftwareONE Marketplace Platform
5
5
  Author: SoftwareOne AG
6
6
  License: Apache-2.0 license
@@ -9,7 +9,6 @@ Requires-Python: <4,>=3.12
9
9
  Requires-Dist: azure-identity<2,>=1.21.0
10
10
  Requires-Dist: azure-keyvault-secrets<5,>=4.9.0
11
11
  Requires-Dist: azure-monitor-opentelemetry-exporter==1.0.0b25
12
- Requires-Dist: boto3==1.34.*
13
12
  Requires-Dist: click==8.1.*
14
13
  Requires-Dist: debugpy==1.8.*
15
14
  Requires-Dist: django-ninja==1.1.*
@@ -40,10 +40,13 @@ mpt_extension_sdk/runtime/djapp/management/commands/__init__.py,sha256=47DEQpj8H
40
40
  mpt_extension_sdk/runtime/djapp/management/commands/consume_events.py,sha256=BVb4sgKI2Ep759bDcOH5Qm1xJEewc5-bNt_snspV2ck,1229
41
41
  mpt_extension_sdk/runtime/events/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
42
  mpt_extension_sdk/runtime/events/dispatcher.py,sha256=ZMswk50vGGfUgKeQ2Cc3k9wtrm63z0OH7EtlKCkWQYM,3008
43
- mpt_extension_sdk/runtime/events/producers.py,sha256=otc5x2jR103h_C8X4iY3mmxqTiRNio5S0_WslIQwypE,3611
43
+ mpt_extension_sdk/runtime/events/producers.py,sha256=amTUJxTYkePNMQEZ6yP9iCN_0Wk2dpGNl32kBwJS5qE,3654
44
44
  mpt_extension_sdk/runtime/events/utils.py,sha256=bxFo-iQUqEJDqcIotC88Ty3Xt0K2FyYjA9UMWFEyMKI,2663
45
- mpt_extension_sdk-5.2.3.dist-info/METADATA,sha256=OB_k5s1V6X_ka5p-P_PVhvb25xa8Vp1XhPOoFhoudeY,1386
46
- mpt_extension_sdk-5.2.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
47
- mpt_extension_sdk-5.2.3.dist-info/entry_points.txt,sha256=N8T9gBssEOm_UeBf9ABbGqtlnethrumfMoL4hNYWVFA,146
48
- mpt_extension_sdk-5.2.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
49
- mpt_extension_sdk-5.2.3.dist-info/RECORD,,
45
+ mpt_extension_sdk/swo_rql/__init__.py,sha256=QrvRDYhpK-Pd3OF-U2aMeazuKm_kbvXwLRIjd9Mm6ok,104
46
+ mpt_extension_sdk/swo_rql/constants.py,sha256=39BZ78OzdU_dIQtoy-Z_5utXqEXRweIFvM9Zfxg9j5M,171
47
+ mpt_extension_sdk/swo_rql/query_builder.py,sha256=XAoMYV1jaAouYjpqrh8nMaDNzD0as-BhMx8rsvdeZ0k,10500
48
+ mpt_extension_sdk-5.2.4.dist-info/METADATA,sha256=CY_PBVb6d2yRBIrZjGMQU5xHtTDn0QQG-gIBlJithxk,1357
49
+ mpt_extension_sdk-5.2.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
50
+ mpt_extension_sdk-5.2.4.dist-info/entry_points.txt,sha256=MwL1GQnaWIXHSSxWx1gR7Q-NdFsh_451NgIO2l_ymZ4,146
51
+ mpt_extension_sdk-5.2.4.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
52
+ mpt_extension_sdk-5.2.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  [console_scripts]
2
- swoext = mpt_extension_sdk.runtime.swoext:main
2
+ swosdk = mpt_extension_sdk.runtime.swoext:main
3
3
 
4
4
  [swo.mpt.sdk]
5
5
  app_config = mpt_extension_sdk.runtime.djapp.apps:ExtensionConfig