Comments (16)
Hi,
you can override _s_parse_attr_value
and parse the json there.
I will provide an example tomorrow.
from safrs.
thank you. I should mention I'm getting this associated error:
WARNING:Could not match json datatype for db column type TIME
, using "string" for tceContacts.eventTime
from safrs.
Ok, I think the error comes from the swagger generation, not from the api.
Does your column look like this?
created = db.Column(db.Time)
if so, can you try to add a "default" value and see if it works? eg.
created = db.Column(db.Time, default=datetime.time())
from safrs.
I've commited a fix for this.
Can you check if your code works with the latest version?
from safrs.
Thank you. adding default=datetime.time() got me past the "time" error.
Now I get this error: TypeError: Object of type 'Decimal' is not JSON serializable
I believe it is because of these column types
rate = Column(Numeric(6, 4))
balance = Column(Numeric(10, 4))
from safrs.
Hi,
I can't reproduce with this:
#!/usr/bin/env python
# run:
# $ FLASK_APP=mini_app flask run
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from safrs import SAFRSBase, SAFRSAPI
import datetime
db = SQLAlchemy()
class User(SAFRSBase, db.Model):
"""
description: User description
"""
__tablename__ = "Users"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
email = db.Column(db.String)
rate = db.Column(db.Numeric(6, 4))
def create_api(app, HOST="localhost", PORT=5000, API_PREFIX=""):
api = SAFRSAPI(app, host=HOST, port=PORT, prefix=API_PREFIX)
api.expose_object(User)
user = User(name="test", email="[email protected]")
print("Starting API: http://{}:{}/{}".format(HOST, PORT, API_PREFIX))
def create_app(config_filename=None, host="localhost"):
app = Flask("demo_app")
app.config.update(SQLALCHEMY_DATABASE_URI="sqlite://")
db.init_app(app)
with app.app_context():
db.create_all()
create_api(app, host)
return app
host = "127.0.0.1"
app = create_app(host=host)
if __name__ == "__main__":
app.run(host=host)
Can you send a full stack traces please?
from safrs.
Ok. The error occurs when I expose this class
api.expose_object(TceContactsHst)
class TceContactsHst(Base):
__tablename__ = 'tceContactsHst'
contactsId = Column(ForeignKey(u'tceContacts.id'), index=True)
runStatus = Column(String(45))
runInterrupted = Column(DateTime)
batchId = Column(ForeignKey(u'tceBatch.id'), index=True)
processId = Column(Integer)
process = Column(String(45))
rate = Column(Numeric(6, 4))
balance = Column(Numeric(10, 4))
type = Column(String(45))
sid = Column(String(145))
phone = Column(String(45))
duration = Column(Numeric(6, 2))
numSegments = Column(Integer)
price = Column(Numeric(10, 6))
priceunit = Column(String(45))
error_code = Column(String(45))
error_message = Column(String(150))
uri = Column(String(150))
cost = Column(Numeric(10, 6))
tceContacts = relationship(u'TceContacts')
tceBatch = relationship(u'TceBatch')
[2020-04-23 11:05:18,455] base:863 WARNING: Could not match json datatype for db column type `TIME`, using "string" for tceContacts.eventTime
WARNING:Could not match json datatype for db column type `TIME`, using "string" for tceContacts.eventTime
[2020-04-23 11:05:19,099] base:863 WARNING: Could not match json datatype for db column type `TIME`, using "string" for tceContacts.eventTime
WARNING:Could not match json datatype for db column type `TIME`, using "string" for tceContacts.eventTime
[2020-04-23 11:05:20,186] base:863 WARNING: Could not match json datatype for db column type `TIME`, using "string" for tceContacts.eventTime
WARNING:Could not match json datatype for db column type `TIME`, using "string" for tceContacts.eventTime
[2020-04-23 11:05:20,216] _api:107 INFO: Exposing TceContacts on /tceContacts/, endpoint: api.TceContacts
INFO:Exposing TceContacts on /tceContacts/, endpoint: api.TceContacts
[2020-04-23 11:05:20,238] _api:115 INFO: Exposing tceContacts instances on /tceContacts/<string:TceContactsId>/, endpoint: api.TceContactsId
INFO:Exposing tceContacts instances on /tceContacts/<string:TceContactsId>/, endpoint: api.TceContactsId
[2020-04-23 11:05:22,326] _api:219 INFO: Exposing relationship tceCampRun on /tceContacts/<string:TceContactsId>/tceCampRun, endpoint: /tceContacts/<string:TceContactsId>/api.tceCampRun
INFO:Exposing relationship tceCampRun on /tceContacts/<string:TceContactsId>/tceCampRun, endpoint: /tceContacts/<string:TceContactsId>/api.tceCampRun
[2020-04-23 11:05:22,335] _api:244 INFO: Exposing TceContacts relationship tceCampRun on /tceContacts/<string:TceContactsId>/tceCampRun/<string:TceCampRunId>, endpoint: /tceContacts/<string:TceContactsId>/api.tceCampRunId
INFO:Exposing TceContacts relationship tceCampRun on /tceContacts/<string:TceContactsId>/tceCampRun/<string:TceCampRunId>, endpoint: /tceContacts/<string:TceContactsId>/api.tceCampRunId
[2020-04-23 11:05:24,662] _api:107 INFO: Exposing TceContactsHst on /tceContactsHst/, endpoint: api.TceContactsHst
INFO:Exposing TceContactsHst on /tceContactsHst/, endpoint: api.TceContactsHst
[2020-04-23 11:05:24,688] _api:115 INFO: Exposing tceContactsHst instances on /tceContactsHst/<string:TceContactsHstId>/, endpoint: api.TceContactsHstId
INFO:Exposing tceContactsHst instances on /tceContactsHst/<string:TceContactsHstId>/, endpoint: api.TceContactsHstId
[2020-04-23 11:05:25,244] base:863 WARNING: Could not match json datatype for db column type `TIME`, using "string" for tceContacts.eventTime
WARNING:Could not match json datatype for db column type `TIME`, using "string" for tceContacts.eventTime
[2020-04-23 11:05:25,820] base:863 WARNING: Could not match json datatype for db column type `TIME`, using "string" for tceContacts.eventTime
WARNING:Could not match json datatype for db column type `TIME`, using "string" for tceContacts.eventTime
[2020-04-23 11:05:26,400] base:863 WARNING: Could not match json datatype for db column type `TIME`, using "string" for tceContacts.eventTime
WARNING:Could not match json datatype for db column type `TIME`, using "string" for tceContacts.eventTime
[2020-04-23 11:05:26,623] _api:219 INFO: Exposing relationship tceContacts on /tceContactsHst/<string:TceContactsHstId>/tceContacts, endpoint: /tceContactsHst/<string:TceContactsHstId>/api.tceContacts
INFO:Exposing relationship tceContacts on /tceContactsHst/<string:TceContactsHstId>/tceContacts, endpoint: /tceContactsHst/<string:TceContactsHstId>/api.tceContacts
[2020-04-23 11:05:26,632] _api:244 INFO: Exposing TceContactsHst relationship tceContacts on /tceContactsHst/<string:TceContactsHstId>/tceContacts/<string:TceContactsId>, endpoint: /tceContactsHst/<string:TceContactsHstId>/api.tceContactsId
INFO:Exposing TceContactsHst relationship tceContacts on /tceContactsHst/<string:TceContactsHstId>/tceContacts/<string:TceContactsId>, endpoint: /tceContactsHst/<string:TceContactsHstId>/api.tceContactsId
[2020-04-23 11:05:28,258] _api:219 INFO: Exposing relationship tceBatch on /tceContactsHst/<string:TceContactsHstId>/tceBatch, endpoint: /tceContactsHst/<string:TceContactsHstId>/api.tceBatch
INFO:Exposing relationship tceBatch on /tceContactsHst/<string:TceContactsHstId>/tceBatch, endpoint: /tceContactsHst/<string:TceContactsHstId>/api.tceBatch
[2020-04-23 11:05:28,267] _api:244 INFO: Exposing TceContactsHst relationship tceBatch on /tceContactsHst/<string:TceContactsHstId>/tceBatch/<string:TceBatchId>, endpoint: /tceContactsHst/<string:TceContactsHstId>/api.tceBatchId
INFO:Exposing TceContactsHst relationship tceBatch on /tceContactsHst/<string:TceContactsHstId>/tceBatch/<string:TceBatchId>, endpoint: /tceContactsHst/<string:TceContactsHstId>/api.tceBatchId
Starting API: http://localhost:5000
WARNING: * Debugger is active!
INFO: * Debugger PIN: 139-708-040
INFO: * Running on http://localhost:5000/ (Press CTRL+C to quit)
INFO:127.0.0.1 - - [23/Apr/2020 11:05:34] "GET / HTTP/1.1" 200 -
INFO:127.0.0.1 - - [23/Apr/2020 11:05:35] "GET /favicon-32x32.png HTTP/1.1" 200 -
INFO:127.0.0.1 - - [23/Apr/2020 11:05:35] "GET /swagger.json HTTP/1.1" 500 -
Traceback (most recent call last):
File "C:\Sites\OAuth\env\lib\site-packages\flask\app.py", line 2463, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Sites\OAuth\env\lib\site-packages\flask\app.py", line 2449, in wsgi_app
response = self.handle_exception(e)
File "C:\Sites\OAuth\env\lib\site-packages\flask_restful\__init__.py", line 272, in error_router
return original_handler(e)
File "C:\Sites\OAuth\env\lib\site-packages\flask_restful\__init__.py", line 272, in error_router
return original_handler(e)
File "C:\Sites\OAuth\env\lib\site-packages\flask\app.py", line 1866, in handle_exception
reraise(exc_type, exc_value, tb)
File "C:\Sites\OAuth\env\lib\site-packages\flask\_compat.py", line 38, in reraise
raise value.with_traceback(tb)
File "C:\Sites\OAuth\env\lib\site-packages\flask\app.py", line 2446, in wsgi_app
response = self.full_dispatch_request()
File "C:\Sites\OAuth\env\lib\site-packages\flask\app.py", line 1951, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Sites\OAuth\env\lib\site-packages\flask_restful\__init__.py", line 272, in error_router
return original_handler(e)
File "C:\Sites\OAuth\env\lib\site-packages\flask_restful\__init__.py", line 272, in error_router
return original_handler(e)
File "C:\Sites\OAuth\env\lib\site-packages\flask\app.py", line 1820, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "C:\Sites\OAuth\env\lib\site-packages\flask\_compat.py", line 38, in reraise
raise value.with_traceback(tb)
File "C:\Sites\OAuth\env\lib\site-packages\flask\app.py", line 1949, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Sites\OAuth\env\lib\site-packages\flask\app.py", line 1935, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\Sites\OAuth\env\lib\site-packages\flask_restful\__init__.py", line 472, in wrapper
return self.make_response(data, code, headers=headers)
File "C:\Sites\OAuth\env\lib\site-packages\flask_restful\__init__.py", line 501, in make_response
resp = self.representations[mediatype](data, *args, **kwargs)
File "C:\Sites\OAuth\env\lib\site-packages\flask_restful\representations\json.py", line 21, in output_json
dumped = dumps(data, **settings) + "\n"
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\__init__.py", line 238, in dumps
**kw).encode(obj)
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\encoder.py", line 201, in encode
chunks = list(chunks)
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\encoder.py", line 430, in _iterencode
yield from _iterencode_dict(o, _current_indent_level)
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\encoder.py", line 404, in _iterencode_dict
yield from chunks
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\encoder.py", line 404, in _iterencode_dict
yield from chunks
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\encoder.py", line 404, in _iterencode_dict
yield from chunks
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\encoder.py", line 404, in _iterencode_dict
yield from chunks
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\encoder.py", line 404, in _iterencode_dict
yield from chunks
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\encoder.py", line 325, in _iterencode_list
yield from chunks
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\encoder.py", line 404, in _iterencode_dict
yield from chunks
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\encoder.py", line 404, in _iterencode_dict
yield from chunks
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\encoder.py", line 437, in _iterencode
o = _default(o)
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\encoder.py", line 180, in default
o.__class__.__name__)
TypeError: Object of type 'Decimal' is not JSON serializable
from safrs.
Can you check if it works by providing a default column value?
I will try to reproduce this tomorrow.
from safrs.
Sorry, I should have tied that.
duration = Column(Numeric(6, 2), default = 0)
adding a default of 0 solves the runtime errors, but I don't want to fill up the database with zeros where null should be. Any suggestions
from safrs.
Can you try again please?
The swagger generator didn't use the app's json encoder (i.e. the encoder used for api serialization). It does now.
There's still no support for the Decimal type but we convert to str for unknown types now so it won't generate an exception.
You will see a warning though, if you want to avoid this warning, you may create a custom json encoder and pass it as an argument to
SAFRSAPI(.. json_encoder=YourEncoder)
from safrs.
Ok sounds good. I will try it out.
I have another question. I am integrating SAFRS with OAuth with this example https://github.com/authlib/example-oauth2-server
Following your example, in demo_jwt.py I have the code below working.
- I'm not sure what code to use instead of "jwt_required" in the custom_decorators = [jwt_required, test_dec]
OAuth uses require_oauth = ResourceProtector()
https://github.com/authlib/example-oauth2-server/blob/master/website/oauth2.py
I've been trying something like "custom_decorators = [require_oauth, test_dec]" but it causes a circular error.
- what class do I implement the custom custom_decorators? The base?
api._swagger_object["securityDefinitions"] = {
"Bearer": {'type': 'oauth2', 'flow': 'application',
'tokenUrl': myApi+'/oauth/token',
'scopes': {
'read': 'Grants read access',
'write': 'Grants write access',
'admin': 'Grants read and write access to administrative information',
'profile': 'all'
},
}}
from safrs.
I changed the custom_decorators
to just decorators
, you can use it like this:
def test_decorator(func):
""" Example flask-restful decorator that can be used in the "decorators" Api argument
cfr. https://flask-restful.readthedocs.io/en/latest/api.html#id1
"""
def api_wrapper(*args, **kwargs):
print(args, kwargs)
return func(*args, **kwargs)
if func.__name__.lower() == "get":
result = api_wrapper
result.__name__ = func.__name__ # make sure to to reset the __name__ !
return result
return func
class User(SAFRSBase, db.Model):
"""
description: User description
"""
decorators = [test_decorator]
__tablename__ = "Users"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
email = db.Column(db.String)
so you can implement authorization checks in the api_wrapper
. However, I have to warn you that implementing fine-grained access control can be quite tricky: you have to verify the different paths and take care of relationships too.
I don't know where you get the circular error, the jwt example is from a while ago and things have changed since then.
I'll try to implement another demo soon.
from safrs.
Your demo is fine. no circular error.
In my app, I am not using flask_jwt_extended to create the token. the token is created and saved in the model class OAuth2Token. Therefore, I could not find a way to use "jwt_required" in the custom_decorators. The circular reference is caused by my trying to use a model class query to check the validity of the token. I hope this makes sense to you.
I do not desire more fine-grained access control. My problem is how to check if a token supplied exist in the database and is still valid.
I will review your exam above and see if it will do the trick.
Thank you
class OAuth2TokenMixin(TokenMixin):
client_id = Column(String(48))
token_type = Column(String(40))
access_token = Column(String(255), unique=True, nullable=False)
refresh_token = Column(String(255), index=True)
scope = Column(Text, default='')
revoked = Column(Boolean, default=False)
issued_at = Column(
Integer, nullable=False, default=lambda: int(time.time())
)
expires_in = Column(Integer, nullable=False, default=0)
def get_client_id(self):
return self.client_id
def get_scope(self):
return self.scope
def get_expires_in(self):
return self.expires_in
def get_expires_at(self):
return self.issued_at + self.expires_in
OAuth2Token(db.Model, OAuth2TokenMixin):
__tablename__ = 'oauth2_token'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(
db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'))
user = db.relationship('User')
def is_refresh_token_active(self):
if self.revoked:
return False
expires_at = self.issued_at + self.expires_in * 2
return expires_at >= time.time()
from safrs.
Hi,
The code changes worked. I removed the default values in the model without error.
FYI: I have a database field titled type. it's giving this warning.
[2020-04-27 12:31:17,023] base:940 DEBUG: (): attribute name "type" is reserved, renamed to "Type"
DEBUG:(): attribute name "type" is reserved, renamed to "Type"
I am updating my authentication code to use flask_jwt_extended instead of the other token logic. When I run examples\authentication\demo_jwt.py I get this error: TypeError: 'module' object is not callable How do I resolve this error?
Traceback (most recent call last):
File "C:\Sites\safrs2\safrs\_api.py", line 468, in api_decorator
decorated_method = swagger_decorator(decorated_method)
File "C:\Sites\safrs2\safrs\swagger_doc.py", line 484, in swagger_doc_gen
rel_post_schema = schema_from_object("{}_Relationship".format(class_name), {"data": data})
File "C:\Sites\safrs2\safrs\swagger_doc.py", line 198, in schema_from_object
properties = encode_schema(properties)
File "C:\Sites\safrs2\safrs\swagger_doc.py", line 154, in encode_schema
log.warning("Json encoding failed for {}, type {} ({})".format(obj, type(obj), exc))
NameError: name 'log' is not defined
[2020-04-27 12:22:37,736] _api:475 ERROR: Failed to generate documentation for <function SAFRSRestRelationshipAPI.post at 0x051731E0>
[2020-04-27 12:22:37,784] _api:474 ERROR: name 'log' is not defined
<function SAFRSRestRelationshipAPI.delete at 0x051738A0> delete
Traceback (most recent call last):
File "C:\Sites\safrs2\safrs\swagger_doc.py", line 152, in encode_schema
result = json.loads(json.dumps(obj, cls=flask.current_app.json_encoder))
File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\__init__.py", line 238, in dumps
**kw).encode(obj)
TypeError: 'module' object is not callable
from safrs.
Hi,
can you open new issues for new problems please?
-
type can't be used as an attribute name because it's a top level member in the json:api spec
https://jsonapi.org/format/#document-resource-object-fields -
I'll check the jwt demo tomorrow
from safrs.
Okay, thank you. This issue is closed. I will open a separate issue for TypeError: 'module' object is not callable.
from safrs.
Related Issues (20)
- Invalid swagger for "properties" under "definitions" breaks defintions display and validation HOT 7
- Patch method unreliable? HOT 2
- Read-only Columns HOT 2
- Flask-SQLAlchemy weakref KeyError
- in version 3.0 _s_count function does not apply filters to query HOT 1
- `count` property value is wrong for `GET` methods HOT 1
- Unable to delete entries HOT 5
- overriding methods does not work HOT 2
- extend API with custom classes HOT 1
- requirements.txt too restrictive HOT 1
- Duplicate definitions generated HOT 5
- safrs doesn't work in python 3.9 - TypeError HOT 4
- expose_existing starts but no swagger.json found HOT 1
- Run in example demo_relationship.py creating users or books in flask_admin returns Integrity Errors HOT 17
- Doubts about SAFRS HOT 2
- JSONAPI relationship missing in the swagger documentation HOT 4
- Swagger generation removing plurals HOT 1
- Flask 2.0 / Flask-restful "ImportError: cannot import name '_endpoint_from_view_func'" HOT 1
- Possible issue with BIGINT primary keys? HOT 5
- Make many to many relationship methods extendable HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from safrs.