ApiLogicServer 15.2.3__py3-none-any.whl → 15.2.10__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.
- api_logic_server_cli/api_logic_server.py +3 -1
- api_logic_server_cli/prototypes/base/.github/.copilot-instructions.md +114 -52
- api_logic_server_cli/prototypes/base/docs/training/testing.md +95 -9
- api_logic_server_cli/prototypes/base/test/api_logic_server_behave/behave_logic_report.py +19 -6
- api_logic_server_cli/prototypes/basic_demo/.github/.copilot-instructions.md +744 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/logic/declare_logic.py +17 -1
- api_logic_server_cli/prototypes/basic_demo/readme.md +13 -5
- api_logic_server_cli/prototypes/basic_demo/tutor.md +1436 -0
- api_logic_server_cli/prototypes/manager/.github/.copilot-instructions.md +50 -23
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/.github/.copilot-instructions.md +3 -0
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/customizations/logic/declare_logic.py +17 -1
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/docs/training/testing.md +95 -9
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/iteration/logic/declare_logic.py +17 -1
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/logic/declare_logic.py +38 -1
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/features/order_processing.feature +59 -50
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/features/steps/order_processing_steps.py +395 -248
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/behave.log +66 -62
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Carbon_Neutral_Discount_A.log +51 -41
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Change_Order_Customer.log +29 -0
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Change_Product_in_Item.log +35 -0
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Delete_Item_Reduces_Order.log +39 -19
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Exceed_Credit_Limit_Rejec.log +36 -45
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Good_Order_Placed_via_B2B.log +50 -40
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Item_Quantity_Change.log +33 -0
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Multi-Item_Order_via_B2B_.log +67 -0
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Ship_Order_Excludes_from_.log +24 -14
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Transaction_Processing.log +26 -17
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/logs/scenario_logic_logs/Unship_Order_Includes_in_.log +24 -14
- api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/test/api_logic_server_behave/reports/Behave Logic Report.md +361 -146
- api_logic_server_cli/prototypes/manager/system/ApiLogicServer-Internal-Dev/copilot-dev-context.md +270 -2
- {apilogicserver-15.2.3.dist-info → apilogicserver-15.2.10.dist-info}/METADATA +25 -16
- {apilogicserver-15.2.3.dist-info → apilogicserver-15.2.10.dist-info}/RECORD +36 -30
- {apilogicserver-15.2.3.dist-info → apilogicserver-15.2.10.dist-info}/WHEEL +0 -0
- {apilogicserver-15.2.3.dist-info → apilogicserver-15.2.10.dist-info}/entry_points.txt +0 -0
- {apilogicserver-15.2.3.dist-info → apilogicserver-15.2.10.dist-info}/licenses/LICENSE +0 -0
- {apilogicserver-15.2.3.dist-info → apilogicserver-15.2.10.dist-info}/top_level.txt +0 -0
|
@@ -1,20 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Copilot Instructions for Manager Workspace
|
|
3
|
+
Description: Manager-level instructions for creating projects
|
|
4
|
+
Source: ApiLogicServer-src/prototypes/manager/.github/.copilot-instructions.md
|
|
5
|
+
Propagation: BLT process → Manager workspace
|
|
6
|
+
Usage: AI assistants read this when user opens Manager workspace
|
|
7
|
+
version: 2.1
|
|
8
|
+
changelog:
|
|
9
|
+
- 2.1 (Oct 2025) - Added "What is the Project Manager?" orientation, friendly collaborative tone, conditional Quick Start for returning users
|
|
10
|
+
- 2.0 (Oct 2025) - OBX improvements, strengthen basic_demo as default path
|
|
11
|
+
- 1.0 (Initial) - Established project creation methods
|
|
12
|
+
---
|
|
13
|
+
|
|
1
14
|
# GitHub Copilot Instructions for GenAI-Logic (aka API Logic Server) - Project Manager
|
|
2
15
|
|
|
3
|
-
⚠️ **IMPORTANT FOR AI ASSISTANTS**: Always read and follow these instructions when helping
|
|
16
|
+
⚠️ **IMPORTANT FOR AI ASSISTANTS**: Always read and follow these instructions when helping with this project.
|
|
17
|
+
|
|
18
|
+
## What is the Project Manager?
|
|
19
|
+
|
|
20
|
+
Welcome! This is the **Project Manager** - think of it as your workspace for creating and managing API Logic Server projects.
|
|
21
|
+
|
|
22
|
+
**What's here:**
|
|
23
|
+
- Sample databases you can use to create projects (`samples/dbs/`)
|
|
24
|
+
- Training materials and examples (`system/genai/examples/`)
|
|
25
|
+
- A shared virtual environment for all your projects (`venv/`)
|
|
26
|
+
- A place to create new projects (they'll appear as subdirectories)
|
|
4
27
|
|
|
5
|
-
|
|
28
|
+
**What you'll do here:**
|
|
29
|
+
Create projects from databases - each project becomes a complete, working microservice with API, Admin UI, and business logic support. Once created, you'll work inside that project to customize and extend it.
|
|
6
30
|
|
|
7
|
-
> Note:
|
|
31
|
+
> Note: Your virtual environment is automatically configured in most cases; verify it with `genai-logic` - if it's not there, activate the `venv` in this project.
|
|
8
32
|
|
|
9
|
-
## Quick Start
|
|
33
|
+
## Quick Start
|
|
10
34
|
|
|
11
|
-
**
|
|
35
|
+
**First time here?** Let's start with the 20-minute basic_demo tutorial - it's the best way to see what GenAI-Logic can do.
|
|
12
36
|
|
|
13
|
-
|
|
37
|
+
Run this command:
|
|
14
38
|
|
|
15
39
|
```bash
|
|
16
40
|
genai-logic create --project_name=basic_demo --db_url=basic_demo
|
|
17
41
|
```
|
|
42
|
+
|
|
43
|
+
After it runs, the project will auto-open with its own README and I'll guide you through a hands-on tour.
|
|
44
|
+
|
|
45
|
+
**Already have basic_demo?** Open it and say "Guide me through basic_demo" for the hands-on tour, or ask me what else we can work on.
|
|
46
|
+
|
|
18
47
|
<br>
|
|
19
48
|
|
|
20
49
|
## Creating Projects
|
|
@@ -27,7 +56,7 @@ There are multiple ways to create projects (aka systems, microservices) - see th
|
|
|
27
56
|
|
|
28
57
|
### Method 1: Create Projects from an existing database (Infrastructure Only)
|
|
29
58
|
|
|
30
|
-
If
|
|
59
|
+
If you have a database reference, I can create a project from it. (Sample databases are in `samples/dbs`).
|
|
31
60
|
|
|
32
61
|
```bash
|
|
33
62
|
genai-logic create --project_name=nw --db_url=sqlite:///samples/dbs/nw.sqlite
|
|
@@ -35,13 +64,11 @@ genai-logic create --project_name=nw --db_url=sqlite:///samples/dbs/nw.sqlite
|
|
|
35
64
|
|
|
36
65
|
**Important:** This creates the project structure, API, Admin App, and database models, but **NO business logic rules**.
|
|
37
66
|
|
|
38
|
-
**To add logic:**
|
|
39
|
-
- Natural language with Copilot/AI assistant in `logic/declare_logic.py`, or
|
|
40
|
-
- Manual coding with IDE code completion
|
|
67
|
+
**To add logic:** We can add rules together using natural language in `logic/declare_logic.py`, or you can code them manually with IDE autocomplete.
|
|
41
68
|
|
|
42
69
|
### Method 2: Create Projects with GenAI (Complete System with Logic)
|
|
43
70
|
|
|
44
|
-
**Use this when
|
|
71
|
+
**Use this when you have both database AND business logic requirements.**
|
|
45
72
|
|
|
46
73
|
Describe database and logic in Natural Language in a prompt file.
|
|
47
74
|
|
|
@@ -58,25 +85,25 @@ genai-logic genai --using=system/genai/examples/genai_demo/genai_demo.prompt --p
|
|
|
58
85
|
4. Creates test data
|
|
59
86
|
5. Creates API, Admin App, and complete project structure
|
|
60
87
|
|
|
61
|
-
**When to use:**
|
|
88
|
+
**When to use:** When you have requirements like "Customer balance is sum of orders" or "Check credit limit" - the LLM will generate the corresponding `Rule.sum`, `Rule.constraint`, etc.
|
|
62
89
|
|
|
63
90
|
### Method 3: Create Projects with new databases (Manual Approach)
|
|
64
91
|
|
|
65
|
-
If
|
|
92
|
+
If you provide a description but want to create the database manually:
|
|
66
93
|
|
|
67
|
-
1.
|
|
68
|
-
2. Then, use the `genai-logic create` command above.
|
|
69
|
-
3. If logic
|
|
94
|
+
1. I'll create a sqlite database from your description. I'll be sure to include foreign keys. The system works well with `id INTEGER PRIMARY KEY AUTOINCREMENT`.
|
|
95
|
+
2. Then, I'll use the `genai-logic create` command above.
|
|
96
|
+
3. If you have logic requirements, we can translate them together using `system/genai/learning_requests/logic_bank_api.prompt`.
|
|
70
97
|
|
|
71
|
-
##
|
|
98
|
+
## Working Together
|
|
72
99
|
|
|
73
|
-
When
|
|
100
|
+
When you ask "what should I do now?" or similar:
|
|
74
101
|
|
|
75
|
-
1. **
|
|
76
|
-
2. **
|
|
77
|
-
3. **
|
|
78
|
-
4. **
|
|
79
|
-
5. **
|
|
102
|
+
1. **I'll recommend starting with basic_demo** using the command above
|
|
103
|
+
2. **I'll use CLI commands** (genai-logic) not Docker scripts for project creation
|
|
104
|
+
3. **I'll point you to sample databases** in `samples/dbs/` for examples
|
|
105
|
+
4. **I'll show you GenAI examples** in `system/genai/examples/` for AI-generated projects
|
|
106
|
+
5. **We'll follow the 20-minute workflow** described in the main README
|
|
80
107
|
|
|
81
108
|
## Available Databases
|
|
82
109
|
- `basic_demo` - Best for first-time users
|
api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/.github/.copilot-instructions.md
CHANGED
|
@@ -26,6 +26,9 @@ This is a **GenAI-Logic (aka API Logic Server) project** - a complete, working m
|
|
|
26
26
|
|
|
27
27
|
3. **Automatic Business Logic**: All APIs (standard and custom) automatically inherit LogicBank rules without additional code.
|
|
28
28
|
|
|
29
|
+
4. **CLI Commands**: Use `genai-logic --help` to see all available commands. When CLI commands exist for a task (e.g., `add-auth`, `genai-add-mcp-client`, `genai-add-app`), ALWAYS use them instead of manual configuration - they handle all setup correctly.
|
|
30
|
+
|
|
31
|
+
|
|
29
32
|
## ⚠️ IMPORTANT: What's Already Built
|
|
30
33
|
|
|
31
34
|
**DO NOT recreate these - they're already working:**
|
|
@@ -32,7 +32,23 @@ def declare_logic():
|
|
|
32
32
|
from logic.logic_discovery.auto_discovery import discover_logic
|
|
33
33
|
discover_logic()
|
|
34
34
|
|
|
35
|
-
# Logic from GenAI
|
|
35
|
+
# Logic from GenAI
|
|
36
|
+
'''
|
|
37
|
+
You can enter logic in 2 ways:
|
|
38
|
+
1. Using your IDE and code completion (Rule.)
|
|
39
|
+
|
|
40
|
+
2. Use your AI Assistant and enter logic in Natural Language, e.g.:
|
|
41
|
+
Create Business Logic for Use Case = Check Credit:
|
|
42
|
+
1. The Customer's balance is less than the credit limit
|
|
43
|
+
2. The Customer's balance is the sum of the Order amount_total where date_shipped is null
|
|
44
|
+
3. The Order's amount_total is the sum of the Item amount
|
|
45
|
+
4. The Item amount is the quantity * unit_price
|
|
46
|
+
5. The Item unit_price is copied from the Product unit_price
|
|
47
|
+
|
|
48
|
+
Use case: App Integration
|
|
49
|
+
1. Send the Order to Kafka topic 'order_shipping' if the date_shipped is not None.
|
|
50
|
+
Also, using logic/logic_discovery is a Best Practice - see logic/readme_logic.md
|
|
51
|
+
'''
|
|
36
52
|
from database.models import Product, Order, Item, Customer, SysEmail
|
|
37
53
|
|
|
38
54
|
# Ensure the customer's balance is less than their credit limit
|
|
@@ -32,6 +32,9 @@ Step 5. SUGGEST how to run tests (DO NOT run automatically)
|
|
|
32
32
|
- [ ] @when patterns: carbon neutral > multi-item > single-item
|
|
33
33
|
- [ ] @given patterns: multi-item > single-item
|
|
34
34
|
- [ ] Use `grep -n "@when('.*with" steps/*.py` to verify order
|
|
35
|
+
- [ ] **Clear scenario language?** (Rule #13: Action-oriented wording)
|
|
36
|
+
- [ ] Use "Order is created" not "Order exists" (shows when rules fire)
|
|
37
|
+
- [ ] Makes test flow obvious: setup → action → verify
|
|
35
38
|
- [ ] Test data uses timestamps? (Rule #0: Repeatability)
|
|
36
39
|
- [ ] Security config read? (SECURITY_ENABLED value)
|
|
37
40
|
|
|
@@ -167,7 +170,7 @@ def step_impl(context, qty):
|
|
|
167
170
|
r = requests.patch(url=patch_uri, json=patch_data)
|
|
168
171
|
```
|
|
169
172
|
|
|
170
|
-
## The
|
|
173
|
+
## The Critical Rules (Read FIRST!)
|
|
171
174
|
|
|
172
175
|
### Rule #0: TEST REPEATABILITY (MOST CRITICAL!) ⚠️
|
|
173
176
|
|
|
@@ -386,18 +389,65 @@ Scenario: Exceed Credit (Constraint FAIL - negative test)
|
|
|
386
389
|
|
|
387
390
|
**CRITICAL:** Check `logic/declare_logic.py` for custom Python logic affecting calculations (e.g., discounts, adjustments) before writing constraint test expectations!
|
|
388
391
|
|
|
389
|
-
### Rule #5: Security Configuration
|
|
392
|
+
### Rule #5: Security Configuration - JWT Authentication
|
|
393
|
+
|
|
394
|
+
**CRITICAL:** When `SECURITY_ENABLED=True`, tests MUST obtain JWT token and include Authorization header.
|
|
395
|
+
|
|
390
396
|
```python
|
|
391
|
-
|
|
392
|
-
|
|
397
|
+
from pathlib import Path
|
|
398
|
+
import os
|
|
399
|
+
from dotenv import load_dotenv
|
|
393
400
|
|
|
394
|
-
#
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
401
|
+
# Load config to check SECURITY_ENABLED
|
|
402
|
+
config_path = Path(__file__).parent.parent.parent.parent.parent / 'config' / 'default.env'
|
|
403
|
+
load_dotenv(config_path)
|
|
404
|
+
|
|
405
|
+
# Cache for auth token (obtained once per test session)
|
|
406
|
+
_auth_token = None
|
|
407
|
+
|
|
408
|
+
def get_auth_token():
|
|
409
|
+
"""Login and get JWT token if security is enabled"""
|
|
410
|
+
global _auth_token
|
|
411
|
+
|
|
412
|
+
if _auth_token is not None:
|
|
413
|
+
return _auth_token
|
|
414
|
+
|
|
415
|
+
# Login with default admin credentials
|
|
416
|
+
login_url = f'{BASE_URL}/api/auth/login'
|
|
417
|
+
login_data = {'username': 'admin', 'password': 'p'}
|
|
418
|
+
|
|
419
|
+
response = requests.post(login_url, json=login_data)
|
|
420
|
+
if response.status_code == 200:
|
|
421
|
+
_auth_token = response.json().get('access_token')
|
|
422
|
+
return _auth_token
|
|
423
|
+
else:
|
|
424
|
+
raise Exception(f"Login failed: {response.status_code}")
|
|
425
|
+
|
|
426
|
+
def get_headers():
|
|
427
|
+
"""Get headers including auth token if security is enabled"""
|
|
428
|
+
security_enabled = os.getenv('SECURITY_ENABLED', 'false').lower() not in ['false', 'no']
|
|
429
|
+
|
|
430
|
+
headers = {'Content-Type': 'application/json'}
|
|
431
|
+
|
|
432
|
+
if security_enabled:
|
|
433
|
+
token = get_auth_token()
|
|
434
|
+
if token:
|
|
435
|
+
headers['Authorization'] = f'Bearer {token}'
|
|
436
|
+
|
|
437
|
+
return headers
|
|
438
|
+
|
|
439
|
+
# Use in ALL API requests
|
|
440
|
+
response = requests.post(url=api_url, json=data, headers=get_headers())
|
|
441
|
+
response = requests.get(url=api_url, headers=get_headers())
|
|
442
|
+
response = requests.patch(url=api_url, json=data, headers=get_headers())
|
|
399
443
|
```
|
|
400
444
|
|
|
445
|
+
**Key Points:**
|
|
446
|
+
- Tests DO NOT automatically include auth - you must code the pattern above
|
|
447
|
+
- Token is cached globally to avoid repeated login calls
|
|
448
|
+
- Works for both `SECURITY_ENABLED=True` and `SECURITY_ENABLED=False`
|
|
449
|
+
- See complete example: `test/api_logic_server_behave/features/steps/order_processing_steps.py`
|
|
450
|
+
|
|
401
451
|
### Rule #6: Logic Logging
|
|
402
452
|
```python
|
|
403
453
|
@when('Good Order Placed')
|
|
@@ -535,6 +585,40 @@ def step_impl(context):
|
|
|
535
585
|
return # Skip gracefully
|
|
536
586
|
```
|
|
537
587
|
|
|
588
|
+
### Rule #13: Use Clear, Action-Oriented Scenario Language ⚠️ NEW
|
|
589
|
+
```gherkin
|
|
590
|
+
# ❌ AMBIGUOUS - "exists" is passive, doesn't show when balance changes
|
|
591
|
+
Scenario: Ship Order Excludes from Balance
|
|
592
|
+
Given Customer "Charlie" with balance 0 and credit limit 2000
|
|
593
|
+
And Order exists for "Charlie" with 2 Widget
|
|
594
|
+
When Order is shipped
|
|
595
|
+
Then Customer balance should be 0
|
|
596
|
+
|
|
597
|
+
# Question: How can Charlie have balance 0 AND an order for widgets?
|
|
598
|
+
# Answer: The order EXISTS at test start, but balance calculation is unclear
|
|
599
|
+
|
|
600
|
+
# ✅ CLEAR - "is created" shows action that triggers rule
|
|
601
|
+
Scenario: Ship Order Excludes from Balance
|
|
602
|
+
Given Customer "Charlie" with balance 0 and credit limit 2000
|
|
603
|
+
And Order is created for "Charlie" with 2 Widget # Action! Balance becomes 180
|
|
604
|
+
When Order is shipped # Action! Balance drops to 0
|
|
605
|
+
Then Customer balance should be 0 # Verification
|
|
606
|
+
|
|
607
|
+
# Now it's obvious:
|
|
608
|
+
# 1. Customer starts with balance 0
|
|
609
|
+
# 2. Creating unshipped order → rule fires → balance becomes 180
|
|
610
|
+
# 3. Shipping order → WHERE clause excludes it → balance drops to 0
|
|
611
|
+
|
|
612
|
+
# More examples:
|
|
613
|
+
# ❌ "And Shipped order exists for..."
|
|
614
|
+
# ✅ "And Shipped order is created for..."
|
|
615
|
+
|
|
616
|
+
# Why this matters:
|
|
617
|
+
# - Shows WHEN rules fire (on creation, not just existence)
|
|
618
|
+
# - Makes test flow obvious (setup → action → verify)
|
|
619
|
+
# - Clarifies that declarative rules execute automatically on changes
|
|
620
|
+
```
|
|
621
|
+
|
|
538
622
|
## Complete Phase 2 Example
|
|
539
623
|
|
|
540
624
|
### .feature File
|
|
@@ -631,6 +715,8 @@ def step_impl(context, expected):
|
|
|
631
715
|
| **"balance: expected 570, got 0.0"** | **Step ordering issue (Rule #0.5)! Multi-item pattern after single-item** |
|
|
632
716
|
| **"context has no attribute 'item_id'"** | **Step ordering issue! Specific pattern defined after general pattern** |
|
|
633
717
|
| "Order created but no items" | Check if wrong step matched (print debug in step) |
|
|
718
|
+
| **"expected 810, got 900"** (carbon neutral) | **Rule #10: Verify actual database values! Check product flags with SQL first** |
|
|
719
|
+
| **"How can balance be 0 with an order?"** | **Rule #13: Use "is created" not "exists" - shows when rules fire** |
|
|
634
720
|
|
|
635
721
|
### Debugging Step Ordering Issues
|
|
636
722
|
|
api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/iteration/logic/declare_logic.py
CHANGED
|
@@ -32,7 +32,23 @@ def declare_logic():
|
|
|
32
32
|
from logic.logic_discovery.auto_discovery import discover_logic
|
|
33
33
|
discover_logic()
|
|
34
34
|
|
|
35
|
-
# Logic from GenAI
|
|
35
|
+
# Logic from GenAI
|
|
36
|
+
'''
|
|
37
|
+
You can enter logic in 2 ways:
|
|
38
|
+
1. Using your IDE and code completion (Rule.)
|
|
39
|
+
|
|
40
|
+
2. Use your AI Assistant and enter logic in Natural Language, e.g.:
|
|
41
|
+
Create Business Logic for Use Case = Check Credit:
|
|
42
|
+
1. The Customer's balance is less than the credit limit
|
|
43
|
+
2. The Customer's balance is the sum of the Order amount_total where date_shipped is null
|
|
44
|
+
3. The Order's amount_total is the sum of the Item amount
|
|
45
|
+
4. The Item amount is the quantity * unit_price
|
|
46
|
+
5. The Item unit_price is copied from the Product unit_price
|
|
47
|
+
|
|
48
|
+
Use case: App Integration
|
|
49
|
+
1. Send the Order to Kafka topic 'order_shipping' if the date_shipped is not None.
|
|
50
|
+
Also, using logic/logic_discovery is a Best Practice - see logic/readme_logic.md
|
|
51
|
+
'''
|
|
36
52
|
from database.models import Product, Order, Item, Customer
|
|
37
53
|
|
|
38
54
|
# Ensure the customer's balance is less than their credit limit
|
|
@@ -31,6 +31,43 @@ def declare_logic():
|
|
|
31
31
|
from logic.logic_discovery.auto_discovery import discover_logic
|
|
32
32
|
discover_logic()
|
|
33
33
|
|
|
34
|
+
# Logic from GenAI: (or, use your IDE w/ code completion)
|
|
35
|
+
from database.models import Product, Order, Item, Customer
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# ************ Python Customization Example ****************
|
|
40
|
+
|
|
41
|
+
# Send order details to Kafka if order is shipped.
|
|
42
|
+
# Formeraly, we had this rule:
|
|
43
|
+
# Rule.after_flush_row_event(on_class=Order, calling=kafka_producer.send_row_to_kafka, if_condition=lambda row: row.date_shipped is not None, with_args={'topic': 'order_shipping'})
|
|
44
|
+
# If the integration were more complex, we could use Python, like this:
|
|
45
|
+
#als: Demonstrate that logic == Rules + Python (for extensibility)
|
|
46
|
+
|
|
47
|
+
def send_order_to_shipping(row: models.Order, old_row: models.Order, logic_row: LogicRow):
|
|
48
|
+
""" #als: Send Kafka message formatted by OrderShipping RowDictMapper
|
|
49
|
+
|
|
50
|
+
Format row per shipping requirements, and send (e.g., a message)
|
|
51
|
+
|
|
52
|
+
NB: the after_flush event makes Order.Id avaible.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
row (models.Order): inserted Order
|
|
56
|
+
old_row (models.Order): n/a
|
|
57
|
+
logic_row (LogicRow): bundles curr/old row, with ins/upd/dlt logic
|
|
58
|
+
"""
|
|
59
|
+
if logic_row.is_inserted():
|
|
60
|
+
kafka_producer.send_kafka_message(logic_row=logic_row,
|
|
61
|
+
row_dict_mapper=OrderShipping,
|
|
62
|
+
kafka_topic="order_shipping",
|
|
63
|
+
kafka_key=str(row.id),
|
|
64
|
+
msg="Sending Order to Shipping")
|
|
65
|
+
|
|
66
|
+
Rule.after_flush_row_event(on_class=models.Order, calling=send_order_to_shipping) # see above
|
|
67
|
+
|
|
68
|
+
# End Logic from GenAI
|
|
69
|
+
|
|
70
|
+
|
|
34
71
|
def handle_all(logic_row: LogicRow): # #als: TIME / DATE STAMPING, OPTIMISTIC LOCKING
|
|
35
72
|
"""
|
|
36
73
|
This is generic - executed for all classes.
|
|
@@ -53,7 +90,7 @@ def declare_logic():
|
|
|
53
90
|
Grant.process_updates(logic_row=logic_row)
|
|
54
91
|
|
|
55
92
|
did_stamping = False
|
|
56
|
-
if enable_stamping :=
|
|
93
|
+
if enable_stamping := True: # #als: DATE / USER STAMPING
|
|
57
94
|
row = logic_row.row
|
|
58
95
|
if logic_row.ins_upd_dlt == "ins" and hasattr(row, "CreatedOn"):
|
|
59
96
|
row.CreatedOn = datetime.datetime.now()
|
|
@@ -1,72 +1,81 @@
|
|
|
1
1
|
Feature: Order Processing with Business Logic
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# Tests all declarative rules:
|
|
4
|
+
# 1. Customer.balance = sum(Order.amount_total) WHERE date_shipped is None
|
|
5
|
+
# 2. Order.amount_total = sum(Item.amount)
|
|
6
|
+
# 3. Item.amount = quantity * unit_price (with 10% carbon neutral discount for qty >= 10)
|
|
7
|
+
# 4. Item.unit_price copied from Product.unit_price
|
|
8
|
+
# 5. Customer.balance <= credit_limit constraint
|
|
9
|
+
# 6. Kafka integration when order shipped
|
|
5
10
|
|
|
6
11
|
Scenario: Good Order Placed via B2B API
|
|
7
|
-
Given Customer "Alice" with balance 0 and credit limit
|
|
12
|
+
Given Customer "Alice" with balance 0 and credit limit 5000
|
|
8
13
|
When B2B order placed for "Alice" with 5 Widget
|
|
9
14
|
Then Customer balance should be 450
|
|
10
15
|
And Order amount_total should be 450
|
|
11
|
-
And
|
|
16
|
+
And Item amount should be 450
|
|
17
|
+
And Item unit_price should be 90
|
|
12
18
|
|
|
13
|
-
Scenario: Item
|
|
14
|
-
Given Customer "Bob" with balance 0 and credit limit
|
|
15
|
-
|
|
19
|
+
Scenario: Multi-Item Order via B2B API
|
|
20
|
+
Given Customer "Bob" with balance 0 and credit limit 3000
|
|
21
|
+
When B2B order placed for "Bob" with 3 Widget and 2 Gadget
|
|
22
|
+
Then Customer balance should be 570
|
|
23
|
+
And Order amount_total should be 570
|
|
24
|
+
|
|
25
|
+
Scenario: Carbon Neutral Discount Applied
|
|
26
|
+
Given Customer "Diana" with balance 0 and credit limit 5000
|
|
27
|
+
When B2B order placed for "Diana" with 10 carbon neutral Gadget
|
|
28
|
+
Then Customer balance should be 1350
|
|
29
|
+
And Item amount should be 1350
|
|
30
|
+
|
|
31
|
+
Scenario: Item Quantity Change
|
|
32
|
+
Given Customer "Charlie" with balance 0 and credit limit 2000
|
|
33
|
+
And Order is created for "Charlie" with 5 Widget
|
|
16
34
|
When Item quantity changed to 10
|
|
17
35
|
Then Item amount should be 900
|
|
18
36
|
And Order amount_total should be 900
|
|
19
37
|
And Customer balance should be 900
|
|
20
38
|
|
|
21
|
-
Scenario: Change
|
|
22
|
-
Given Customer "
|
|
23
|
-
And
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
And
|
|
39
|
+
Scenario: Change Product in Item
|
|
40
|
+
Given Customer "Alice" with balance 0 and credit limit 5000
|
|
41
|
+
And Order is created for "Alice" with 5 Widget
|
|
42
|
+
When Item product changed to "Gadget"
|
|
43
|
+
Then Item unit_price should be 150
|
|
44
|
+
And Item amount should be 750
|
|
45
|
+
And Order amount_total should be 750
|
|
46
|
+
And Customer balance should be 750
|
|
28
47
|
|
|
29
|
-
Scenario: Delete Item Reduces Order
|
|
30
|
-
Given Customer "
|
|
31
|
-
And Order
|
|
32
|
-
When First item deleted
|
|
48
|
+
Scenario: Delete Item Reduces Order
|
|
49
|
+
Given Customer "Bob" with balance 0 and credit limit 3000
|
|
50
|
+
And Order is created for "Bob" with 3 Widget and 2 Gadget
|
|
51
|
+
When First item is deleted
|
|
33
52
|
Then Order amount_total should be 300
|
|
34
53
|
And Customer balance should be 300
|
|
35
54
|
|
|
36
|
-
Scenario:
|
|
37
|
-
Given Customer "
|
|
38
|
-
And
|
|
55
|
+
Scenario: Change Order Customer
|
|
56
|
+
Given Customer "Alice" with balance 0 and credit limit 5000
|
|
57
|
+
And Customer "Bob" with balance 0 and credit limit 3000
|
|
58
|
+
And Order is created for "Alice" with 5 Widget
|
|
59
|
+
When Order customer changed to "Bob"
|
|
60
|
+
Then Customer "Alice" balance should be 0
|
|
61
|
+
And Customer "Bob" balance should be 450
|
|
62
|
+
|
|
63
|
+
Scenario: Ship Order Excludes from Balance
|
|
64
|
+
Given Customer "Charlie" with balance 0 and credit limit 2000
|
|
65
|
+
And Order is created for "Charlie" with 2 Widget
|
|
39
66
|
When Order is shipped
|
|
40
67
|
Then Customer balance should be 0
|
|
68
|
+
And Order amount_total should be 180
|
|
41
69
|
|
|
42
|
-
Scenario: Unship Order Includes in Balance
|
|
43
|
-
Given Customer "
|
|
44
|
-
And Shipped order
|
|
70
|
+
Scenario: Unship Order Includes in Balance
|
|
71
|
+
Given Customer "Diana" with balance 0 and credit limit 5000
|
|
72
|
+
And Shipped order is created for "Diana" with 3 Gadget
|
|
45
73
|
When Order is unshipped
|
|
46
|
-
Then Customer balance should be
|
|
47
|
-
|
|
48
|
-
Scenario: Exceed Credit Limit Rejected (Constraint FAIL)
|
|
49
|
-
Given Customer "LimitTest" with balance 0 and credit limit 500
|
|
50
|
-
When B2B order placed for "LimitTest" with 10 Gadget
|
|
51
|
-
Then Order creation should fail
|
|
52
|
-
And Error message should contain "credit limit"
|
|
53
|
-
|
|
54
|
-
Scenario: Carbon Neutral Discount Applied (Custom Logic)
|
|
55
|
-
Given Customer "GreenBuyer" with balance 0 and credit limit 2000
|
|
56
|
-
When B2B order placed for "GreenBuyer" with 10 carbon neutral Gadget
|
|
57
|
-
Then Item amount should be 1350
|
|
58
|
-
And Order amount_total should be 1350
|
|
59
|
-
And Customer balance should be 1350
|
|
60
|
-
|
|
61
|
-
Scenario: Product Unit Price Copied to Item
|
|
62
|
-
Given Customer "PriceCopy" with balance 0 and credit limit 3000
|
|
63
|
-
When B2B order placed for "PriceCopy" with 1 Green
|
|
64
|
-
Then Item unit_price should be 109
|
|
74
|
+
Then Customer balance should be 450
|
|
75
|
+
And Order amount_total should be 450
|
|
65
76
|
|
|
66
|
-
Scenario:
|
|
67
|
-
Given Customer "
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
And Item amount should be 300
|
|
72
|
-
And Order amount_total should be 300
|
|
77
|
+
Scenario: Exceed Credit Limit Rejected
|
|
78
|
+
Given Customer "Silent" with balance 0 and credit limit 1000
|
|
79
|
+
When B2B order placed for "Silent" with 20 Widget
|
|
80
|
+
Then Order should be rejected
|
|
81
|
+
And Error message should contain "exceeds credit limit"
|