Code Monkey home page Code Monkey logo

dedoussis / asynction Goto Github PK

View Code? Open in Web Editor NEW
46.0 5.0 5.0 279 KB

SocketIO python framework driven by the AsyncAPI specification. Built on top of Flask-SocketIO. Inspired by Connexion.

Home Page: https://pypi.org/project/asynction/

License: MIT License

Makefile 0.98% Python 93.60% Dockerfile 0.57% JavaScript 3.30% HTML 0.37% CSS 0.73% Jinja 0.46%
websockets socketio asyncapi asyncapi-specification flask python flask-socketio connexion

asynction's People

Contributors

alex-zywicki avatar dedoussis avatar dependabot[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

asynction's Issues

Bug with array payload validation

When using:

payload:
  type: array

And sending any array to to the server I get this error.

Traceback (most recent call last):
 File "/usr/local/lib/python3.9/site-packages/asynction/validation.py", line 26, in jsonschema_validate_with_error_handling
   jsonschema.validate(instance, schema)
 File "/usr/local/lib/python3.9/site-packages/jsonschema/validators.py", line 967, in validate
   raise error
jsonschema.exceptions.ValidationError: ([],) is not of type 'array'
Failed validating 'type' in schema:
   {'type': 'array'}
On instance:
   ([],)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
 File "src/gevent/greenlet.py", line 906, in gevent._gevent_cgreenlet.Greenlet.run
 File "/usr/local/lib/python3.9/site-packages/socketio/server.py", line 720, in _handle_event_internal
   r = server._trigger_event(data[0], namespace, sid, *data[1:])
 File "/usr/local/lib/python3.9/site-packages/socketio/server.py", line 745, in _trigger_event
   return self.handlers[namespace][event](*args)
 File "/usr/local/lib/python3.9/site-packages/flask_socketio/__init__.py", line 282, in _handler
   return self._handle_event(handler, message, namespace, sid,
 File "/usr/local/lib/python3.9/site-packages/flask_socketio/__init__.py", line 766, in _handle_event
   ret = handler(*args)
 File "/usr/local/lib/python3.9/site-packages/asynction/validation.py", line 97, in handler_with_validation
   validate_payload(args, message.payload)
 File "/usr/local/lib/python3.9/site-packages/asynction/validation.py", line 57, in validate_payload
   jsonschema_validate_payload(args, schema)
 File "/usr/local/lib/python3.9/site-packages/asynction/validation.py", line 28, in jsonschema_validate_with_error_handling
   raise exc_type(str(e))
asynction.exceptions.PayloadValidationException: ([],) is not of type 'array'
Failed validating 'type' in schema:
   {'type': 'array'}
On instance:
   ([],)
2021-12-15T20:29:58Z <Thread at 0x7feeac4cc480: <bound method Server._handle_event_internal of <socketio.server.Server object at 0x7feead801700>>(<socketio.server.Server object at 0x7feead801700>, '0Fn-hKNPKV47OogIAAAF', 'HR_USU2XamBt4DyHAAAE', ['subscriptions', []], '/subscriptions/', 3)> failed with PayloadValidationException

After Some quick digging I tried changing:

def validate_payload(args: Sequence, schema: Optional[JSONSchema]) -> None:
    if schema is None:
        if args:
            raise PayloadValidationException(
                "Handler args provided for message that has no payload defined."
            )
        # Validation succeeded since there is no message payload specified
        # and no args have been provided.
        return

    schema_type = schema["type"]
    if schema_type == "array":
        jsonschema_validate_payload(args, schema)
    else:
        if len(args) > 1:
            raise PayloadValidationException(
                "Multiple handler arguments provided, "
                f"although schema type is: {schema_type}"
            )

        jsonschema_validate_payload(args[0], schema)

to

def validate_payload(args: Sequence, schema: Optional[JSONSchema]) -> None:
    if schema is None:
        if args:
            raise PayloadValidationException(
                "Handler args provided for message that has no payload defined."
            )
        # Validation succeeded since there is no message payload specified
        # and no args have been provided.
        return

    schema_type = schema["type"]
    if schema_type == "array":
        jsonschema_validate_payload(args[0], schema)  # added the [0] here
    else:
        if len(args) > 1:
            raise PayloadValidationException(
                "Multiple handler arguments provided, "
                f"although schema type is: {schema_type}"
            )

        jsonschema_validate_payload(args[0], schema)

And the error was no longer present.

Though this does seem to go against the logic in place for the special case provided there.
Why is this special case there?
This issue only seems to occur when the top level payload object type is an array.

it appears that the validator is receiving the args as a length 1 tuple where the 0th element is the actual payload.

The else case seems to handle that, but the "array" case seems to assume that the args is the array itself

docs: contribution section

Great work with this library!
I am a fan of specification first and have been using connexion for a while.
It would be great if you specified some milestones and contribution guidelines so people like me can help you.
Great work!

CI should verify python version compatibility

Problem statement

Asynction's documentation claims that the package is compatible with versions of python that are 3.7 or higher.

However, there is no check in the CI that verifies this claim.

Proposed solution

Introduce a matrix strategy in the tests GitHub actions workflow of the repository. See here for an example.

Unit and integration tests should succeed against all python3.7+ versions.

Docs viewer unhappy with Oauth2 Security scheme

When using the newly added security schemes, with the following yaml snippet.

  securitySchemes:
    oauth2:
      type: oauth2
      flows:
        implicit:
          authorizationUrl: /oauth/basic
          scopes:
            secure_ping:access: "Access SecurePing"
      x-tokenInfoFunc: token_info

I get these error messages from the /docs/ endpoint

/components/securitySchemes/oauth2 should have required property '$ref'.
/components/securitySchemes/oauth2 should NOT have additional properties
/components/securitySchemes/oauth2/type should be equal to one of the allowed values
/components/securitySchemes/oauth2 should NOT have additional properties
/components/securitySchemes/oauth2/type should be equal to one of the allowed values
/components/securitySchemes/oauth2 should have required property 'in'
/components/securitySchemes/oauth2 should NOT have additional properties
/components/securitySchemes/oauth2/type should be equal to one of the allowed values
/components/securitySchemes/oauth2 should NOT have additional properties
/components/securitySchemes/oauth2/type should be equal to one of the allowed values
/components/securitySchemes/oauth2 should NOT have additional properties
/components/securitySchemes/oauth2/type should be equal to one of the allowed values
/components/securitySchemes/oauth2 should NOT have additional properties
/components/securitySchemes/oauth2 should have required property 'scheme'
/components/securitySchemes/oauth2/type should be equal to one of the allowed values
/components/securitySchemes/oauth2 should NOT be valid
/components/securitySchemes/oauth2 should NOT have additional properties
/components/securitySchemes/oauth2 should have required property 'scheme'
/components/securitySchemes/oauth2/type should be equal to one of the allowed values
/components/securitySchemes/oauth2 should NOT have additional properties
/components/securitySchemes/oauth2/type should be equal to one of the allowed values
/components/securitySchemes/oauth2 should have required property 'name'
/components/securitySchemes/oauth2 should have required property 'in'
/components/securitySchemes/oauth2 should match exactly one schema in oneOf
/components/securitySchemes/oauth2/flows/implicit/authorizationUrl should match format "uri"
/components/securitySchemes/oauth2 should NOT have additional properties
/components/securitySchemes/oauth2/type should be equal to one of the allowed values
/components/securitySchemes/oauth2 should have required property 'openIdConnectUrl'
/components/securitySchemes/oauth2 should NOT have additional properties
/components/securitySchemes/oauth2/type should be equal to one of the allowed values
/components/securitySchemes/oauth2 should NOT have additional properties
/components/securitySchemes/oauth2/type should be equal to one of the allowed values
/components/securitySchemes/oauth2 should NOT have additional properties
/components/securitySchemes/oauth2/type should be equal to one of the allowed values
/components/securitySchemes/oauth2 should match exactly one schema in oneOf
/components/securitySchemes/oauth2 should match exactly one schema in oneOf
/components/securitySchemes/oauth2 should match exactly one schema in oneOf

Multiple asyncapi yaml files

Hi,

Connexion offers the ability to mount multiple API yaml files onto a single server. Is this a capability that asynction has or that could be added in the future? I would like to use asynction for a product I am starting on that also uses connexion, and the ability to modularize features into plugins with separate yaml files is a must for me.

Is this something you would be willing to add or willing to merge if someone were to develop it?

python3.6 support

Is there a practical reason why python3.6 is not supported? My intended use case for asynction would wind up deployed on a Red Hat linux system (RHEL7, and RHEL8) which come with python 3.6.8 as the system python. In my case it would be tricky to install a newer python for numerous reasons.

What would it take to support python3.6?

/docs does not work with asyncapi 2.2.0

Hi @dedoussis

Screen Shot 2021-10-25 at 12 06 36 PM
When running the example on the current master branch, the error Error: Version 2.2.0 is not supported. is presented.

According to this issue Version 2.2.0 should be support on the next branch of asyncapi-react.
It appears you are using version 1.0.0-next.18.
There is now a 1.0.0-next.21 available, perhaps a version bump is needed?

Document examples of using Asynction

Problem statement

The current documentation is not easy to skim through.

Proposed solution

Developers usually look for examples, or quick-start guides, in order to get a rough understanding of a piece of software.

Introduce an example of a minimal working chat application that uses Asynction, possibly under an examples top level directory. This should include the AsyncAPI spec as well as functional handlers. A very thin index.html (to represent the client side) would also be useful. The application does not need to cover the entirety of Asynction's features, however it should make use of the following core functionality:

  • Registering default namespace handlers (connect and error ones)
  • Registering custom event handlers
  • Message payload validation
  • Binding validation

The application should be accompanied with a simple to follow readme.

In the future this application could be used as part of a potential E2E testing suite.

Security schemes

Hi @dedoussis,

I noticed you listed implementing security schemes as a TODO item in your README.
Do you have any current design plans for implementing security schemes?
What would be needed to help move this feature along?

My use case for asynction would involve an oauth2 based security scheme.

I am currently using connexion's oauth2 application flow and would be aiming for something similar with asynction.

Payload validator fails with oneOf in payload

WIth a message payload that contains a oneOf

payload:
  oneOf:
     - type: array
        items:
          type: string
    - type: string

When passing an array of string I get this exception:

('[sos on_replay:49]', '------------------ traceback ------------------')
('  |', 'Traceback (most recent call last):')
('  |', '  File "/usr/lib/python3.8/site-packages/asti/sapi_flask/plugins/sos/__init__.py", line 45, in on_replay')
('  |', '    emit("sosreport", reports, to=session.sid, namespace=NAMESPACE)')
('  |', '  File "/usr/local/lib/python3.8/site-packages/flask_socketio/__init__.py", line 838, in emit')
('  |', '    return socketio.emit(event, *args, namespace=namespace, to=to,')
('  |', '  File "/usr/local/lib/python3.8/site-packages/asynction/server.py", line 286, in emit')
('  |', '    validate_payload(payload_args, message.payload)')
('  |', '  File "/usr/local/lib/python3.8/site-packages/asynction/validation.py", line 56, in validate_payload')
('  |', '    if schema["type"] == "array" and schema.get("prefixItems"):  # Tuple validation')
('  |', "KeyError: 'type'")
('[sos on_replay:49]', '---------------- end traceback ----------------')

I'm not 100% sure if changing to schema.get("type") would be correct, but the oneOf case needs to be handled somehow.
From what I can tell based on https://www.asyncapi.com/docs/specifications/v2.2.0#messageObject this is a valid payload and it seems to validate correctly on https://studio.asyncapi.com/

1.0.0 release checklist

1.0.0 release

TODO items before cutting the first major tag of Asynction:

  • Rename the spec_path argument to spec in the AsynctionSocketIO.from_spec and MockAsynctionSocketIO.from_spec methods.
  • v3.0.0 release of the specification?
  • #205

All message payload and ack argument schemata should be tuples

Problem statement

The Socket.IO protocol allows events to have accompanying data that consist of one or multiple arguments. Up until now, Asynction used the following logic in order to validate such event data (either message payloads, or ack callback arguments):

if the JSONSchema of the respective entity (message or ack callback) is a JSONSchema tuple, then treat the entity instance as a sequence of multiple arguments

Else, treat the instance as a single argument.

The above validation logic required asynction to parse the JSONSchema of the respective entity, in order to understand whether it is a tuple schema (contains prefixItems) or not. This however does not play well in more complex schematas that make use of a generic top level JSONSchema keys, such as (oneOf, anyOf, and multipleOf ). In such cases there is no top level prefixItems key and asynction fails to label a schema as a tuple.

Solution

In order to solve the above problem, we need to find a way for the spec or the JSON schema to signal the multiple arguments semantic.

One option would be for Asynction to do a more sophisticated parsing of the schemata object, to detect generic structures that include tuples. However, there is a risk that this more sophisticated parsing would end up being too complex and could turn Asynction into yet another jsonschema parser.

The root cause of this problem is that we treat single argument events and multi-argument events as 2 separate cases:
image

Table referenced from https://dedouss.is/posts/2021-07-14-documenting-socketio-part-2.html#channels

However, these 2 cases can be reduced to 1. In essence, expressing the single argument using type: object is syntactic sugar for expressing it as a tuple of 1 element:

  type: array
  prefixItems:
    - type: object

If Asynction enforces its users to always use tuples when expressing such JSON schemata (even the single argument ones), then there is no need to parse the schemata themselves.

Incorporating asynction into existing flask API with socketio events

Hi there, this project looks really interesting & I have a question which I hope is not out of place.

I have an API in development which uses flask and flask-restx for generating an openapi spec upon which several developers are dependent in order to develop front-end applications. There is also a small set of websocket events (one path, 3 - 5 events) implemented via flask-socketio, and I am really missing the Swagger-style documentation when it comes to the socketio pieces. That ultimately led me to AsyncAPI & then asynction.

I've already missed the "spec first" boat since the code is already implemented. But I'm wondering if I should back up and incorporate asynction in order to be able to properly integrate some nice documentation.

My question is, given the fact I'm already running an operational API with some websocket events, how complicated would it be to add in asynction at this point and convert my existing events? I haven't had the chance to fully look into how things work with asynction yet, so I appreciate any thoughts about the complexity such a transition might pose. Thanks!

Per namespace security

As a continuation of #110 It would be very helpful to be able to customize security schemes on a per namespace level.
Currently asyncapi does not support this, so it would need to be in the form of a x-security specification.

One of the main questions I see is how per namespace security would interact with global security. Would it override global level security? or would both have to pass?

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.