remcohaszing / flask-openapi

Generate a `swagger.json` handler from a Flask app
MIT License
3 stars 2 forks source link

Implement parameters for request handlers (operations) #1

Open remcohaszing opened 8 years ago

remcohaszing commented 8 years ago

http://swagger.io/specification/#parameterObject

Currently the only support type of input parameters is body. It should be possible to consume all types of documented parameters.

Query parameters should be supported. From within a handler it should be possible to use processed parameters.

Since the parameters are mostly implemented by a JSON schema, their definition can be expressed using one. This can be either a JSON schema or **kwargs passed as a JSON schema.

Thoughts explained by a usage example:

app = Flask(__name__)
oauth = OAuth(app)

@app.route('/')
@oauth.query_param('type', 'BeverageType')
@oauth.query_param('names', collection_format=CollectionFormat.pipes, type='string')
def handler():
    assert isinstance(oauth.parameters.type, str)
    assert isinstance(oauth.parameters.names, list)

assert isinstance(CollectionFormat, enum.Enum)
assert CollectionFormat.pipes == '|'

client = app.test_client()
client.get('/?type=beer&names=Hertog%20Jan|Palm')
xuru commented 7 years ago

@remcohaszing You might want to have a look at webargs. I've used this in many projects, and it would be beneficial, to not have to do twice the work.

What's nice is, it uses marshmallow under the hood to define the schema. A lot of projects use Marshmallow to serialize/deserialize resource models...

I was thinking of writing a project much like this one before I stumbled upon yours. Here is what I was thinking (to clarify):

The Marshmallow schema definition:

class UserSchema(ma.Shema):
    first_name = fields.Str(allow_none=False, required=True)
    last_name = fields.Str(allow_none=False, required=True)
    email = fields.Email(allow_none=False, required=True)

The user model:

class User(EntityModel):
    first_name = Field(data_type=unicode)
    last_name = Field(data_type=unicode)
    email = Field(data_type=unicode)

The user resource (using flask-restful:


user_args = {
    'email': fields.Str(required=True), 
    'active_only': fields.Bool(location='query', missing=True)
}

class UsersResource(BaseResource):
    schema = UserSchema
    model = User

    @jwt_required()
    @use_kwargs(user_args)
    def get(self, email, active_only):
        user = User.get_by_email(email)
        # do some stuff
        return jsonify(self.schema(strict=True).dump(user).data)

So you can see that I'm already defining a 'schema' for the response, and the query params (webargs also gets the args from query, json, form, etc.. that you mentioned above). Because they use marshmallow, you can pass in extra information in the fields as well. For example:

class UserSchema(ma.Shema):
    first_name = fields.Str(allow_none=False, required=True, description="First name")
    last_name = fields.Str(allow_none=False, required=True)
    email = fields.Email(allow_none=False, required=True)
    aliases = fields.Str(collection_format="pipes", description="A Pipe delimited string of names to be used as aliases")

Sorry if I'm rambling... Let me know what you think, and I'll contribute some time.

remcohaszing commented 7 years ago

@xuru Cool to hear someone's interested! At the moment I'm a bit busy doing other stuff, so this project has gotten lower priority.

I'm personally not a big fan of flask-restful, so I prefer not to depend on that.

Also I'd rather use raw JSON schemas, but I'd welcome a patch that can generate a description from a marshmallow model.

Looking at webargs, I'm not sure such an API can be described using OpenAPI 1.0. Eventually I'd like to support OpenAPI 2.0 anyway though.

I'm definitely hoping to pick up this project sooner or later. Contributions are welcome in the form of ideas or actual code contributions. :wink:

xuru commented 7 years ago

Good to hear. Time is a always a commodity here as well. I'll see if I can free up some time.