ApiLogicServer 14.2.20__py3-none-any.whl → 14.3.7__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.
- {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.7.dist-info}/METADATA +2 -2
- {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.7.dist-info}/RECORD +90 -69
- api_logic_server_cli/api_logic_server.py +5 -1
- api_logic_server_cli/api_logic_server_info.yaml +3 -3
- api_logic_server_cli/cli.py +5 -2
- api_logic_server_cli/create_from_model/__pycache__/api_logic_server_utils.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/__pycache__/ont_build.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/__pycache__/ont_create.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/api_logic_server_utils.py +4 -0
- api_logic_server_cli/create_from_model/ont_build.py +53 -19
- api_logic_server_cli/create_from_model/ont_create.py +14 -5
- api_logic_server_cli/fragments/declare_logic.py +72 -0
- api_logic_server_cli/{prototypes/manager/system/genai/create_db_models_inserts/logic_discovery_prefix.py → fragments/declare_logic_begin.py} +2 -1
- api_logic_server_cli/fragments/declare_logic_end.py +52 -0
- api_logic_server_cli/genai/genai.py +25 -8
- api_logic_server_cli/genai/genai_logic_builder.py +14 -11
- api_logic_server_cli/genai/genai_svcs.py +104 -7
- api_logic_server_cli/manager.py +20 -16
- api_logic_server_cli/model_migrator/model_migrator_start.py +1 -1
- api_logic_server_cli/model_migrator/reposreader.py +9 -1
- api_logic_server_cli/model_migrator/rule_obj.py +24 -6
- api_logic_server_cli/prototypes/base/api/api_discovery/ontimize_api.py +4 -1
- api_logic_server_cli/prototypes/base/api/system/expression_parser.py +10 -4
- api_logic_server_cli/prototypes/base/config/activate_logicbank.py +8 -4
- api_logic_server_cli/prototypes/base/database/bind_dbs.py +1 -1
- api_logic_server_cli/prototypes/base/database/test_data/readme.md +5 -5
- api_logic_server_cli/prototypes/base/integration/kafka/kafka_producer.py +32 -8
- api_logic_server_cli/prototypes/base/integration/system/RowDictMapper.py +33 -16
- api_logic_server_cli/prototypes/base/logic/declare_logic.py +9 -3
- api_logic_server_cli/prototypes/base/logic/load_verify_rules.py +217 -0
- api_logic_server_cli/prototypes/base/logic/logic_discovery/auto_discovery.py +22 -13
- api_logic_server_cli/prototypes/genai_demo/api/customize_api.py +9 -11
- api_logic_server_cli/prototypes/genai_demo/database/.DS_Store +0 -0
- api_logic_server_cli/prototypes/genai_demo/database/db.sqlite +0 -0
- api_logic_server_cli/prototypes/genai_demo/database/models.py +52 -42
- api_logic_server_cli/prototypes/genai_demo/integration/row_dict_maps/OrderB2B.py +4 -6
- api_logic_server_cli/prototypes/genai_demo/integration/row_dict_maps/__pycache__/OrderB2B.cpython-312.pyc +0 -0
- api_logic_server_cli/prototypes/genai_demo/integration/row_dict_maps/row_dict_maps_readme.md +3 -0
- api_logic_server_cli/prototypes/genai_demo/logic/__pycache__/declare_logic.cpython-312.pyc +0 -0
- api_logic_server_cli/prototypes/genai_demo/logic/__pycache__/load_verify_rules.cpython-312.pyc +0 -0
- api_logic_server_cli/prototypes/genai_demo/logic/declare_logic.py +58 -62
- api_logic_server_cli/prototypes/genai_demo/logic/load_verify_rules.py +216 -0
- api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/__pycache__/__init__.cpython-312.pyc +0 -0
- api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/__pycache__/auto_discovery.cpython-312.pyc +0 -0
- api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/__pycache__/error_testing.cpython-312.pyc +0 -0
- api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/auto_discovery.py +52 -0
- api_logic_server_cli/prototypes/genai_demo/logic/readme_declare_logic.md +172 -0
- api_logic_server_cli/prototypes/genai_demo/security/__pycache__/declare_security.cpython-312.pyc +0 -0
- api_logic_server_cli/prototypes/genai_demo/ui/admin/admin.yaml +86 -53
- api_logic_server_cli/prototypes/manager/.vscode/launch.json +1 -1
- api_logic_server_cli/prototypes/manager/README.md +19 -4
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.prompt +4 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +34 -26
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_informal.prompt +3 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/000_you_are.prompt +1 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/001_logic_training.prompt +314 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/002_create_db_models.prompt +150 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/003_create_db_models.response +134 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/004_iteratio_logic.prompt +131 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/005_create_db_models.response-example +141 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/create_db_models.py +105 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/db.dbml +70 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/readme.md +6 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/response.json +178 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/base_genai_demo_no_logic/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/dev_demo_no_logic_fixed/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/genai/examples/genai_demo/wg_dev_merge/base_genai_demo_no_logic/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/genai/examples/genai_demo/wg_dev_merge/dev_demo_no_logic_fixed/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/genai/examples/genai_demo/wg_dev_merge/wg_genai_demo_no_logic_fixed_from_CLI/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/system/genai/examples/genai_demo/wg_dev_merge/base_genai_demo_no_logic/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/system/genai/examples/genai_demo/wg_dev_merge/dev_demo_no_logic_fixed/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/system/genai/examples/genai_demo/wg_dev_merge/wg_genai_demo_no_logic_fixed_from_CLI/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/002_create_db_models.prompt +194 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/003_create_db_models.response +298 -0
- api_logic_server_cli/prototypes/{genai_demo/database/chatgpt/sample_ai.sqlite → manager/system/genai/examples/time_tracking_billing/db.sqlite} +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/readme.md +61 -0
- api_logic_server_cli/prototypes/manager/system/genai/learning_requests/logic_bank_api.prompt +29 -11
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/iteration.prompt +2 -1
- api_logic_server_cli/prototypes/nw_no_cust/venv_setup/system_note.txt +1 -1
- api_logic_server_cli/prototypes/ont_app/templates/home_tree_template.html +9 -0
- api_logic_server_cli/prototypes/ont_app/templates/tree_routing.jinja +32 -0
- api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/__pycache__/codegen.cpython-312.pyc +0 -0
- api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/codegen.py +4 -2
- api_logic_server_cli/tools/mini_skel/logic/load_verify_rules.py +1 -1
- api_logic_server_cli/model_migrator/system/custom_endpoint.py +0 -545
- api_logic_server_cli/prototypes/base/database/test_data/z_test_data_rows.py +0 -98
- api_logic_server_cli/prototypes/genai_demo/database/chatgpt/__pycache__/copilot_models.cpython-312.pyc +0 -0
- api_logic_server_cli/prototypes/genai_demo/database/chatgpt/__pycache__/sample_ai_models.cpython-312.pyc +0 -0
- api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai.chatgpt +0 -16
- api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai.sql +0 -66
- api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai_items.sqlite +0 -0
- api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai_models.py +0 -156
- api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai_models.sqlite +0 -0
- api_logic_server_cli/prototypes/genai_demo/logic/cocktail-napkin.jpg +0 -0
- {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.7.dist-info}/LICENSE +0 -0
- {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.7.dist-info}/WHEEL +0 -0
- {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.7.dist-info}/entry_points.txt +0 -0
- {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.7.dist-info}/top_level.txt +0 -0
|
@@ -1,545 +0,0 @@
|
|
|
1
|
-
from functools import wraps
|
|
2
|
-
import logging
|
|
3
|
-
from flask_jwt_extended import get_jwt, jwt_required, verify_jwt_in_request
|
|
4
|
-
from config import Config
|
|
5
|
-
from security.system.authorization import Security
|
|
6
|
-
import util
|
|
7
|
-
from typing import List
|
|
8
|
-
import safrs
|
|
9
|
-
import sqlalchemy
|
|
10
|
-
from flask import request, jsonify
|
|
11
|
-
from safrs import jsonapi_rpc, SAFRSAPI
|
|
12
|
-
from sqlalchemy.ext.hybrid import hybrid_property
|
|
13
|
-
from sqlalchemy.orm import object_mapper
|
|
14
|
-
from database import models
|
|
15
|
-
from api.system.integration_endpoint import IntegrationEndpoint
|
|
16
|
-
from config import Args
|
|
17
|
-
from flask_cors import cross_origin
|
|
18
|
-
from logic_bank.rule_bank.rule_bank import RuleBank
|
|
19
|
-
from api.custom_resources.Customer import Customer
|
|
20
|
-
from api.custom_resources.OrderById import OrderById
|
|
21
|
-
from api.custom_resources.OrderB2B import OrderB2B
|
|
22
|
-
from api.custom_resources.OrderShipping import OrderShipping
|
|
23
|
-
|
|
24
|
-
# Customize this file to add endpoints/services, using SQLAlchemy as required
|
|
25
|
-
# Separate from expose_api_models.py, to simplify merge if project rebuilt
|
|
26
|
-
# Called by api_logic_server_run.py
|
|
27
|
-
|
|
28
|
-
app_logger = logging.getLogger("api_logic_server_app") # only for create-and-run, no?
|
|
29
|
-
|
|
30
|
-
def expose_services(app, api, project_dir, swagger_host: str, PORT: str):
|
|
31
|
-
"""
|
|
32
|
-
Illustrates Customized APIs, Data Access.
|
|
33
|
-
|
|
34
|
-
* Observe that APIs not limited to database objects, but are extensible.
|
|
35
|
-
* See: https://apilogicserver.github.io/Docs/API-Customize/
|
|
36
|
-
* See: https://github.com/thomaxxl/safrs/wiki/Customization
|
|
37
|
-
|
|
38
|
-
Examples
|
|
39
|
-
|
|
40
|
-
* order_nested_objects() -
|
|
41
|
-
* Uses util.format_nested_objects() (-> jsonify(row).json)
|
|
42
|
-
|
|
43
|
-
* CustomAPICustomer() -
|
|
44
|
-
* SQLAlchemy related row retrieval, reformat as multi-table dict => json
|
|
45
|
-
|
|
46
|
-
* join_order() -
|
|
47
|
-
* Illustrates: SQLAlchemy parent join fields
|
|
48
|
-
|
|
49
|
-
* CategoriesEndPoint get_cats() - swagger, row security
|
|
50
|
-
* Uses util.rows_to_dict (-> row.to_dict())
|
|
51
|
-
|
|
52
|
-
* filters_cats() - model query with filters
|
|
53
|
-
* Uses manual result creation (not util)
|
|
54
|
-
|
|
55
|
-
* raw_sql_cats() - raw sql (non-modeled objects)
|
|
56
|
-
* Uses util.rows_to_dict (-> iterate attributes)
|
|
57
|
-
|
|
58
|
-
"""
|
|
59
|
-
|
|
60
|
-
app_logger.info("..api/expose_service.py, exposing custom services: hello_world, add_order")
|
|
61
|
-
|
|
62
|
-
api.expose_object(ServicesEndPoint) # Swagger-visible services
|
|
63
|
-
api.expose_object(CategoriesEndPoint)
|
|
64
|
-
|
|
65
|
-
@app.route('/hello_world')
|
|
66
|
-
def hello_world():
|
|
67
|
-
"""
|
|
68
|
-
Illustrates:
|
|
69
|
-
* Use standard Flask, here for non-database endpoints.
|
|
70
|
-
|
|
71
|
-
Test it with:
|
|
72
|
-
|
|
73
|
-
http://localhost:5656/hello_world?user=ApiLogicServer
|
|
74
|
-
"""
|
|
75
|
-
user = request.args.get('user')
|
|
76
|
-
# app_logger.info(f'hello_world returning: hello, {user}')
|
|
77
|
-
app_logger.info(f'{user}')
|
|
78
|
-
return jsonify({"result": f'hello, {user}'})
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
@app.route('/stop')
|
|
82
|
-
def stop():
|
|
83
|
-
"""
|
|
84
|
-
Use this to stop the server from the Browser.
|
|
85
|
-
* See: https://stackoverflow.com/questions/15562446/how-to-stop-flask-application-without-using-ctrl-c
|
|
86
|
-
* See: https://github.com/thomaxxl/safrs/wiki/Customization
|
|
87
|
-
|
|
88
|
-
Usage:
|
|
89
|
-
|
|
90
|
-
http://localhost:5656/stop?msg=API stop - Stop API Logic Server
|
|
91
|
-
"""
|
|
92
|
-
|
|
93
|
-
import os, signal
|
|
94
|
-
|
|
95
|
-
msg = request.args.get('msg')
|
|
96
|
-
app_logger.info(f'\nStopped server: {msg}\n')
|
|
97
|
-
|
|
98
|
-
os.kill(os.getpid(), signal.SIGINT)
|
|
99
|
-
return jsonify({ "success": True, "message": "Server is shutting down..." })
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def bypass_security():
|
|
103
|
-
"""
|
|
104
|
-
Support option to bypass security (see cats, below).
|
|
105
|
-
"""
|
|
106
|
-
def wrapper(fn):
|
|
107
|
-
@wraps(fn)
|
|
108
|
-
def decorator(*args, **kwargs):
|
|
109
|
-
if Config.SECURITY_ENABLED == False:
|
|
110
|
-
return fn(*args, **kwargs)
|
|
111
|
-
verify_jwt_in_request(True) # must be issued if security enabled
|
|
112
|
-
return fn(*args, **kwargs)
|
|
113
|
-
return decorator
|
|
114
|
-
return wrapper
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def admin_required():
|
|
118
|
-
"""
|
|
119
|
-
Support option to bypass security (see cats, below).
|
|
120
|
-
|
|
121
|
-
See: https://flask-jwt-extended.readthedocs.io/en/stable/custom_decorators/
|
|
122
|
-
"""
|
|
123
|
-
def wrapper(fn):
|
|
124
|
-
@wraps(fn)
|
|
125
|
-
def decorator(*args, **kwargs):
|
|
126
|
-
if Args.security_enabled == False:
|
|
127
|
-
return fn(*args, **kwargs)
|
|
128
|
-
verify_jwt_in_request(True) # must be issued if security enabled
|
|
129
|
-
return fn(*args, **kwargs)
|
|
130
|
-
return decorator
|
|
131
|
-
return wrapper
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
@app.route('/CustomAPI/Customer', methods=['GET','OPTIONS'])
|
|
135
|
-
@admin_required()
|
|
136
|
-
@jwt_required()
|
|
137
|
-
@cross_origin(supports_credentials=True)
|
|
138
|
-
def CustomAPICustomer():
|
|
139
|
-
"""
|
|
140
|
-
SQLAlchemy row retrieval, reformat as multi-table dict => json
|
|
141
|
-
|
|
142
|
-
start the server (f5) and in the terminal window:
|
|
143
|
-
$(venv) ApiLogicServer login --user=admin --password=p
|
|
144
|
-
$(venv) ApiLogicServer curl "http://localhost:5656/CustomAPI/Customer?Id=ALFKI"
|
|
145
|
-
"""
|
|
146
|
-
request_id = request.args.get('Id')
|
|
147
|
-
if request_id is None:
|
|
148
|
-
request_id = 'ALFKI'
|
|
149
|
-
|
|
150
|
-
db = safrs.DB # Use the safrs.DB, not db!
|
|
151
|
-
session = db.session # sqlalchemy.orm.scoping.scoped_session
|
|
152
|
-
# Security.set_user_sa() # an endpoint that requires no auth header (see also @bypass_security)
|
|
153
|
-
the_customer : models.Customer = session.query(models.Customer) \
|
|
154
|
-
.filter(models.Customer.Id == request_id).one()
|
|
155
|
-
|
|
156
|
-
customer_def = Customer()
|
|
157
|
-
dict_row = customer_def.to_dict(row = the_customer)
|
|
158
|
-
return jsonify({"Customer with related data": dict_row})
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
@app.route('/join_order')
|
|
162
|
-
@bypass_security()
|
|
163
|
-
def join_order():
|
|
164
|
-
"""
|
|
165
|
-
Illustrates: SQLAlchemy join fields
|
|
166
|
-
|
|
167
|
-
$(venv) ApiLogicServer curl "http://localhost:5656/join_order?id=11077"
|
|
168
|
-
|
|
169
|
-
Returns:
|
|
170
|
-
_type_: _description_
|
|
171
|
-
"""
|
|
172
|
-
|
|
173
|
-
request_id = request.args.get('id')
|
|
174
|
-
if request_id is None:
|
|
175
|
-
request_id = 11078
|
|
176
|
-
db = safrs.DB # Use the safrs.DB, not db!
|
|
177
|
-
session = db.session # sqlalchemy.orm.scoping.scoped_session
|
|
178
|
-
Security.set_user_sa() # an endpoint that requires no auth header (see also @bypass_security)
|
|
179
|
-
the_order : models.Order = session.query(models.Order) \
|
|
180
|
-
.filter(models.Order.Id == request_id).one()
|
|
181
|
-
|
|
182
|
-
dict_row = {}
|
|
183
|
-
dict_row["id"] = the_order.Id
|
|
184
|
-
dict_row["AmountTotal"] = the_order.AmountTotal
|
|
185
|
-
dict_row["SalesRepLastName"] = the_order.Employee.LastName
|
|
186
|
-
return jsonify({"order_with_join_attr": dict_row})
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
@app.route('/join_order_custom', methods=['GET','OPTIONS'])
|
|
190
|
-
@admin_required()
|
|
191
|
-
@jwt_required()
|
|
192
|
-
@cross_origin(supports_credentials=True)
|
|
193
|
-
def join_order_custom():
|
|
194
|
-
"""
|
|
195
|
-
SQLAlchemy row retrieval, reformat as multi-table dict => json
|
|
196
|
-
|
|
197
|
-
$(venv) ApiLogicServer login --user=admin --password=p
|
|
198
|
-
$(venv) ApiLogicServer curl "http://localhost:5656/join_order_custom?id=11077"
|
|
199
|
-
|
|
200
|
-
"""
|
|
201
|
-
request_id = request.args.get('id')
|
|
202
|
-
if request_id is None:
|
|
203
|
-
request_id = 11078
|
|
204
|
-
db = safrs.DB # Use the safrs.DB, not db!
|
|
205
|
-
session = db.session # sqlalchemy.orm.scoping.scoped_session
|
|
206
|
-
Security.set_user_sa() # an endpoint that requires no auth header (see also @bypass_security)
|
|
207
|
-
the_order : models.Order = session.query(models.Order) \
|
|
208
|
-
.filter(models.Order.Id == request_id).one()
|
|
209
|
-
|
|
210
|
-
order_def = OrderShipping()
|
|
211
|
-
dict_row = order_def.to_dict(row = the_order)
|
|
212
|
-
return jsonify({"Order with related data": dict_row})
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
@app.route('/join_order_b2b', methods=['GET','OPTIONS'])
|
|
216
|
-
@admin_required()
|
|
217
|
-
@jwt_required()
|
|
218
|
-
@cross_origin(supports_credentials=True)
|
|
219
|
-
def join_order_b2b():
|
|
220
|
-
"""
|
|
221
|
-
SQLAlchemy row retrieval, reformat as multi-table dict => json
|
|
222
|
-
|
|
223
|
-
$(venv) ApiLogicServer login --user=admin --password=p
|
|
224
|
-
$(venv) ApiLogicServer curl "http://localhost:5656/join_order_b2b?id=11077"
|
|
225
|
-
|
|
226
|
-
"""
|
|
227
|
-
request_id = request.args.get('id')
|
|
228
|
-
if request_id is None:
|
|
229
|
-
request_id = 11078
|
|
230
|
-
db = safrs.DB # Use the safrs.DB, not db!
|
|
231
|
-
session = db.session # sqlalchemy.orm.scoping.scoped_session
|
|
232
|
-
Security.set_user_sa() # an endpoint that requires no auth header (see also @bypass_security)
|
|
233
|
-
the_order : models.Order = session.query(models.Order) \
|
|
234
|
-
.filter(models.Order.Id == request_id).one()
|
|
235
|
-
|
|
236
|
-
order_def = OrderB2B()
|
|
237
|
-
dict_row = order_def.to_dict(row = the_order)
|
|
238
|
-
return jsonify({"Order with related data": dict_row})
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
@app.route('/filters_cats')
|
|
242
|
-
@bypass_security()
|
|
243
|
-
def filters_cats():
|
|
244
|
-
"""
|
|
245
|
-
Illustrates:
|
|
246
|
-
* Explore SQLAlchemy and/or filters.
|
|
247
|
-
|
|
248
|
-
Test (returns rows 2-5) (no auth):
|
|
249
|
-
curl -X GET "http://localhost:5656/filters_cats" [no-filter | simple-filter]"
|
|
250
|
-
"""
|
|
251
|
-
|
|
252
|
-
from sqlalchemy import and_, or_
|
|
253
|
-
filter_type = request.args.get('filter')
|
|
254
|
-
if filter_type is None:
|
|
255
|
-
filter_type = "multiple filters"
|
|
256
|
-
db = safrs.DB # Use the safrs.DB, not db!
|
|
257
|
-
session = db.session # sqlalchemy.orm.scoping.scoped_session
|
|
258
|
-
Security.set_user_sa() # an endpoint that requires no auth header (see also @bypass_security)
|
|
259
|
-
|
|
260
|
-
if filter_type.startswith("n"):
|
|
261
|
-
results = session.query(models.Category) # .filter(models.Category.Id > 1)
|
|
262
|
-
elif filter_type.startswith("s"): # normally coded like this
|
|
263
|
-
results = session.query(models.Category) \
|
|
264
|
-
.filter(models.Category.Id > 1) \
|
|
265
|
-
.filter(or_((models.Category.Client_id == 2), (models.Category.Id == 5)))
|
|
266
|
-
else: # simulate grant logic (multiple filters)
|
|
267
|
-
client_grant = models.Category.Client_id == 2
|
|
268
|
-
id_grant = models.Category.Id == 5
|
|
269
|
-
grant_filter = or_( client_grant, id_grant)
|
|
270
|
-
results = session.query(models.Category) \
|
|
271
|
-
.filter(models.Category.Id > 1) \
|
|
272
|
-
.filter(grant_filter)
|
|
273
|
-
return_result = []
|
|
274
|
-
for each_result in results:
|
|
275
|
-
row = { 'id': each_result.Id, 'name': each_result.CategoryName}
|
|
276
|
-
return_result.append(row)
|
|
277
|
-
return jsonify({ "success": True, "result": return_result})
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
@app.route('/raw_sql_cats')
|
|
281
|
-
@bypass_security()
|
|
282
|
-
def raw_sql_cats():
|
|
283
|
-
"""
|
|
284
|
-
Illustrates:
|
|
285
|
-
* "Raw" SQLAlchemy table queries (non-mapped objects)
|
|
286
|
-
* Observe phyical column name: CategoryName_ColumnName
|
|
287
|
-
* Contrast to models.py, get_cats()
|
|
288
|
-
|
|
289
|
-
Test (auth optional):
|
|
290
|
-
curl -X GET "http://localhost:5656/raw_sql_cats"
|
|
291
|
-
|
|
292
|
-
"""
|
|
293
|
-
DB = safrs.DB
|
|
294
|
-
sql_query = DB.text("SELECT * FROM CategoryTableNameTest")
|
|
295
|
-
with DB.engine.begin() as connection:
|
|
296
|
-
query_result = connection.execute(sql_query).all()
|
|
297
|
-
rows_to_dict_rows = util.rows_to_dict(query_result)
|
|
298
|
-
response = {"result": rows_to_dict_rows}
|
|
299
|
-
return response
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
@app.route('/order_nested_objects')
|
|
303
|
-
def order_nested_objects():
|
|
304
|
-
"""
|
|
305
|
-
Illustrates:
|
|
306
|
-
* Returning a nested result set response
|
|
307
|
-
* Using SQLAlchemy to obtain data, and related data
|
|
308
|
-
* Restructuring row results to desired json (e.g., for tool such as Sencha)
|
|
309
|
-
|
|
310
|
-
Test (auth optional):
|
|
311
|
-
http://localhost:5656/order_nested_objects?Id=10643
|
|
312
|
-
curl -X GET "http://localhost:5656/order_nested_objects?Id=10643"
|
|
313
|
-
|
|
314
|
-
"""
|
|
315
|
-
order_id = request.args.get('Id')
|
|
316
|
-
db = safrs.DB # Use the safrs.DB, not db!
|
|
317
|
-
session = db.session # sqlalchemy.orm.scoping.scoped_session
|
|
318
|
-
order = session.query(models.Order).filter(models.Order.Id == order_id).one()
|
|
319
|
-
|
|
320
|
-
result_std_dict = util.format_nested_object(order
|
|
321
|
-
, replace_attribute_tag='data'
|
|
322
|
-
, remove_links_relationships=True)
|
|
323
|
-
result_std_dict['data']['Customer_Name'] = order.Customer.CompanyName # eager fetch
|
|
324
|
-
result_std_dict['data']['OrderDetailListAsDicts'] = []
|
|
325
|
-
for each_order_detail in order.OrderDetailList: # lazy fetch
|
|
326
|
-
each_order_detail_dict = util.format_nested_object(row=each_order_detail
|
|
327
|
-
, replace_attribute_tag='data'
|
|
328
|
-
, remove_links_relationships=True)
|
|
329
|
-
each_order_detail_dict['data']['ProductName'] = each_order_detail.Product.ProductName
|
|
330
|
-
result_std_dict['data']['OrderDetailListAsDicts'].append(each_order_detail_dict)
|
|
331
|
-
return result_std_dict
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
@app.route('/server_log')
|
|
335
|
-
def server_log():
|
|
336
|
-
"""
|
|
337
|
-
Used by test/*.py - enables client app to log msg into server
|
|
338
|
-
"""
|
|
339
|
-
return util.server_log(request, jsonify)
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
@app.route('/metadata')
|
|
343
|
-
def metadata():
|
|
344
|
-
"""
|
|
345
|
-
Swagger provides typical API discovery. This is for tool providers
|
|
346
|
-
requiring programmatic access to api definition, e.g.,
|
|
347
|
-
to drive artifact code generation.
|
|
348
|
-
|
|
349
|
-
Returns json for list of 1 / all resources, with optional attribute name/type, eg
|
|
350
|
-
|
|
351
|
-
curl -X GET "http://localhost:5656/metadata?resource=Category&include=attributes"
|
|
352
|
-
|
|
353
|
-
curl -X GET "http://localhost:5656/metadata?include=attributes"
|
|
354
|
-
"""
|
|
355
|
-
import inspect
|
|
356
|
-
import sys
|
|
357
|
-
from sqlalchemy.ext.declarative import declarative_base
|
|
358
|
-
|
|
359
|
-
resource_name = request.args.get('resource')
|
|
360
|
-
include_attributes = False
|
|
361
|
-
include = request.args.get('include')
|
|
362
|
-
if include:
|
|
363
|
-
include_attributes = "attributes" in include
|
|
364
|
-
resource_list = [] # array of attributes[], name (so, the name is last...)
|
|
365
|
-
resource_objs = {} # objects, named = resource_name
|
|
366
|
-
|
|
367
|
-
models_name = "database.models"
|
|
368
|
-
cls_members = inspect.getmembers(sys.modules["database.models"], inspect.isclass)
|
|
369
|
-
for each_cls_member in cls_members:
|
|
370
|
-
each_class_def_str = str(each_cls_member)
|
|
371
|
-
if (f"'{models_name}." in str(each_class_def_str) and
|
|
372
|
-
"Ab" not in str(each_class_def_str)):
|
|
373
|
-
each_resource_name = each_cls_member[0]
|
|
374
|
-
each_resource_class = each_cls_member[1]
|
|
375
|
-
each_resource_mapper = each_resource_class.__mapper__
|
|
376
|
-
if resource_name is None or resource_name == each_resource_name:
|
|
377
|
-
resource_object = {"name": each_resource_name}
|
|
378
|
-
resource_list.append(resource_object)
|
|
379
|
-
resource_objs[each_resource_name] = {}
|
|
380
|
-
if include_attributes:
|
|
381
|
-
attr_list = []
|
|
382
|
-
for each_attr in each_resource_mapper.attrs:
|
|
383
|
-
if not each_attr._is_relationship:
|
|
384
|
-
try:
|
|
385
|
-
attribute_object = {"name": each_attr.key,
|
|
386
|
-
"type": str(each_attr.expression.type)}
|
|
387
|
-
except:
|
|
388
|
-
attribute_object = {"name": each_attr.key,
|
|
389
|
-
"type": "unkown"}
|
|
390
|
-
attr_list.append(attribute_object)
|
|
391
|
-
resource_object["attributes"] = attr_list
|
|
392
|
-
resource_objs[each_resource_name] = {"attributes": attr_list}
|
|
393
|
-
# pick the format you like
|
|
394
|
-
return_result = {"resources": resource_list}
|
|
395
|
-
return_result = {"resources": resource_objs}
|
|
396
|
-
return jsonify(return_result)
|
|
397
|
-
|
|
398
|
-
class ServicesEndPoint(safrs.JABase):
|
|
399
|
-
"""
|
|
400
|
-
Illustrates
|
|
401
|
-
* Custom service - visible in swagger
|
|
402
|
-
* Quite small, since leverages logic/declare_logic rules
|
|
403
|
-
"""
|
|
404
|
-
|
|
405
|
-
@classmethod
|
|
406
|
-
@jsonapi_rpc(http_methods=["POST"])
|
|
407
|
-
def add_order(self, *args, **kwargs): # yaml comment => swagger description
|
|
408
|
-
""" # yaml creates Swagger description
|
|
409
|
-
args :
|
|
410
|
-
CustomerId: ALFKI
|
|
411
|
-
EmployeeId: 1
|
|
412
|
-
Freight: 10
|
|
413
|
-
OrderDetailList :
|
|
414
|
-
- ProductId: 1
|
|
415
|
-
Quantity: 1
|
|
416
|
-
Discount: 0
|
|
417
|
-
- ProductId: 2
|
|
418
|
-
Quantity: 2
|
|
419
|
-
Discount: 0
|
|
420
|
-
"""
|
|
421
|
-
|
|
422
|
-
# test using swagger -> try it out (includes sample data, above)
|
|
423
|
-
|
|
424
|
-
db = safrs.DB # Use the safrs.DB, not db!
|
|
425
|
-
session = db.session # sqlalchemy.orm.scoping.scoped_session
|
|
426
|
-
new_order = models.Order()
|
|
427
|
-
session.add(new_order)
|
|
428
|
-
|
|
429
|
-
util.json_to_entities(kwargs, new_order) # generic function - any db object
|
|
430
|
-
return {"Thankyou For Your Order"} # automatic commit, which executes transaction logic
|
|
431
|
-
"""
|
|
432
|
-
curl -X 'POST' \
|
|
433
|
-
'http://localhost:5656/api/ServicesEndPoint/add_order' \
|
|
434
|
-
-H 'accept: application/vnd.api+json' \
|
|
435
|
-
-H 'Content-Type: application/json' \
|
|
436
|
-
-d '{
|
|
437
|
-
"meta": {
|
|
438
|
-
"method": "add_order",
|
|
439
|
-
"args": {
|
|
440
|
-
"CustomerId": "ALFKI",
|
|
441
|
-
"EmployeeId": 1,
|
|
442
|
-
"Freight": 10,
|
|
443
|
-
"OrderDetailList": [
|
|
444
|
-
{
|
|
445
|
-
"ProductId": 1,
|
|
446
|
-
"Quantity": 1,
|
|
447
|
-
"Discount": 0
|
|
448
|
-
},
|
|
449
|
-
{
|
|
450
|
-
"ProductId": 2,
|
|
451
|
-
"Quantity": 2,
|
|
452
|
-
"Discount": 0
|
|
453
|
-
}
|
|
454
|
-
]
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}'
|
|
458
|
-
"""
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
@classmethod
|
|
462
|
-
@jsonapi_rpc(http_methods=["POST"])
|
|
463
|
-
def add_b2b_order(self, *args, **kwargs): # yaml comment => swagger description
|
|
464
|
-
""" # yaml creates Swagger description
|
|
465
|
-
args :
|
|
466
|
-
AccountId: "ALFKI"
|
|
467
|
-
Given: "xx"
|
|
468
|
-
Surname: "yy"
|
|
469
|
-
Items :
|
|
470
|
-
- ProductName: "Chai"
|
|
471
|
-
QuantityOrdered: 1
|
|
472
|
-
- ProductName: "Chang"
|
|
473
|
-
QuantityOrdered: 2
|
|
474
|
-
"""
|
|
475
|
-
|
|
476
|
-
db = safrs.DB # Use the safrs.DB, not db!
|
|
477
|
-
session = db.session # sqlalchemy.orm.scoping.scoped_session
|
|
478
|
-
|
|
479
|
-
order_id_def = OrderB2B()
|
|
480
|
-
request_dict_str = request.data.decode('utf-8')
|
|
481
|
-
request_dict = eval(request_dict_str)
|
|
482
|
-
request_dict_data = request_dict["order"]
|
|
483
|
-
sql_alchemy_row = order_id_def.to_row(row_dict = request_dict_data, session = session)
|
|
484
|
-
|
|
485
|
-
session.add(sql_alchemy_row)
|
|
486
|
-
return {"Thankyou For Your OrderB2B"} # automatic commit, which executes transaction logic
|
|
487
|
-
|
|
488
|
-
@classmethod
|
|
489
|
-
# @jwt_required()
|
|
490
|
-
@jsonapi_rpc(http_methods=["POST"])
|
|
491
|
-
def add_order_by_id(self, *args, **kwargs): # yaml comment => swagger description
|
|
492
|
-
""" # yaml creates Swagger description
|
|
493
|
-
order :
|
|
494
|
-
AccountId: ALFKI
|
|
495
|
-
SalesRepId: 1
|
|
496
|
-
Items :
|
|
497
|
-
- ProductId: 1
|
|
498
|
-
QuantityOrdered: 1
|
|
499
|
-
- ProductId: 2
|
|
500
|
-
QuantityOrdered: 2
|
|
501
|
-
"""
|
|
502
|
-
|
|
503
|
-
# test using swagger -> try it out (includes sample data, above)
|
|
504
|
-
|
|
505
|
-
db = safrs.DB # Use the safrs.DB, not db!
|
|
506
|
-
session = db.session # sqlalchemy.orm.scoping.scoped_session
|
|
507
|
-
|
|
508
|
-
order_id_def = OrderById()
|
|
509
|
-
request_dict_str = request.data.decode('utf-8')
|
|
510
|
-
request_dict = eval(request_dict_str)
|
|
511
|
-
request_dict_data = request_dict["order"]
|
|
512
|
-
sql_alchemy_row = order_id_def.to_row(row_dict = request_dict_data, session = session)
|
|
513
|
-
|
|
514
|
-
session.add(sql_alchemy_row)
|
|
515
|
-
return {"Thankyou For Your OrderById"} # automatic commit, which executes transaction logic
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
class CategoriesEndPoint(safrs.JABase):
|
|
519
|
-
"""
|
|
520
|
-
Illustrates
|
|
521
|
-
* Swagger-visible RPC that requires authentication (@jwt_required()).
|
|
522
|
-
* Row Security
|
|
523
|
-
|
|
524
|
-
Test in swagger (auth required)
|
|
525
|
-
* Post to endpoint auth to obtain <access_token> value - copy to clipboard
|
|
526
|
-
* Row Security - Users determines results
|
|
527
|
-
* u1 - 1 row, u2 - 4 rows, admin - 9 rows
|
|
528
|
-
* Authorize (top of swagger), using Bearer <access_token>
|
|
529
|
-
* Post to CategoriesEndPoint/get_cats, observe results depend on login
|
|
530
|
-
|
|
531
|
-
"""
|
|
532
|
-
|
|
533
|
-
@staticmethod
|
|
534
|
-
@jwt_required()
|
|
535
|
-
@jsonapi_rpc(http_methods=['POST'], valid_jsonapi=False)
|
|
536
|
-
def get_cats():
|
|
537
|
-
db = safrs.DB
|
|
538
|
-
session = db.session
|
|
539
|
-
|
|
540
|
-
result = session.query(models.Category)
|
|
541
|
-
for each_row in result:
|
|
542
|
-
app_logger.debug(f'each_row: {each_row}')
|
|
543
|
-
rows = util.rows_to_dict(result)
|
|
544
|
-
response = {"result": rows}
|
|
545
|
-
return response
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
# experiment - under construction. Not working, not used.
|
|
2
|
-
# created from response, to create create_db_models.sqlite, with test data
|
|
3
|
-
# that is used to create project
|
|
4
|
-
# should run without error in manager
|
|
5
|
-
# if not, check for decimal, indent, or import issues
|
|
6
|
-
|
|
7
|
-
import decimal
|
|
8
|
-
import logging
|
|
9
|
-
import sqlalchemy
|
|
10
|
-
from sqlalchemy.sql import func
|
|
11
|
-
from logic_bank.logic_bank import Rule
|
|
12
|
-
from sqlalchemy import create_engine, Column, Integer, String, Float, ForeignKey, Date, DateTime, Numeric, Boolean, Text, DECIMAL
|
|
13
|
-
from sqlalchemy.types import *
|
|
14
|
-
from sqlalchemy.ext.declarative import declarative_base
|
|
15
|
-
from sqlalchemy.orm import sessionmaker
|
|
16
|
-
from sqlalchemy.orm import relationship
|
|
17
|
-
from sqlalchemy.orm import Mapped
|
|
18
|
-
from datetime import date
|
|
19
|
-
from datetime import datetime
|
|
20
|
-
from typing import List
|
|
21
|
-
import os, logging, logging.config, sys, yaml
|
|
22
|
-
from pathlib import Path
|
|
23
|
-
from logic_bank.logic_bank import LogicBank
|
|
24
|
-
from database.models import *
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def load_data(session):
|
|
29
|
-
# Create test data
|
|
30
|
-
session.commit()
|
|
31
|
-
customer1 = Customer(id=1, name="John Doe", balance=130.00, credit_limit=500.00)
|
|
32
|
-
session.add(customer1)
|
|
33
|
-
session.commit()
|
|
34
|
-
|
|
35
|
-
customer2 = Customer(id=2, name="Jane Smith", balance=200.00, credit_limit=600.00)
|
|
36
|
-
session.add(customer2)
|
|
37
|
-
session.commit()
|
|
38
|
-
|
|
39
|
-
customer3 = Customer(id=3, name="George White", balance=300.00, credit_limit=700.00)
|
|
40
|
-
session.add(customer3)
|
|
41
|
-
session.commit()
|
|
42
|
-
|
|
43
|
-
customer4 = Customer(id=4, name="Lisa Brown", balance=400.00, credit_limit=800.00)
|
|
44
|
-
session.add(customer4)
|
|
45
|
-
session.commit()
|
|
46
|
-
|
|
47
|
-
product1 = Product(id=1, unit_price=10.00)
|
|
48
|
-
session.add(product1)
|
|
49
|
-
session.commit()
|
|
50
|
-
|
|
51
|
-
product2 = Product(id=2, unit_price=15.00)
|
|
52
|
-
session.add(product2)
|
|
53
|
-
session.commit()
|
|
54
|
-
|
|
55
|
-
product3 = Product(id=3, unit_price=20.00)
|
|
56
|
-
session.add(product3)
|
|
57
|
-
session.commit()
|
|
58
|
-
|
|
59
|
-
product4 = Product(id=4, unit_price=30.00)
|
|
60
|
-
session.add(product4)
|
|
61
|
-
session.commit()
|
|
62
|
-
|
|
63
|
-
order1 = Order(id=1, customer_id=1, date_shipped=None, amount_total=30.00, notes="First order")
|
|
64
|
-
session.add(order1)
|
|
65
|
-
session.commit()
|
|
66
|
-
|
|
67
|
-
order2 = Order(id=2, customer_id=2, date_shipped=None, amount_total=45.00, notes="Second order")
|
|
68
|
-
session.add(order2)
|
|
69
|
-
session.commit()
|
|
70
|
-
|
|
71
|
-
order3 = Order(id=3, customer_id=3, date_shipped=None, amount_total=60.00, notes="Third order")
|
|
72
|
-
session.add(order3)
|
|
73
|
-
session.commit()
|
|
74
|
-
|
|
75
|
-
order4 = Order(id=4, customer_id=4, date_shipped=None, amount_total=50.00, notes="Fourth order")
|
|
76
|
-
session.add(order4)
|
|
77
|
-
session.commit()
|
|
78
|
-
|
|
79
|
-
item1 = Item(id=1, order_id=1, product_id=1, quantity=3, amount=30.00, unit_price=10.00)
|
|
80
|
-
session.add(item1)
|
|
81
|
-
session.commit()
|
|
82
|
-
|
|
83
|
-
item2 = Item(id=2, order_id=2, product_id=2, quantity=3, amount=45.00, unit_price=15.00)
|
|
84
|
-
session.add(item2)
|
|
85
|
-
session.commit()
|
|
86
|
-
|
|
87
|
-
item3 = Item(id=3, order_id=3, product_id=3, quantity=3, amount=60.00, unit_price=20.00)
|
|
88
|
-
session.add(item3)
|
|
89
|
-
session.commit()
|
|
90
|
-
|
|
91
|
-
item4 = Item(id=4, order_id=4, product_id=4, quantity=2, amount=50.00, unit_price=25.00)
|
|
92
|
-
session.add(item4)
|
|
93
|
-
session.commit()
|
|
94
|
-
|
|
95
|
-
# session.add_all([customer1, customer2, customer3, customer4, product1, product2, product3, product4, order1, order2, order3, order4, item1, item2, item3, item4])
|
|
96
|
-
session.commit()
|
|
97
|
-
# end of test data
|
|
98
|
-
|
|
Binary file
|
|
Binary file
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
Create a sqlite database for customers, orders, items and product
|
|
3
|
-
|
|
4
|
-
Hints: use autonum keys, allow nulls, Decimal types, foreign keys, no check constraints.
|
|
5
|
-
|
|
6
|
-
Include a notes field for orders.
|
|
7
|
-
|
|
8
|
-
Create a few rows of only customer and product data.
|
|
9
|
-
|
|
10
|
-
Enforce the Check Credit requirement (do not generate check constraints):
|
|
11
|
-
|
|
12
|
-
1. Customer.Balance <= CreditLimit
|
|
13
|
-
2. Customer.Balance = Sum(Order.AmountTotal where date shipped is null)
|
|
14
|
-
3. Order.AmountTotal = Sum(Items.Amount)
|
|
15
|
-
4. Items.Amount = Quantity * UnitPrice
|
|
16
|
-
5. Store the Items.UnitPrice as a copy from Product.UnitPrice
|