ApiLogicServer 14.2.20__py3-none-any.whl → 14.3.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 (67) hide show
  1. {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.0.dist-info}/METADATA +2 -2
  2. {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.0.dist-info}/RECORD +65 -47
  3. api_logic_server_cli/api_logic_server.py +4 -1
  4. api_logic_server_cli/api_logic_server_info.yaml +3 -3
  5. api_logic_server_cli/create_from_model/__pycache__/api_logic_server_utils.cpython-312.pyc +0 -0
  6. api_logic_server_cli/create_from_model/__pycache__/ont_build.cpython-312.pyc +0 -0
  7. api_logic_server_cli/create_from_model/__pycache__/ont_create.cpython-312.pyc +0 -0
  8. api_logic_server_cli/create_from_model/api_logic_server_utils.py +4 -0
  9. api_logic_server_cli/create_from_model/ont_build.py +53 -19
  10. api_logic_server_cli/create_from_model/ont_create.py +14 -5
  11. api_logic_server_cli/fragments/declare_logic.py +72 -0
  12. api_logic_server_cli/{prototypes/manager/system/genai/create_db_models_inserts/logic_discovery_prefix.py → fragments/declare_logic_begin.py} +2 -1
  13. api_logic_server_cli/fragments/declare_logic_end.py +52 -0
  14. api_logic_server_cli/genai/genai.py +21 -8
  15. api_logic_server_cli/genai/genai_logic_builder.py +14 -11
  16. api_logic_server_cli/genai/genai_svcs.py +102 -7
  17. api_logic_server_cli/model_migrator/model_migrator_start.py +1 -1
  18. api_logic_server_cli/model_migrator/reposreader.py +9 -1
  19. api_logic_server_cli/model_migrator/rule_obj.py +24 -6
  20. api_logic_server_cli/prototypes/base/api/api_discovery/ontimize_api.py +4 -1
  21. api_logic_server_cli/prototypes/base/config/activate_logicbank.py +8 -4
  22. api_logic_server_cli/prototypes/base/database/bind_dbs.py +1 -1
  23. api_logic_server_cli/prototypes/base/database/test_data/readme.md +5 -5
  24. api_logic_server_cli/prototypes/base/logic/declare_logic.py +8 -3
  25. api_logic_server_cli/prototypes/base/logic/load_verify_rules.py +216 -0
  26. api_logic_server_cli/prototypes/base/logic/logic_discovery/auto_discovery.py +22 -13
  27. api_logic_server_cli/prototypes/genai_demo/logic/declare_logic.py +29 -21
  28. api_logic_server_cli/prototypes/manager/README.md +18 -3
  29. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +19 -18
  30. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/.DS_Store +0 -0
  31. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/000_you_are.prompt +1 -0
  32. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/001_logic_training.prompt +314 -0
  33. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/002_create_db_models.prompt +150 -0
  34. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/003_create_db_models.response +134 -0
  35. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/004_iteratio_logic.prompt +131 -0
  36. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/005_create_db_models.response-example +141 -0
  37. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/create_db_models.py +105 -0
  38. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/db.dbml +70 -0
  39. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/readme.md +6 -0
  40. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/response.json +178 -0
  41. 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
  42. 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
  43. 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
  44. 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
  45. 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
  46. 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
  47. 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
  48. 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
  49. 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
  50. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/002_create_db_models.prompt +194 -0
  51. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/003_create_db_models.response +298 -0
  52. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/db.sqlite +0 -0
  53. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/readme.md +8 -0
  54. api_logic_server_cli/prototypes/manager/system/genai/learning_requests/logic_bank_api.prompt +14 -10
  55. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/iteration.prompt +2 -1
  56. api_logic_server_cli/prototypes/nw_no_cust/venv_setup/system_note.txt +1 -1
  57. api_logic_server_cli/prototypes/ont_app/templates/home_tree_template.html +9 -0
  58. api_logic_server_cli/prototypes/ont_app/templates/tree_routing.jinja +32 -0
  59. api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/__pycache__/codegen.cpython-312.pyc +0 -0
  60. api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/codegen.py +2 -1
  61. api_logic_server_cli/tools/mini_skel/logic/load_verify_rules.py +1 -1
  62. api_logic_server_cli/model_migrator/system/custom_endpoint.py +0 -545
  63. api_logic_server_cli/prototypes/base/database/test_data/z_test_data_rows.py +0 -98
  64. {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.0.dist-info}/LICENSE +0 -0
  65. {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.0.dist-info}/WHEEL +0 -0
  66. {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.0.dist-info}/entry_points.txt +0 -0
  67. {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.0.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
-