planar 0.5.0__py3-none-any.whl → 0.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.
Files changed (211) hide show
  1. planar/_version.py +1 -1
  2. planar/ai/agent.py +155 -283
  3. planar/ai/agent_base.py +170 -0
  4. planar/ai/agent_utils.py +7 -0
  5. planar/ai/pydantic_ai.py +638 -0
  6. planar/ai/test_agent_serialization.py +1 -1
  7. planar/app.py +64 -20
  8. planar/cli.py +39 -27
  9. planar/config.py +45 -36
  10. planar/db/db.py +2 -1
  11. planar/files/storage/azure_blob.py +343 -0
  12. planar/files/storage/base.py +7 -0
  13. planar/files/storage/config.py +70 -7
  14. planar/files/storage/s3.py +6 -6
  15. planar/files/storage/test_azure_blob.py +435 -0
  16. planar/logging/formatter.py +17 -4
  17. planar/logging/test_formatter.py +327 -0
  18. planar/registry_items.py +2 -1
  19. planar/routers/agents_router.py +3 -1
  20. planar/routers/files.py +11 -2
  21. planar/routers/models.py +14 -1
  22. planar/routers/test_agents_router.py +1 -1
  23. planar/routers/test_files_router.py +49 -0
  24. planar/routers/test_routes_security.py +5 -7
  25. planar/routers/test_workflow_router.py +270 -3
  26. planar/routers/workflow.py +95 -36
  27. planar/rules/models.py +36 -39
  28. planar/rules/test_data/account_dormancy_management.json +223 -0
  29. planar/rules/test_data/airline_loyalty_points_calculator.json +262 -0
  30. planar/rules/test_data/applicant_risk_assessment.json +435 -0
  31. planar/rules/test_data/booking_fraud_detection.json +407 -0
  32. planar/rules/test_data/cellular_data_rollover_system.json +258 -0
  33. planar/rules/test_data/clinical_trial_eligibility_screener.json +437 -0
  34. planar/rules/test_data/customer_lifetime_value.json +143 -0
  35. planar/rules/test_data/import_duties_calculator.json +289 -0
  36. planar/rules/test_data/insurance_prior_authorization.json +443 -0
  37. planar/rules/test_data/online_check_in_eligibility_system.json +254 -0
  38. planar/rules/test_data/order_consolidation_system.json +375 -0
  39. planar/rules/test_data/portfolio_risk_monitor.json +471 -0
  40. planar/rules/test_data/supply_chain_risk.json +253 -0
  41. planar/rules/test_data/warehouse_cross_docking.json +237 -0
  42. planar/rules/test_rules.py +750 -6
  43. planar/scaffold_templates/planar.dev.yaml.j2 +6 -6
  44. planar/scaffold_templates/planar.prod.yaml.j2 +9 -5
  45. planar/scaffold_templates/pyproject.toml.j2 +1 -1
  46. planar/security/auth_context.py +21 -0
  47. planar/security/{jwt_middleware.py → auth_middleware.py} +70 -17
  48. planar/security/authorization.py +9 -15
  49. planar/security/tests/test_auth_middleware.py +162 -0
  50. planar/sse/proxy.py +4 -9
  51. planar/test_app.py +92 -1
  52. planar/test_cli.py +81 -59
  53. planar/test_config.py +17 -14
  54. planar/testing/fixtures.py +325 -0
  55. planar/testing/planar_test_client.py +5 -2
  56. planar/utils.py +41 -1
  57. planar/workflows/execution.py +1 -1
  58. planar/workflows/orchestrator.py +5 -0
  59. planar/workflows/serialization.py +12 -6
  60. planar/workflows/step_core.py +3 -1
  61. planar/workflows/test_serialization.py +9 -1
  62. {planar-0.5.0.dist-info → planar-0.8.0.dist-info}/METADATA +30 -5
  63. planar-0.8.0.dist-info/RECORD +166 -0
  64. planar/.__init__.py.un~ +0 -0
  65. planar/._version.py.un~ +0 -0
  66. planar/.app.py.un~ +0 -0
  67. planar/.cli.py.un~ +0 -0
  68. planar/.config.py.un~ +0 -0
  69. planar/.context.py.un~ +0 -0
  70. planar/.db.py.un~ +0 -0
  71. planar/.di.py.un~ +0 -0
  72. planar/.engine.py.un~ +0 -0
  73. planar/.files.py.un~ +0 -0
  74. planar/.log_context.py.un~ +0 -0
  75. planar/.log_metadata.py.un~ +0 -0
  76. planar/.logging.py.un~ +0 -0
  77. planar/.object_registry.py.un~ +0 -0
  78. planar/.otel.py.un~ +0 -0
  79. planar/.server.py.un~ +0 -0
  80. planar/.session.py.un~ +0 -0
  81. planar/.sqlalchemy.py.un~ +0 -0
  82. planar/.task_local.py.un~ +0 -0
  83. planar/.test_app.py.un~ +0 -0
  84. planar/.test_config.py.un~ +0 -0
  85. planar/.test_object_config.py.un~ +0 -0
  86. planar/.test_sqlalchemy.py.un~ +0 -0
  87. planar/.test_utils.py.un~ +0 -0
  88. planar/.util.py.un~ +0 -0
  89. planar/.utils.py.un~ +0 -0
  90. planar/ai/.__init__.py.un~ +0 -0
  91. planar/ai/._models.py.un~ +0 -0
  92. planar/ai/.agent.py.un~ +0 -0
  93. planar/ai/.agent_utils.py.un~ +0 -0
  94. planar/ai/.events.py.un~ +0 -0
  95. planar/ai/.files.py.un~ +0 -0
  96. planar/ai/.models.py.un~ +0 -0
  97. planar/ai/.providers.py.un~ +0 -0
  98. planar/ai/.pydantic_ai.py.un~ +0 -0
  99. planar/ai/.pydantic_ai_agent.py.un~ +0 -0
  100. planar/ai/.pydantic_ai_provider.py.un~ +0 -0
  101. planar/ai/.step.py.un~ +0 -0
  102. planar/ai/.test_agent.py.un~ +0 -0
  103. planar/ai/.test_agent_serialization.py.un~ +0 -0
  104. planar/ai/.test_providers.py.un~ +0 -0
  105. planar/ai/.utils.py.un~ +0 -0
  106. planar/ai/providers.py +0 -1088
  107. planar/ai/test_agent.py +0 -1298
  108. planar/ai/test_providers.py +0 -463
  109. planar/db/.db.py.un~ +0 -0
  110. planar/files/.config.py.un~ +0 -0
  111. planar/files/.local.py.un~ +0 -0
  112. planar/files/.local_filesystem.py.un~ +0 -0
  113. planar/files/.model.py.un~ +0 -0
  114. planar/files/.models.py.un~ +0 -0
  115. planar/files/.s3.py.un~ +0 -0
  116. planar/files/.storage.py.un~ +0 -0
  117. planar/files/.test_files.py.un~ +0 -0
  118. planar/files/storage/.__init__.py.un~ +0 -0
  119. planar/files/storage/.base.py.un~ +0 -0
  120. planar/files/storage/.config.py.un~ +0 -0
  121. planar/files/storage/.context.py.un~ +0 -0
  122. planar/files/storage/.local_directory.py.un~ +0 -0
  123. planar/files/storage/.test_local_directory.py.un~ +0 -0
  124. planar/files/storage/.test_s3.py.un~ +0 -0
  125. planar/human/.human.py.un~ +0 -0
  126. planar/human/.test_human.py.un~ +0 -0
  127. planar/logging/.__init__.py.un~ +0 -0
  128. planar/logging/.attributes.py.un~ +0 -0
  129. planar/logging/.formatter.py.un~ +0 -0
  130. planar/logging/.logger.py.un~ +0 -0
  131. planar/logging/.otel.py.un~ +0 -0
  132. planar/logging/.tracer.py.un~ +0 -0
  133. planar/modeling/.mixin.py.un~ +0 -0
  134. planar/modeling/.storage.py.un~ +0 -0
  135. planar/modeling/orm/.planar_base_model.py.un~ +0 -0
  136. planar/object_config/.object_config.py.un~ +0 -0
  137. planar/routers/.__init__.py.un~ +0 -0
  138. planar/routers/.agents_router.py.un~ +0 -0
  139. planar/routers/.crud.py.un~ +0 -0
  140. planar/routers/.decision.py.un~ +0 -0
  141. planar/routers/.event.py.un~ +0 -0
  142. planar/routers/.file_attachment.py.un~ +0 -0
  143. planar/routers/.files.py.un~ +0 -0
  144. planar/routers/.files_router.py.un~ +0 -0
  145. planar/routers/.human.py.un~ +0 -0
  146. planar/routers/.info.py.un~ +0 -0
  147. planar/routers/.models.py.un~ +0 -0
  148. planar/routers/.object_config_router.py.un~ +0 -0
  149. planar/routers/.rule.py.un~ +0 -0
  150. planar/routers/.test_object_config_router.py.un~ +0 -0
  151. planar/routers/.test_workflow_router.py.un~ +0 -0
  152. planar/routers/.workflow.py.un~ +0 -0
  153. planar/rules/.decorator.py.un~ +0 -0
  154. planar/rules/.runner.py.un~ +0 -0
  155. planar/rules/.test_rules.py.un~ +0 -0
  156. planar/security/.jwt_middleware.py.un~ +0 -0
  157. planar/sse/.constants.py.un~ +0 -0
  158. planar/sse/.example.html.un~ +0 -0
  159. planar/sse/.hub.py.un~ +0 -0
  160. planar/sse/.model.py.un~ +0 -0
  161. planar/sse/.proxy.py.un~ +0 -0
  162. planar/testing/.client.py.un~ +0 -0
  163. planar/testing/.memory_storage.py.un~ +0 -0
  164. planar/testing/.planar_test_client.py.un~ +0 -0
  165. planar/testing/.predictable_tracer.py.un~ +0 -0
  166. planar/testing/.synchronizable_tracer.py.un~ +0 -0
  167. planar/testing/.test_memory_storage.py.un~ +0 -0
  168. planar/testing/.workflow_observer.py.un~ +0 -0
  169. planar/workflows/.__init__.py.un~ +0 -0
  170. planar/workflows/.builtin_steps.py.un~ +0 -0
  171. planar/workflows/.concurrency_tracing.py.un~ +0 -0
  172. planar/workflows/.context.py.un~ +0 -0
  173. planar/workflows/.contrib.py.un~ +0 -0
  174. planar/workflows/.decorators.py.un~ +0 -0
  175. planar/workflows/.durable_test.py.un~ +0 -0
  176. planar/workflows/.errors.py.un~ +0 -0
  177. planar/workflows/.events.py.un~ +0 -0
  178. planar/workflows/.exceptions.py.un~ +0 -0
  179. planar/workflows/.execution.py.un~ +0 -0
  180. planar/workflows/.human.py.un~ +0 -0
  181. planar/workflows/.lock.py.un~ +0 -0
  182. planar/workflows/.misc.py.un~ +0 -0
  183. planar/workflows/.model.py.un~ +0 -0
  184. planar/workflows/.models.py.un~ +0 -0
  185. planar/workflows/.notifications.py.un~ +0 -0
  186. planar/workflows/.orchestrator.py.un~ +0 -0
  187. planar/workflows/.runtime.py.un~ +0 -0
  188. planar/workflows/.serialization.py.un~ +0 -0
  189. planar/workflows/.step.py.un~ +0 -0
  190. planar/workflows/.step_core.py.un~ +0 -0
  191. planar/workflows/.sub_workflow_runner.py.un~ +0 -0
  192. planar/workflows/.sub_workflow_scheduler.py.un~ +0 -0
  193. planar/workflows/.test_concurrency.py.un~ +0 -0
  194. planar/workflows/.test_concurrency_detection.py.un~ +0 -0
  195. planar/workflows/.test_human.py.un~ +0 -0
  196. planar/workflows/.test_lock_timeout.py.un~ +0 -0
  197. planar/workflows/.test_orchestrator.py.un~ +0 -0
  198. planar/workflows/.test_race_conditions.py.un~ +0 -0
  199. planar/workflows/.test_serialization.py.un~ +0 -0
  200. planar/workflows/.test_suspend_deserialization.py.un~ +0 -0
  201. planar/workflows/.test_workflow.py.un~ +0 -0
  202. planar/workflows/.tracing.py.un~ +0 -0
  203. planar/workflows/.types.py.un~ +0 -0
  204. planar/workflows/.util.py.un~ +0 -0
  205. planar/workflows/.utils.py.un~ +0 -0
  206. planar/workflows/.workflow.py.un~ +0 -0
  207. planar/workflows/.workflow_wrapper.py.un~ +0 -0
  208. planar/workflows/.wrappers.py.un~ +0 -0
  209. planar-0.5.0.dist-info/RECORD +0 -289
  210. {planar-0.5.0.dist-info → planar-0.8.0.dist-info}/WHEEL +0 -0
  211. {planar-0.5.0.dist-info → planar-0.8.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,407 @@
1
+ {
2
+ "nodes": [
3
+ {
4
+ "type": "inputNode",
5
+ "content": {
6
+ "schema": ""
7
+ },
8
+ "id": "ip1",
9
+ "name": "request",
10
+ "position": {
11
+ "x": 110,
12
+ "y": 114
13
+ }
14
+ },
15
+ {
16
+ "type": "decisionTableNode",
17
+ "content": {
18
+ "hitPolicy": "first",
19
+ "rules": [
20
+ {
21
+ "_id": "r1-1",
22
+ "i1-1": "'prepaid_card'",
23
+ "i1-2": "> 2000",
24
+ "o1-1": "40",
25
+ "o1-2": "'High-value prepaid card transaction'"
26
+ },
27
+ {
28
+ "_id": "r1-2",
29
+ "i1-1": "'prepaid_card'",
30
+ "i1-2": "",
31
+ "o1-1": "20",
32
+ "o1-2": "'Prepaid card transaction'"
33
+ },
34
+ {
35
+ "_id": "r1-3",
36
+ "i1-1": "'gift_card'",
37
+ "i1-2": "> 1000",
38
+ "o1-1": "30",
39
+ "o1-2": "'High-value gift card transaction'"
40
+ },
41
+ {
42
+ "_id": "r1-4",
43
+ "i1-1": "'gift_card'",
44
+ "i1-2": "",
45
+ "o1-1": "15",
46
+ "o1-2": "'Gift card transaction'"
47
+ },
48
+ {
49
+ "_id": "r1-5",
50
+ "i1-1": "'cryptocurrency'",
51
+ "i1-2": "",
52
+ "o1-1": "25",
53
+ "o1-2": "'Cryptocurrency transaction'"
54
+ },
55
+ {
56
+ "_id": "r1-6",
57
+ "i1-1": "'credit_card'",
58
+ "i1-2": "> 3000",
59
+ "o1-1": "15",
60
+ "o1-2": "'High-value credit card transaction'"
61
+ },
62
+ {
63
+ "_id": "r1-7",
64
+ "i1-1": "",
65
+ "i1-2": "",
66
+ "o1-1": "0",
67
+ "o1-2": "'Standard transaction'"
68
+ }
69
+ ],
70
+ "inputs": [
71
+ {
72
+ "id": "i1-1",
73
+ "name": "Payment Method",
74
+ "field": "booking.payment_method"
75
+ },
76
+ {
77
+ "id": "i1-2",
78
+ "name": "Transaction Amount",
79
+ "field": "booking.amount"
80
+ }
81
+ ],
82
+ "outputs": [
83
+ {
84
+ "id": "o1-1",
85
+ "name": "Payment Risk Score",
86
+ "field": "risk.payment.score"
87
+ },
88
+ {
89
+ "id": "o1-2",
90
+ "name": "Payment Risk Reason",
91
+ "field": "risk.payment.reason"
92
+ }
93
+ ],
94
+ "passThrough": true,
95
+ "inputField": null,
96
+ "outputPath": null,
97
+ "executionMode": "single",
98
+ "passThorough": false
99
+ },
100
+ "id": "dt1",
101
+ "name": "payment_risk",
102
+ "position": {
103
+ "x": 430,
104
+ "y": 16
105
+ }
106
+ },
107
+ {
108
+ "type": "decisionTableNode",
109
+ "content": {
110
+ "hitPolicy": "first",
111
+ "rules": [
112
+ {
113
+ "_id": "r2-1",
114
+ "i2-1": "> 5",
115
+ "i2-2": "!= booking.ip_country",
116
+ "o2-1": "50",
117
+ "o2-2": "'Multiple bookings from different country'"
118
+ },
119
+ {
120
+ "_id": "r2-2",
121
+ "i2-1": "> 5",
122
+ "i2-2": "",
123
+ "o2-1": "30",
124
+ "o2-2": "'Multiple rapid bookings'"
125
+ },
126
+ {
127
+ "_id": "r2-3",
128
+ "i2-1": "> 3",
129
+ "i2-2": "!= booking.ip_country",
130
+ "o2-1": "35",
131
+ "o2-2": "'Several bookings from different country'"
132
+ },
133
+ {
134
+ "_id": "r2-4",
135
+ "i2-1": "> 3",
136
+ "i2-2": "",
137
+ "o2-1": "20",
138
+ "o2-2": "'Several rapid bookings'"
139
+ },
140
+ {
141
+ "_id": "r2-5",
142
+ "i2-1": "",
143
+ "i2-2": "!= booking.ip_country",
144
+ "o2-1": "25",
145
+ "o2-2": "'Booking from different country'"
146
+ },
147
+ {
148
+ "_id": "r2-6",
149
+ "i2-1": "",
150
+ "i2-2": "",
151
+ "o2-1": "0",
152
+ "o2-2": "'Normal booking pattern'"
153
+ }
154
+ ],
155
+ "inputs": [
156
+ {
157
+ "id": "i2-1",
158
+ "name": "Recent Bookings 24h",
159
+ "field": "account.bookings_last_24h"
160
+ },
161
+ {
162
+ "id": "i2-2",
163
+ "name": "Account Country",
164
+ "field": "account.country"
165
+ }
166
+ ],
167
+ "outputs": [
168
+ {
169
+ "id": "o2-1",
170
+ "name": "Pattern Risk Score",
171
+ "field": "risk.pattern.score"
172
+ },
173
+ {
174
+ "id": "o2-2",
175
+ "name": "Pattern Risk Reason",
176
+ "field": "risk.pattern.reason"
177
+ }
178
+ ],
179
+ "passThrough": true,
180
+ "inputField": null,
181
+ "outputPath": null,
182
+ "executionMode": "single",
183
+ "passThorough": false
184
+ },
185
+ "id": "dt2",
186
+ "name": "pattern_risk",
187
+ "position": {
188
+ "x": 430,
189
+ "y": 114
190
+ }
191
+ },
192
+ {
193
+ "type": "decisionTableNode",
194
+ "content": {
195
+ "hitPolicy": "first",
196
+ "rules": [
197
+ {
198
+ "_id": "r3-1",
199
+ "i3-1": "< 30",
200
+ "o3-1": "40",
201
+ "o3-2": "'New account with significant transaction'"
202
+ },
203
+ {
204
+ "_id": "r3-2",
205
+ "i3-1": "< 90",
206
+ "o3-1": "20",
207
+ "o3-2": "'Relatively new account'"
208
+ },
209
+ {
210
+ "_id": "r3-3",
211
+ "i3-1": "> 1",
212
+ "o3-1": "15",
213
+ "o3-2": "'Previous chargeback history'"
214
+ },
215
+ {
216
+ "_id": "r3-4",
217
+ "i3-1": "",
218
+ "o3-1": "0",
219
+ "o3-2": "'Normal account history'"
220
+ }
221
+ ],
222
+ "inputs": [
223
+ {
224
+ "id": "i3-1",
225
+ "name": "Account Age (days)",
226
+ "field": "account.age_days"
227
+ }
228
+ ],
229
+ "outputs": [
230
+ {
231
+ "id": "o3-1",
232
+ "name": "Account Risk Score",
233
+ "field": "risk.account.score"
234
+ },
235
+ {
236
+ "id": "o3-2",
237
+ "name": "Account Risk Reason",
238
+ "field": "risk.account.reason"
239
+ }
240
+ ],
241
+ "passThrough": true,
242
+ "inputField": null,
243
+ "outputPath": null,
244
+ "executionMode": "single",
245
+ "passThorough": false
246
+ },
247
+ "id": "dt3",
248
+ "name": "account_risk",
249
+ "position": {
250
+ "x": 430,
251
+ "y": 212
252
+ }
253
+ },
254
+ {
255
+ "type": "expressionNode",
256
+ "content": {
257
+ "expressions": [
258
+ {
259
+ "id": "e1-1",
260
+ "key": "total_risk_score",
261
+ "value": "sum(\n map(values(risk), #.score)\n)"
262
+ },
263
+ {
264
+ "id": "e1-5",
265
+ "key": "risk_factors",
266
+ "value": "map(\n filter(values(risk), #.score > 0),\n #.reason\n)"
267
+ }
268
+ ],
269
+ "passThrough": true,
270
+ "inputField": null,
271
+ "outputPath": null,
272
+ "executionMode": "single"
273
+ },
274
+ "id": "ex1",
275
+ "name": "risk_calculation",
276
+ "position": {
277
+ "x": 750,
278
+ "y": 114
279
+ }
280
+ },
281
+ {
282
+ "type": "decisionTableNode",
283
+ "content": {
284
+ "hitPolicy": "first",
285
+ "rules": [
286
+ {
287
+ "_id": "42dc5a6d-6605-40b6-83b4-bad0bcf445c6",
288
+ "7385e66d-dc97-4048-a214-3f35d0029465": ">= 70",
289
+ "35a4cafc-e27c-4419-bfd3-b89a3dcddf46": "\"high\""
290
+ },
291
+ {
292
+ "_id": "c7910082-59fa-4aaf-b67e-5f807db68180",
293
+ "7385e66d-dc97-4048-a214-3f35d0029465": ">= 30",
294
+ "35a4cafc-e27c-4419-bfd3-b89a3dcddf46": "\"medium\""
295
+ },
296
+ {
297
+ "_id": "cea70345-a575-4c72-a81d-b2808f1193ff",
298
+ "7385e66d-dc97-4048-a214-3f35d0029465": "",
299
+ "35a4cafc-e27c-4419-bfd3-b89a3dcddf46": "\"low\""
300
+ }
301
+ ],
302
+ "inputs": [
303
+ {
304
+ "id": "7385e66d-dc97-4048-a214-3f35d0029465",
305
+ "name": "Total risk score",
306
+ "field": "total_risk_score"
307
+ }
308
+ ],
309
+ "outputs": [
310
+ {
311
+ "id": "35a4cafc-e27c-4419-bfd3-b89a3dcddf46",
312
+ "name": "Risk level",
313
+ "field": "risk_level"
314
+ }
315
+ ],
316
+ "passThrough": true,
317
+ "inputField": null,
318
+ "outputPath": null,
319
+ "executionMode": "single",
320
+ "passThorough": false
321
+ },
322
+ "id": "9213dd68-a9f4-4b90-ada9-c7418d8b3c51",
323
+ "name": "risk_level",
324
+ "position": {
325
+ "x": 1070,
326
+ "y": 114
327
+ }
328
+ },
329
+ {
330
+ "type": "expressionNode",
331
+ "content": {
332
+ "expressions": [
333
+ {
334
+ "id": "d58ad60e-a542-4213-919d-66db853847ef",
335
+ "key": "flags.requires_verification",
336
+ "value": "risk_level in [\"medium\", \"high\"]"
337
+ },
338
+ {
339
+ "id": "898fd37d-5370-4e9b-afa7-e04628805b29",
340
+ "key": "flags.manual_review",
341
+ "value": "risk_level == \"high\""
342
+ }
343
+ ],
344
+ "passThrough": false,
345
+ "inputField": null,
346
+ "outputPath": null,
347
+ "executionMode": "single"
348
+ },
349
+ "id": "e6dd7428-9ce6-4d8d-9a62-e38af18da14b",
350
+ "name": "flags",
351
+ "position": {
352
+ "x": 1390,
353
+ "y": 114
354
+ }
355
+ }
356
+ ],
357
+ "edges": [
358
+ {
359
+ "id": "ed1",
360
+ "sourceId": "ip1",
361
+ "targetId": "dt1",
362
+ "type": "edge"
363
+ },
364
+ {
365
+ "id": "ed2",
366
+ "sourceId": "ip1",
367
+ "targetId": "dt2",
368
+ "type": "edge"
369
+ },
370
+ {
371
+ "id": "ed3",
372
+ "sourceId": "ip1",
373
+ "targetId": "dt3",
374
+ "type": "edge"
375
+ },
376
+ {
377
+ "id": "ed4",
378
+ "sourceId": "dt1",
379
+ "targetId": "ex1",
380
+ "type": "edge"
381
+ },
382
+ {
383
+ "id": "ed5",
384
+ "sourceId": "dt2",
385
+ "targetId": "ex1",
386
+ "type": "edge"
387
+ },
388
+ {
389
+ "id": "ed6",
390
+ "sourceId": "dt3",
391
+ "targetId": "ex1",
392
+ "type": "edge"
393
+ },
394
+ {
395
+ "id": "018917a8-1af8-4de1-88e9-f7aa829006fc",
396
+ "sourceId": "ex1",
397
+ "type": "edge",
398
+ "targetId": "9213dd68-a9f4-4b90-ada9-c7418d8b3c51"
399
+ },
400
+ {
401
+ "id": "c627b3e0-ed4f-4701-a10c-668c84a96e70",
402
+ "sourceId": "9213dd68-a9f4-4b90-ada9-c7418d8b3c51",
403
+ "type": "edge",
404
+ "targetId": "e6dd7428-9ce6-4d8d-9a62-e38af18da14b"
405
+ }
406
+ ]
407
+ }
@@ -0,0 +1,258 @@
1
+ {
2
+ "nodes": [
3
+ {
4
+ "type": "inputNode",
5
+ "content": {
6
+ "schema": ""
7
+ },
8
+ "id": "ip1",
9
+ "name": "dataRolloverRequest",
10
+ "position": {
11
+ "x": 110,
12
+ "y": 212.5
13
+ }
14
+ },
15
+ {
16
+ "type": "expressionNode",
17
+ "content": {
18
+ "expressions": [
19
+ {
20
+ "id": "e1-1",
21
+ "key": "unusedData",
22
+ "value": "plan.monthlyDataAllowance - currentBillingCycle.dataUsed"
23
+ },
24
+ {
25
+ "id": "e1-2",
26
+ "key": "isPositiveUnusedData",
27
+ "value": "$.unusedData > 0"
28
+ },
29
+ {
30
+ "id": "e1-3",
31
+ "key": "previousRolloverAmount",
32
+ "value": "currentBillingCycle.rolloverData ?? 0"
33
+ }
34
+ ],
35
+ "passThrough": true,
36
+ "inputField": null,
37
+ "outputPath": null,
38
+ "executionMode": "single"
39
+ },
40
+ "id": "ex1",
41
+ "name": "calculateUnusedData",
42
+ "position": {
43
+ "x": 430,
44
+ "y": 212.5
45
+ }
46
+ },
47
+ {
48
+ "type": "decisionTableNode",
49
+ "content": {
50
+ "hitPolicy": "first",
51
+ "rules": [
52
+ {
53
+ "_id": "r1-1",
54
+ "i1-1": "'premium'",
55
+ "i1-2": "true",
56
+ "i1-3": "< 3",
57
+ "o1-1": "unusedData + previousRolloverAmount",
58
+ "o1-2": "'Unlimited rollover for Premium plan'"
59
+ },
60
+ {
61
+ "_id": "r1-2",
62
+ "i1-1": "'standard'",
63
+ "i1-2": "true",
64
+ "i1-3": "< 3",
65
+ "o1-1": "min([unusedData + previousRolloverAmount, plan.monthlyDataAllowance * 0.5])",
66
+ "o1-2": "'Standard plan can rollover up to 50% of monthly allowance'"
67
+ },
68
+ {
69
+ "_id": "r1-3",
70
+ "i1-1": "'basic'",
71
+ "i1-2": "true",
72
+ "i1-3": "< 3",
73
+ "o1-1": "min([unusedData, plan.monthlyDataAllowance * 0.25])",
74
+ "o1-2": "'Basic plan can rollover up to 25% of monthly allowance'"
75
+ },
76
+ {
77
+ "_id": "r1-4",
78
+ "i1-1": "",
79
+ "i1-2": "true",
80
+ "i1-3": ">= 3",
81
+ "o1-1": "0",
82
+ "o1-2": "'Maximum consecutive rollovers (3) reached'"
83
+ },
84
+ {
85
+ "_id": "r1-5",
86
+ "i1-1": "",
87
+ "i1-2": "false",
88
+ "i1-3": "",
89
+ "o1-1": "0",
90
+ "o1-2": "'No unused data to rollover'"
91
+ }
92
+ ],
93
+ "inputs": [
94
+ {
95
+ "id": "i1-1",
96
+ "name": "Plan Type",
97
+ "field": "plan.type"
98
+ },
99
+ {
100
+ "id": "i1-2",
101
+ "name": "Has Unused Data",
102
+ "field": "isPositiveUnusedData"
103
+ },
104
+ {
105
+ "id": "i1-3",
106
+ "name": "Consecutive Rollovers",
107
+ "field": "currentBillingCycle.consecutiveRollovers"
108
+ }
109
+ ],
110
+ "outputs": [
111
+ {
112
+ "id": "o1-1",
113
+ "name": "Rollover Amount",
114
+ "field": "nextBillingCycle.rolloverData"
115
+ },
116
+ {
117
+ "id": "o1-2",
118
+ "name": "Reason",
119
+ "field": "nextBillingCycle.rolloverReason"
120
+ }
121
+ ],
122
+ "passThrough": true,
123
+ "inputField": null,
124
+ "outputPath": null,
125
+ "executionMode": "single",
126
+ "passThorough": false
127
+ },
128
+ "id": "dt1",
129
+ "name": "determineRolloverAmount",
130
+ "position": {
131
+ "x": 750,
132
+ "y": 212.5
133
+ }
134
+ },
135
+ {
136
+ "type": "switchNode",
137
+ "content": {
138
+ "hitPolicy": "first",
139
+ "statements": [
140
+ {
141
+ "id": "s1-1",
142
+ "condition": "nextBillingCycle.rolloverData > 0",
143
+ "isDefault": false
144
+ },
145
+ {
146
+ "id": "s1-2",
147
+ "condition": "",
148
+ "isDefault": true
149
+ }
150
+ ]
151
+ },
152
+ "id": "sw1",
153
+ "name": "verifyRollover",
154
+ "position": {
155
+ "x": 1070,
156
+ "y": 212.5
157
+ }
158
+ },
159
+ {
160
+ "type": "expressionNode",
161
+ "content": {
162
+ "expressions": [
163
+ {
164
+ "id": "e2-1",
165
+ "key": "responseMessage",
166
+ "value": "`Rollover successful. You have ${nextBillingCycle.rolloverData} GB of rollover data available for your next billing cycle.`"
167
+ },
168
+ {
169
+ "id": "e2-2",
170
+ "key": "nextBillingCycle.consecutiveRollovers",
171
+ "value": "currentBillingCycle.consecutiveRollovers + 1"
172
+ },
173
+ {
174
+ "id": "e2-3",
175
+ "key": "nextBillingCycle.status",
176
+ "value": "'approved'"
177
+ }
178
+ ],
179
+ "passThrough": false,
180
+ "inputField": null,
181
+ "outputPath": null,
182
+ "executionMode": "single"
183
+ },
184
+ "id": "ex2",
185
+ "name": "processSuccessfulRollover",
186
+ "position": {
187
+ "x": 1390,
188
+ "y": 163.5
189
+ }
190
+ },
191
+ {
192
+ "type": "expressionNode",
193
+ "content": {
194
+ "expressions": [
195
+ {
196
+ "id": "e3-1",
197
+ "key": "responseMessage",
198
+ "value": "`No data rolled over. Reason: ${nextBillingCycle.rolloverReason ?? 'No unused data available'}`"
199
+ },
200
+ {
201
+ "id": "e3-2",
202
+ "key": "nextBillingCycle.consecutiveRollovers",
203
+ "value": "0"
204
+ },
205
+ {
206
+ "id": "e3-3",
207
+ "key": "nextBillingCycle.status",
208
+ "value": "'no_rollover'"
209
+ }
210
+ ],
211
+ "passThrough": false,
212
+ "inputField": null,
213
+ "outputPath": null,
214
+ "executionMode": "single"
215
+ },
216
+ "id": "ex3",
217
+ "name": "processNoRollover",
218
+ "position": {
219
+ "x": 1390,
220
+ "y": 261.5
221
+ }
222
+ }
223
+ ],
224
+ "edges": [
225
+ {
226
+ "id": "ed1",
227
+ "sourceId": "ip1",
228
+ "targetId": "ex1",
229
+ "type": "edge"
230
+ },
231
+ {
232
+ "id": "ed2",
233
+ "sourceId": "ex1",
234
+ "targetId": "dt1",
235
+ "type": "edge"
236
+ },
237
+ {
238
+ "id": "ed3",
239
+ "sourceId": "dt1",
240
+ "targetId": "sw1",
241
+ "type": "edge"
242
+ },
243
+ {
244
+ "id": "ed4",
245
+ "sourceId": "sw1",
246
+ "targetId": "ex2",
247
+ "sourceHandle": "s1-1",
248
+ "type": "edge"
249
+ },
250
+ {
251
+ "id": "ed5",
252
+ "sourceId": "sw1",
253
+ "targetId": "ex3",
254
+ "sourceHandle": "s1-2",
255
+ "type": "edge"
256
+ }
257
+ ]
258
+ }