Code Monkey home page Code Monkey logo

Comments (16)

thomaxxl avatar thomaxxl commented on August 13, 2024

Hi,

you can override _s_parse_attr_value and parse the json there.
I will provide an example tomorrow.

from safrs.

NexPlex avatar NexPlex commented on August 13, 2024

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.

thomaxxl avatar thomaxxl commented on August 13, 2024

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.

thomaxxl avatar thomaxxl commented on August 13, 2024

I've commited a fix for this.
Can you check if your code works with the latest version?

from safrs.

NexPlex avatar NexPlex commented on August 13, 2024

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.

thomaxxl avatar thomaxxl commented on August 13, 2024

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.

NexPlex avatar NexPlex commented on August 13, 2024

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.

thomaxxl avatar thomaxxl commented on August 13, 2024

Can you check if it works by providing a default column value?

I will try to reproduce this tomorrow.

from safrs.

NexPlex avatar NexPlex commented on August 13, 2024

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.

thomaxxl avatar thomaxxl commented on August 13, 2024

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.

NexPlex avatar NexPlex commented on August 13, 2024

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.

  1. 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.

  1. 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.

thomaxxl avatar thomaxxl commented on August 13, 2024

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.

NexPlex avatar NexPlex commented on August 13, 2024

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.

NexPlex avatar NexPlex commented on August 13, 2024

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.

thomaxxl avatar thomaxxl commented on August 13, 2024

Hi,

can you open new issues for new problems please?

from safrs.

NexPlex avatar NexPlex commented on August 13, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.