class EntityAdd(Resource): api = AppInfo.get_api() @api.doc(parser=create_parser()) def post(self, table): try: context = g.context if request.json == None: abort( 400, "cannot extract json data in http request for insert %s" % (table)) fetch = build_fetchxml_by_alias(context, table, None, request.json, type="insert") fetchparser = FetchXmlParser(fetch, context) rs = DatabaseServices.exec(fetchparser, context, fetch_mode=0) result = { "rows_affected": rs.get_cursor().rowcount, "inserted_id": rs.get_inserted_id() } return result except RestApiNotAllowed as err: logger.exception(f"{err}") abort(400, f"{err}") except Exception as err: logger.exception(f"{err}") abort(500, f"{err}")
class AppHome(Resource): api = AppInfo.get_api("ui") @api.doc(parser=create_parser()) def get(self): try: path = "templates/base/uihome.htm" logger.info(f"Path: {path}") create_parser().parse_args() context = g.context template = JinjaTemplate.create_file_template(context, path) fetch = f""" <restapi type="select"> <table name="api_ui_app" alias="a"/> <select> <field name="id" table_alias="a"/> <field name="name" table_alias="a"/> <field name="description" table_alias="a"/> <field name="home_url" table_alias="a"/> </select> </restapi> """ fetch_parser = FetchXmlParser(fetch, context) rs = DatabaseServices.exec(fetch_parser, context, fetch_mode=0) response = make_response( template.render({ "context": context, "apps": rs.get_result() })) response.headers['content-type'] = 'text/html' return response except ConfigNotValid as err: logger.exception(f"Config not valid {err}") return make_response( JinjaTemplate.render_status_template(500, err), 500) except jinja2.exceptions.TemplateNotFound as err: logger.exception(f"TemplateNotFound: {err}") return make_response( JinjaTemplate.render_status_template( 404, f"Template not found {err}"), 404) except FileNotFoundError as err: logger.exception(f"FileNotFound: {err}") return make_response( JinjaTemplate.render_status_template(404, f"File not found {err}"), 404) except RestApiNotAllowed as err: logger.exception(f"RestApiNotAllowed Exception: {err}") return redirect(f"/ui/login?redirect={request.url}", code=302) except Exception as err: logger.exception(f"Exception: {err}") return make_response( JinjaTemplate.render_status_template(500, err), 500)
class Action(Resource): api = AppInfo.get_api() @api.doc(parser=create_parser_post()) def post(self, action): try: create_parser_post().parse_args() context = g.context if request.json == None: abort(400, f"cannot extract json data in body action:{action}") params = {"input": request.json, "output": {}} handler = Plugin(context, action, "execute") handler.execute('before', params) output = params['output'] response = make_response(output, 200) return response except NameError as err: abort(400, f"{err}") except Exception as err: abort(500, f"{err}")
class Entity(Resource): api=AppInfo.get_api() @api.doc(parser=create_parser_post()) def post(self,table,id): try: create_parser_post().parse_args() context=g.context json=request.form.to_dict() for key in list(json): if key.startswith("__"): del json[key] fetch=build_fetchxml_by_alias(context, table, id, json, type="update") fetchparser=FetchXmlParser(fetch, context) rs=DatabaseServices.exec(fetchparser,context, fetch_mode=0) next=HTTPRequest.redirect(request, default=f"/ui/v1.0/data/{table}/$$id$$", id=id) return redirect(next, code=302) except RestApiNotAllowed as err: logger.info(f"RestApiNotAllowed Exception: {err}") return redirect(f"/ui/login?redirect=/ui/v1.0/data/{table}/{id}", code=302) except Exception as err: return make_response(JinjaTemplate.render_status_template(500, err), 500)
class Portal(Resource): api = AppInfo.get_api("ui") @api.doc(parser=create_parser()) def get(self): try: path = "templates/base/login.htm" logger.info(f"Path: {path}") create_parser().parse_args() context = g.context next = HTTPRequest.redirect(request, "/ui/login") logger.info(f"Redirect : {next}") msg = '' if 'msg' in request.args: msg = request.args.get('msg') logged_on = False if 'session_id' in session: logged_on = True template = JinjaTemplate.create_file_template(context, path) response = make_response( template.render({ "redirect": next, "msg": msg, "logged_on": logged_on, "context": context })) response.headers['content-type'] = 'text/html' return response except ConfigNotValid as err: logger.exception(f"Config not valid {err}") return make_response( JinjaTemplate.render_status_template(500, err), 500) except jinja2.exceptions.TemplateNotFound as err: logger.exception(f"TemplateNotFound: {err}") return make_response( JinjaTemplate.render_status_template( 404, f"Template not found {err}"), 404) except FileNotFoundError as err: logger.exception(f"FileNotFound: {err}") return make_response( JinjaTemplate.render_status_template(404, f"File not found {err}"), 404) except RestApiNotAllowed as err: logger.exception(f"RestApiNotAllowed Exception: {err}") return redirect(f"/ui/login?redirect={request.url}", code=302) except Exception as err: logger.exception(f"Exception: {err}") return make_response( JinjaTemplate.render_status_template(500, err), 500)
class DataFormInsert(Resource): api = AppInfo.get_api("ui") @api.doc(parser=create_parser()) def get(self, table): try: create_parser().parse_args() context = g.context from core.permission import Permission if not Permission().validate(context, "create", context.get_username(), table): raise RestApiNotAllowed("") table_meta = read_table_meta(context, alias=table) solution_id = table_meta['solution_id'] if solution_id == 1: file = f"templates/{table}_insert.htm" else: file = f"solutions/{solution_id}/{table}_insert.htm" logger.info(f"Redirect : {next}") template = JinjaTemplate.create_file_template(context, file) response = make_response( template.render({ "table": table, "pagemode": "dataforminsert", "context": context })) response.headers['content-type'] = 'text/html' return response except ConfigNotValid as err: logger.exception(f"Config not valid {err}") return make_response( JinjaTemplate.render_status_template(context, 500, err), 500) except jinja2.exceptions.TemplateNotFound as err: logger.exception(f"TemplateNotFound: {err}") return make_response( JinjaTemplate.render_status_template( context, 404, f"Template not found {err}"), 404) except FileNotFoundError as err: logger.exception(f"FileNotFound: {err}") return make_response( JinjaTemplate.render_status_template(context, 404, f"File not found {err}"), 404) except RestApiNotAllowed as err: logger.exception(f"RestApiNotAllowed Exception: {err}") #return redirect(f"/ui/login?redirect=/ui/v1.0/data/{table}", code=302) return redirect(f"/ui/login?redirect={request.url}", code=302) except Exception as err: logger.exception(f"Exception: {err}") return make_response( JinjaTemplate.render_status_template(context, 500, err), 500)
class EntitySet(Resource): api = AppInfo.get_api() api = AppInfo.get_api() @api.doc(parser=create_parser()) def get(self, table): try: create_parser().parse_args() context = g.context fetch = build_fetchxml_by_alias(context, table, None, None, type="select") fetchparser = FetchXmlParser(fetch, context) rs = DatabaseServices.exec(fetchparser, context, fetch_mode=0) return rs.get_result() except RestApiNotAllowed as err: logger.info(f"{err}") abort(400, f"{err}") except Exception as err: abort(500, f"{err}")
class DefaultPage(Resource): api = AppInfo.get_api("portal") @api.doc(parser=create_parser()) def get(self): try: logger.info(f"redirect to the default page index.htm") create_parser().parse_args() context = g.context return redirect(f"/index.htm", code=302) except Exception as err: logger.exception(f"Exception: {err}") abort(500, f"{err}")
class EntityListFilter(Resource): api = AppInfo.get_api() @api.doc(parser=create_parser()) def post(self): try: parser = create_parser().parse_args() context = g.context fetch = request.data fetchparser = FetchXmlParser(fetch, context) rs = DatabaseServices.exec(fetchparser, context, fetch_mode=0) return rs.get_result() except RestApiNotAllowed as err: abort(400, f"{err}") except Exception as err: abort(500, f"{err}")
class Login(Resource): api = AppInfo.get_api() @api.doc(parser=create_parser()) def post(self): username = "" password = "" next = HTTPRequest.redirect(request) if 'username' in request.headers: username = request.headers.get("username") password = request.headers.get("password") elif 'username' in request.form: username = request.form.get("username") password = request.form.get("password") elif 'restapi_username' in request.form: username = request.form.get("restapi_username") password = request.form.get("restapi_password") log.create_logger(__name__).info(f"{username} {password}") session_id = AppInfo.login(username, password) log.create_logger(__name__).info(f"{request.accept_mimetypes}") if session_id == None: if next == None: abort(400, 'wrong username or password') else: return redirect( f"/ui/login?redirect={next}&msg=Wrong username or password", code=302) else: session['session_id'] = session_id g.context = AppInfo.create_context(session_id) if next == None: response = make_response({ "session_id": session_id, "status": "logged_on" }) response.headers['content-type'] = 'text/json' return response else: return redirect(next, code=302)
class Logoff(Resource): api=AppInfo.get_api() @api.doc(parser=create_parser()) def post(self): context=g.context session_id='' if 'session_id' in session: session_id=session['session_id'] AppInfo.logoff(context) session.clear() next = HTTPRequest.redirect(request) if next==None: response = make_response({"session_id": session_id, "status":"logged_off"}) response.headers['content-type'] = 'text/json' return response else: return redirect(next, code=302)
class EntityAdd(Resource): api = AppInfo.get_api() @api.doc(parser=create_parser()) def post(self, table): try: context = g.context json = request.form.to_dict() fetch = build_fetchxml_by_alias(context, table, None, json, type="insert") fetchparser = FetchXmlParser(fetch, context) rs = DatabaseServices.exec(fetchparser, context, fetch_mode=0) result = { "rows_affected": rs.get_cursor().rowcount, "inserted_id": rs.get_inserted_id() } next = HTTPRequest.redirect( request, default=f"/ui/v1.0/data/{table}/{rs.get_inserted_id()}", id=rs.get_inserted_id()) return redirect(next, code=302) except RestApiNotAllowed as err: logger.info(f"RestApiNotAllowed Exception: {err}") return redirect(f"/ui/login?redirect=/ui/v1.0/data/{table}", code=302) except Exception as err: return make_response( JinjaTemplate.render_status_template(context, 500, err), 500)
class Portal(Resource): api = AppInfo.get_api("portal") @api.doc(parser=create_parser()) def get(self, path="index.htm", session_id=None, content_name=None): try: logger.info(f"Path: {path}") logger.info(f"content_name: {content_name}") logger.info(f"session_id: {session_id}") create_parser().parse_args() context = g.context portal_id = "default" # # content class # if not path.endswith("login.htm"): pass # # render the defined jinja template (in case ofahtm file) # if path.endswith('.htm'): next = HTTPRequest.redirect(request) logger.info(f"Redirect : {next}") fetch = build_fetchxml_by_alias(context, "api_portal", portal_id, type="select") fetchparser = FetchXmlParser(fetch, context) rs = DatabaseServices.exec(fetchparser, context, fetch_mode=1) if rs.get_result() == None: abort(404, "Portal not found => %s" % id) # render the content first if content_name == None: content_name = HTTPRequest.get_querystring_value( request, "content_name", default="home") content = self.__get_content(context, portal_id, content_name) if content == None: abort(404, "Content not found => %s" % content_name) params = { "context": context, "content_name": content_name, "content_id": content['id'] } template = JinjaTemplate.create_string_template( context, content['content'].encode('utf-8').decode('utf-8')) content_str = template.render(params) # # Render the complete page # params['content'] = content_str template = JinjaTemplate.create_file_template(context, path) page_str = template.render(params) response = make_response(page_str) response.headers['content-type'] = 'text/html' return response else: www_root = FileSystemTools.format_path( AppInfo.get_current_config('ui', 'wwwroot', exception=True)) return send_file(f"{www_root}{path}") except ConfigNotValid as err: logger.exception(f"Config not valid {err}") return make_response( JinjaTemplate.render_status_template(context, 500, err), 500) except jinja2.exceptions.TemplateNotFound as err: logger.exception(f"TemplateNotFound: {err}") return make_response( JinjaTemplate.render_status_template( context, 404, f"Template not found {err}"), 404) except FileNotFoundError as err: logger.exception(f"FileNotFound: {err}") return make_response( JinjaTemplate.render_status_template(context, 404, f"File not found {err}"), 404) except RestApiNotAllowed as err: logger.exception(f"RestApiNotAllowed Exception: {err}") return redirect(f"/ui/login?redirect=/{path}", code=302) except Exception as err: logger.exception(f"Exception: {err}") return make_response( JinjaTemplate.render_status_template(context, 500, err), 500) def __get_content(self, context, portal_id, content_name): fetch = f""" <restapi type="select"> <table name="api_portal_content"/> <filter type="and"> <condition field="name" value="{content_name}" operator="="/> <condition field="portal_id" value="{portal_id}" operator="="/> </filter> </restapi> """ fetchparser = FetchXmlParser(fetch, context) rs_content = DatabaseServices.exec(fetchparser, context, run_as_system=True, fetch_mode=1) if rs_content.get_eof(): return None return rs_content.get_result()
else: guest=AppInfo.guest_credentials() username=guest['username'] password=guest['password'] session_id=AppInfo.login(username,password) if session_id==None: print(f"try to login with username: {username}") abort(400,'Wrong username or password') g.context=AppInfo.create_context(session_id, auto_logoff=True) for arg in request.args: g.context.set_arg(arg, request.args[arg]) AppInfo.get_api().add_resource(api.core.login.get_endpoint() ,"/v1.0/core/login") AppInfo.get_api().add_resource(api.core.logoff.get_endpoint() ,"/v1.0/core/logoff") AppInfo.get_api().add_resource(api.data.entity.get_endpoint(),"/v1.0/data/<table>/<id>", methods=['GET','PUT','DELETE']) AppInfo.get_api().add_resource(api.data.entity.get_endpoint(),"/v1.0/data/<table>/<id>/<field>", methods=['GET']) AppInfo.get_api().add_resource(api.data.entitylistfilter.get_endpoint(),"/v1.0/data", methods=['POST']) AppInfo.get_api().add_resource(api.data.entityadd.get_endpoint(),"/v1.0/data/<table>", methods=['POST']) AppInfo.get_api().add_resource(api.data.entityset.get_endpoint(),"/v1.0/data/<table>", methods=['GET']) AppInfo.get_api().add_resource(api.data.file.get_endpoint(), "/v1.0/file/<path:path>", methods=['POST','GET','PUT']) AppInfo.get_api().add_resource(api.action.get_endpoint(), "/v1.0/action/<action>") AppInfo.get_api().add_resource(api.form.entityupdate.get_endpoint(), "/v1.0/form/<table>/<id>", methods=['POST']) AppInfo.get_api().add_resource(api.form.entityinsert.get_endpoint(), "/v1.0/form/<table>", methods=['POST'])
class PostFile(Resource): api = AppInfo.get_api() @api.doc(parser=create_parser()) def get(self, path): try: context = g.context connection = context.get_connection() file = File() result = file.read_file(context, path) if result == None: return make_response( { "status": "Err", "error": f"File not found: {path}" }, 404) response = make_response(result['file']) response.headers.set('Content-Type', result['mime_type']) #response.headers.set('Content-Disposition', 'attachment', filename='test.jpg') response.headers.set('Content-Disposition', 'inline', filename=result['name']) return response except Exception as err: logger.exception(f"Exception: {err}") return make_response({"status": "Err", "error": str(err)}, 500) @api.doc(parser=create_parser()) def post(self, path): try: context = g.context connection = context.get_connection() result = [] for f in request.files.getlist('file'): file = File() file.create_file(context, f, path) result.append({ "status": "OK", "file_id": file.get_file_id(), "remote_path": path }) return make_response(dict({ "results": result, "status": "ok" }), 200) except Exception as err: logger.exception(f"Exception: {err}") return make_response({"status": "Err", "error": str(err)}, 500) @api.doc(parser=create_parser()) def put(self, path): try: context = g.context connection = context.get_connection() result = [] for f in request.files.getlist('file'): file = File() file.update_file(context, f, path) result.append({ "status": "OK", "file_id": file.get_file_id(), "remote_path": path }) return make_response(dict({ "results": result, "status": "ok" }), 200) except FileNotFoundInDatabase as err: logger.exception(f"FileNotFoundInDatabase: {err}") return make_response({"status": "Err", "error": str(err)}, 404) except Exception as err: logger.exception(f"Exception: {err}") return make_response({"status": "Err", "error": str(err)}, 500)
class EntityList(Resource): api = AppInfo.get_api("ui") @api.doc(parser=create_parser()) def get(self, table, view="default"): try: parser = create_parser().parse_args() context = g.context logger.info(f"app_id: {request.url}") file = f"templates/base/datalist.htm" query = "%" if 'query' in request.args: query = f"%{request.args.get('query')}%" table_meta = read_table_meta(context, alias=table) view_meta = read_table_view_meta(context, table_meta['id'], view, 'LISTVIEW') fetch = view_meta['fetch_xml'] #columns=json.loads(view_meta['col_definition']) fetch = fetch.replace("$$query$$", query) fetchparser = FetchXmlParser(fetch, context) rs = DatabaseServices.exec(fetchparser, context, fetch_mode=0) template = JinjaTemplate.create_file_template(context, file) response = make_response( template.render({ "data": rs.get_result(), "columns": fetchparser.get_columns(), "table": table, "table_meta": table_meta, "view_meta": view_meta, "pagemode": "dataformlist", "context": context })) response.headers['content-type'] = 'text/html' return response except ConfigNotValid as err: logger.exception(f"Config not valid {err}") return make_response( JinjaTemplate.render_status_template(context, 500, err), 500) except DataViewNotFound as err: logger.exception(f"Dataview on api_table_view not found: {err}") return make_response( JinjaTemplate.render_status_template(context, 500, "Dataview not found!"), 500) except jinja2.exceptions.TemplateNotFound as err: logger.exception(f"TemplateNotFound: {err}") return make_response( JinjaTemplate.render_status_template( context, 404, f"Template not found {err}"), 404) except FileNotFoundError as err: logger.exception(f"FileNotFound: {err}") return make_response( JinjaTemplate.render_status_template(context, 404, f"File not found {err}"), 404) except RestApiNotAllowed as err: logger.exception(f"RestApiNotAllowed Exception: {err}") #return redirect(f"/ui/login?redirect=/ui/v1.0/data/view/{table}/default", code=302) return redirect(f"/ui/login?redirect={request.url}", code=302) except Exception as err: logger.exception(f"Exception: {err}") return make_response( JinjaTemplate.render_status_template(context, 500, err), 500)
class Entity(Resource): api=AppInfo.get_api() @api.doc(parser=create_parser_delete()) def delete(self,table,id): try: create_parser_delete().parse_args() context=g.context fetch=build_fetchxml_by_alias(context,table,id, type="delete") fetchparser=FetchXmlParser(fetch, context) rs=DatabaseServices.exec(fetchparser,context, fetch_mode=0) result={"rows_affected": rs.get_cursor().rowcount} rs.close() return result except RestApiNotAllowed as err: abort(400, f"{err}") except Exception as err: abort(500,f"{err}") @api.doc(parser=create_parser_put()) def put(self,table,id): try: create_parser_put().parse_args() context=g.context if request.json==None: abort(400, "cannot extract json data in body %s %s" % (table,id)) fetch=build_fetchxml_by_alias(context, table, id, request.json, type="update") fetchparser=FetchXmlParser(fetch, context) rs=DatabaseServices.exec(fetchparser,context, fetch_mode=0) result={"rows_affected": rs.get_cursor().rowcount} rs.close() return result except RestApiNotAllowed as err: abort(400, f"{err}") except Exception as err: abort(500,f"{err}") @api.doc(parser=create_parser_get()) def get(self,table,id,field=""): try: create_parser_get().parse_args() context=g.context fetch=build_fetchxml_by_alias(context, table, id, type="select") fetchparser=FetchXmlParser(fetch, context) rs=DatabaseServices.exec(fetchparser,context, fetch_mode=1) if rs.get_result()==None: abort(400, "Item not found => %s" % id) if field=="": return rs.get_result() else: return rs.get_result()[field] except RestApiNotAllowed as err: abort(400, f"{err}") except Exception as err: abort(500,f"{err}")
class DataFormUpdate(Resource): api = AppInfo.get_api("ui") @api.doc(parser=create_parser()) def get(self, table, id): try: create_parser().parse_args() context = g.context table_meta = read_table_meta(context, alias=table) solution_id = table_meta['solution_id'] if solution_id == 1: file = f"templates/{table}_update.htm" else: file = f"solutions/{solution_id}/{table}_update.htm" fetch = build_fetchxml_by_alias(context, table, id, type="select") fetchparser = FetchXmlParser(fetch, context) rs = DatabaseServices.exec(fetchparser, context, fetch_mode=1) if rs.get_result() == None: abort(404, "Item not found => %s" % id) # # render the defined jinja template (in case ofahtm file) # logger.info(f"Redirect : {next}") template = JinjaTemplate.create_file_template(context, file) response = make_response( template.render({ "table": table, "pagemode": "dataformupdate", "id": id, "data": rs.get_result(), "context": context })) response.headers['content-type'] = 'text/html' return response except ConfigNotValid as err: logger.exception(f"Config not valid {err}") return make_response( JinjaTemplate.render_status_template(context, 500, err), 500) except jinja2.exceptions.TemplateNotFound as err: logger.exception(f"TemplateNotFound: {err}") return make_response( JinjaTemplate.render_status_template( context, 404, f"Template not found {err}"), 404) except FileNotFoundError as err: logger.exception(f"FileNotFound: {err}") return make_response( JinjaTemplate.render_status_template(context, 404, f"File not found {err}"), 404) except RestApiNotAllowed as err: logger.exception(f"RestApiNotAllowed Exception: {err}") #return redirect(f"/auth/login.htm?redirect=/{path}", code=302) return redirect(f"/ui/login?redirect={request.url}", code=302) except Exception as err: logger.exception(f"Exception: {err}") return make_response( JinjaTemplate.render_status_template(context, 500, err), 500)