flask-smorest icon indicating copy to clipboard operation
flask-smorest copied to clipboard

How to configure body content-type text/plain

Open BorjaEst opened this issue 4 years ago • 2 comments

I would like to configure a method that would use text as body imput (a message) for example:


{
    "...": "...",
    "paths": {
        "/results/report": {
            "post": {
                "responses": {
                    "...": "...",
                    "201": {
                        "description": "Created",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Report"
                                }
                            }
                        }
                    },
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "text/plain": {
                            "type": "string",
                            "description": "Report message as text",
                            "example": "This is a text example"
                        }
                    }
                },
                "...": "...",
            },
            "parameters": [
                "..."
            ]
        }
    },
    "openapi": "3.0.2"
}

However, as @blueprint.arguments() only accepts schemas (or I think so), I could not find a way to accept "text" calls (neither in the swagger UI).

Is there a way I have not think about to produce this? I was thinking in something similar to:


import marshmallow as ma
import schemas

from flask.views import MethodView
from flask_smorest import Blueprint


blp = Blueprint(
    'results', __name__, description='Operations on results'
)

@blp.route('/report')
class Report(MethodView):

    @blp.doc(operationId='AddResultReport')
    @blp.arguments(ms.fields.String, content_type="text/plain")
    @blp.response(201, schemas.Report)
    def post(self, message):
        """Creates a result report."""
        ...
        return report

BorjaEst avatar Aug 11 '21 09:08 BorjaEst

I haven't had the time to test this yet but I think you could archieve this by using marshmallows pre_dump-decorator.

Code-Example:

from marshmallow import Schema
from marshmallow.fields import String

class TextBodySchema(Schema):
    content = String()

    @pre_load
    def wrap_body_content(self, in_data, **kwargs):
        data = {"content": in_data}
        return data
    
@blp.route('/report')
class Report(MethodView):

    @blp.doc(operationId='AddResultReport')
    @blp.arguments(TextBodySchema)
    @blp.response(201, schemas.Report)
    def post(self, body_schema):
        """Creates a result report."""
        message = body_schema.content
        ...
        return report

For more examples of extending schemas take a look at the marshmallow-docs.

der-joel avatar Aug 18 '21 13:08 der-joel

Thanks for the help, I tried but it fails:

$ curl -X 'POST' \
>   'http://localhost:5000/results/265ddee1-555d-47d6-90cb-6279c730218c/report' \
>   -H 'accept: application/json' \
>   -H 'Content-Type: text/plain' \
>   -d 'Result does not match benchmark template'
{
  "code": 422, 
  "errors": {
    "json": {
      "content": [
        "Not a valid string."
      ]
    }
  }, 
  "status": "Unprocessable Entity"
}

Also i swagger the "example/schema" does not look as it shoud:

Example Value
{
  "content": "This is an example string content"
}

when it should be simply:

This is an example string content

When debugging the content of in_data, its value is just {}. I also tired to use content_type="text/plain" but in_data is still {}... Is there a place to check where the content is removed and why?

BorjaEst avatar Aug 23 '21 10:08 BorjaEst