Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
# Following method is exposed through the REST API
# This means it can be invoked with a HTTP POST
@classmethod
@jsonapi_rpc(http_methods=["POST"])
def send_mail(self, **args):
"""
description : Send an email
args:
email:
type : string
example : test email
"""
return {"result": args}
class Book(SAFRSBase, db.Model):
"""
description: Book description
"""
__tablename__ = "Books"
id = db.Column(db.String, primary_key=True)
name = db.Column(db.String, default="")
user_id = db.Column(db.String, db.ForeignKey("Users.id"))
user = db.relationship("User", back_populates="books")
def populate_db():
"""
Add some items to the db
"""
user0 = User(name="user0", email="em@il0")
user1 = User(name="user2", email="em@il2", books=[book2])
if __name__ == "__main__":
HOST = sys.argv[1] if len(sys.argv) > 1 else "0.0.0.0"
PORT = 5000
app = Flask("SAFRS Demo Application")
app.config.update(SQLALCHEMY_DATABASE_URI="sqlite://", DEBUG=True)
db.init_app(app)
db.app = app
# Create the database
db.create_all()
API_PREFIX = ""
with app.app_context():
api = SAFRSAPI(app, host="{}:{}".format(HOST, PORT), port=PORT, prefix=API_PREFIX)
populate_db()
# Expose the database objects as REST API endpoints
api.expose_object(User)
api.expose_object(Book)
# Register the API at /api/docs
print("Starting API: http://{}:{}{}".format(HOST, PORT, API_PREFIX))
app.run(host=HOST, port=PORT)
@jsonapi_rpc(http_methods=["POST"])
def send_mail(self, **args):
"""
description : Send an email
args:
email:
type : string
example : test email
"""
return {"result": args}
Parse the yaml description from the documented methods
"""
api_doc = {}
obj_doc = str(inspect.getdoc(object))
raw_doc = obj_doc.split(DOC_DELIMITER)[0]
yaml_doc = None
try:
yaml_doc = yaml.safe_load(raw_doc)
except (SyntaxError, yaml.scanner.ScannerError) as exc:
safrs.log.error("Failed to parse documentation {} ({})".format(raw_doc, exc))
yaml_doc = {"description": raw_doc}
except Exception as exc:
raise ValidationError("Failed to parse api doc")
if isinstance(yaml_doc, dict):
api_doc.update(yaml_doc)
return api_doc
del links["last"]
if first_args == self_args:
del links["first"]
if next_args == last_args:
del links["next"]
if prev_args == first_args:
del links["prev"]
if isinstance(object_query, (list, sqlalchemy.orm.collections.InstrumentedList)):
instances = object_query[page_offset : page_offset + limit]
else:
try:
res_query = object_query.offset(page_offset).limit(limit)
instances = res_query.all()
except OverflowError:
raise ValidationError("Pagination Overflow Error")
except Exception as exc:
raise GenericError("Pagination Error {}".format(exc))
return links, instances, count
# the SAFRSObject should've implemented a filter method or
# overwritten the _s_filter method to implement custom filtering
filter_args = get_request_param("filter")
if filter_args:
safrs_object_filter = getattr(safrs_object, "filter", None)
if callable(safrs_object_filter):
result = safrs_object_filter(filter_args)
else:
result = safrs_object._s_filter(filter_args)
return result
expressions = []
filters = get_request_param("filters", {})
for attr_name, val in filters.items():
if not attr_name in safrs_object._s_jsonapi_attrs + ["id"]:
safrs.log.warning("Invalid filter {}".format(attr_name))
continue
attr = getattr(safrs_object, attr_name)
expressions.append((attr, val))
if isinstance(safrs_object, (list, sqlalchemy.orm.collections.InstrumentedList)):
# todo: filter properly
result = safrs_object
elif expressions:
expressions_ = [column.in_(val.split(",")) for column, val in expressions]
result = safrs_object._s_query.filter(*expressions_)
else:
result = safrs_object._s_query
return result
def parse_object_doc(object):
"""
Parse the yaml description from the documented methods
"""
api_doc = {}
obj_doc = str(inspect.getdoc(object))
raw_doc = obj_doc.split(DOC_DELIMITER)[0]
yaml_doc = None
try:
yaml_doc = yaml.safe_load(raw_doc)
except (SyntaxError, yaml.scanner.ScannerError) as exc:
safrs.log.error("Failed to parse documentation {} ({})".format(raw_doc, exc))
yaml_doc = {"description": raw_doc}
except Exception as exc:
raise ValidationError("Failed to parse api doc")
if isinstance(yaml_doc, dict):
api_doc.update(yaml_doc)
return api_doc
# Data is optional, it's also really slow for large sets!!!!!
rel_query = getattr(self, rel_name)
limit = request.page_limit
if not get_config("ENABLE_RELATIONSHIPS"):
meta["warning"] = "ENABLE_RELATIONSHIPS set to false in config.py"
elif rel_query:
# todo: chekc if lazy=dynamic
# In order to work with the relationship as with Query,
# you need to configure it with lazy='dynamic'
# "limit" may not be possible !
if getattr(rel_query, "limit", False):
count = rel_query.count()
rel_query = rel_query.limit(limit)
if rel_query.count() >= get_config("BIG_QUERY_THRESHOLD"):
warning = 'Truncated result for relationship "{}",consider paginating this request'.format(rel_name)
safrs.log.warning(warning)
meta["warning"] = warning
items = rel_query.all()
else:
items = list(rel_query)
count = len(items)
meta["count"] = count
meta["limit"] = limit
data = [{"id": i.jsonapi_id, "type": i._s_type} for i in items]
else: # shouldn't happen!!
safrs.log.error("Unknown relationship direction for relationship {}: {}".format(rel_name, relationship.direction))
# add the relationship direction, for debugging purposes.
if is_debug():
# meta["direction"] = relationship.direction.name
pass
rel_link = urljoin(self_link, rel_name)
data = {"id": rel_item.jsonapi_id, "type": rel_item._s_type}
elif relationship.direction in (ONETOMANY, MANYTOMANY):
# Data is optional, it's also really slow for large sets!!!!!
rel_query = getattr(self, rel_name)
limit = request.page_limit
if not get_config("ENABLE_RELATIONSHIPS"):
meta["warning"] = "ENABLE_RELATIONSHIPS set to false in config.py"
elif rel_query:
# todo: chekc if lazy=dynamic
# In order to work with the relationship as with Query,
# you need to configure it with lazy='dynamic'
# "limit" may not be possible !
if getattr(rel_query, "limit", False):
count = rel_query.count()
rel_query = rel_query.limit(limit)
if rel_query.count() >= get_config("BIG_QUERY_THRESHOLD"):
warning = 'Truncated result for relationship "{}",consider paginating this request'.format(rel_name)
safrs.log.warning(warning)
meta["warning"] = warning
items = rel_query.all()
else:
items = list(rel_query)
count = len(items)
meta["count"] = count
meta["limit"] = limit
data = [{"id": i.jsonapi_id, "type": i._s_type} for i in items]
else: # shouldn't happen!!
safrs.log.error("Unknown relationship direction for relationship {}: {}".format(rel_name, relationship.direction))
# add the relationship direction, for debugging purposes.
if is_debug():
# meta["direction"] = relationship.direction.name
pass
safrs.DB.session.commit()
return result
except (ValidationError, GenericError, NotFoundError) as exc:
safrs.log.exception(exc)
status_code = getattr(exc, "status_code")
message = exc.message
except werkzeug.exceptions.NotFound:
status_code = 404
message = "Not Found"
except Exception as exc:
status_code = getattr(exc, "status_code", 500)
traceback.print_exc()
safrs.log.error(exc)
if safrs.log.getEffectiveLevel() > logging.DEBUG:
message = "Logging Disabled"
else:
message = str(exc)
safrs.DB.session.rollback()
safrs.log.error(message)
errors = dict(detail=message)
abort(status_code, errors=[errors])