fastify / fastify-swagger

Swagger documentation generator for Fastify
MIT License
910 stars 200 forks source link

Why don't show input to select file #706

Closed DiogoMarques2003 closed 1 year ago

DiogoMarques2003 commented 1 year ago

Prerequisites

Issue

Hello, i have the flowing schema:

const UserChangeCurriculum: FastifySchema = {
  description: 'Alterar o currículo do utilizador',
  tags: ['User'],
  consumes: ['multipart/form-data'],
  body: {
    type: 'object',
    properties: {
      file: {
        type: 'string', format: 'binary',
      },
    },
  },......

But for some reason in swagger ui this don't show the input to select the file

image

mcollina commented 1 year ago

I don't know, let us know if you find out!

DiogoMarques2003 commented 1 year ago

I don't know, let us know if you find out!

No, i already try some things and don't work, stay on the same

Uzlopak commented 1 year ago

https://swagger.io/docs/specification/describing-request-body/file-upload/

DiogoMarques2003 commented 1 year ago

I already have this but don't work @Uzlopak image

Uzlopak commented 1 year ago

If i understand it correctly you specify the body as application/json and not as multiplart/form-data. Or were do you set the content-type?

DiogoMarques2003 commented 1 year ago

Yes i set the body as application/json, but on this route e say the system consumes multipart/form-data

Uzlopak commented 1 year ago

Where is documented what the consume field does?

DiogoMarques2003 commented 1 year ago

https://www.npmjs.com/package/@fastify/swagger

It's here, but the docs don't say what this do, this looks like what the route consumes

Uzlopak commented 1 year ago

I dont see any relevance for that.

DiogoMarques2003 commented 1 year ago

Yes, but in theory this would work...

Uzlopak commented 1 year ago

In theory it shiuld not work as the body needs to specify content and then the content-type.

DiogoMarques2003 commented 1 year ago

and how i can specify this ?

climba03003 commented 1 year ago

I am curious what is the setting for registered the plugin? If you are generating Swagger 2.0 and it is expected to be fail. If you are generating OpenAPI 3.x, it would works.

DiogoMarques2003 commented 1 year ago

Hey @climba03003 i'm ussing the flowing settings:

  const swaggerOptions: SwaggerOptions = {
  mode: 'dynamic',
  swagger: {
    info: {
      title: 'Portugal Jobs API',
      description: 'Rotas da api do Portugal Jobs',
      version: '1.0.0',
    },
    externalDocs: {
      url: 'https://dev.azure.com/',
      description: 'Docs azure devops',
    },
    consumes: ['application/json'],
    produces: ['application/json'],
    host: process.env.NODE_ENV === 'production' ? '' : 'localhost:3333',
    schemes: process.env.NODE_ENV === 'production' ? ['https'] : ['http'],
    tags: [
      { name: 'Users', description: 'Rotas para os utilizadores' },
      { name: 'Anúncios', description: 'Rotas para os anúncios' },
      { name: 'Candidaturas', description: 'Rotas para as candidaturas' },
    ],
    securityDefinitions: {
      authorization: {
        type: 'apiKey',
        in: 'header',
        name: 'Authorization',
        description: 'Token de autorização a api, exemplo: "Bearer TOKEN"',
      },
    },
    security: [{ authorization: [] }],
  },
};

const swaggerUIOptions: FastifySwaggerUiOptions = {
  routePrefix: '/api/docs',
  uiConfig: {
    deepLinking: false,
    persistAuthorization: true,
  },
  uiHooks: {
    onRequest(request, reply, next) { next(); },
    preHandler(request, reply, next) { next(); },
  },
  staticCSP: true,
  transformStaticCSP: (header) => header,
  transformSpecification: (swaggerObject) => swaggerObject,
  transformSpecificationClone: true,
};

  await fastify.register(fastifySwagger, swaggerOptions);
  fastify.log.info('Swagger registered');

  await fastify.register(fastifySwaggerUI, swaggerUIOptions);
  fastify.log.info('Swagger UI registered');

and the version of dependencies is the flowing:

    "@fastify/swagger": "^8.2.1",
    "@fastify/swagger-ui": "^1.3.0",
climba03003 commented 1 year ago

Then, it is expected not working. "format": "binary" only supported by OpenAPI 3.x. You need to use "type": "file" for Swagger 2.0 and you also need to customize ajv.

DiogoMarques2003 commented 1 year ago

@climba03003 can you give an example to customize ajv with type file pls ?

Rohland commented 1 year ago

Having a similar problem here.

Using Open API 3.x I have this syntax:

        body: {
            type: 'object',
            properties: {
                media: {
                    type: "string",
                    format: "binary"
                }
            }
        }

With this in place, the Swagger UI correctly shows the file upload control, however, upon submitting I get the following error.

  err: {
      "statusCode": 400,
      "validation": [
        {
          "instancePath": "/media",
          "schemaPath": "#/properties/media/type",
          "keyword": "type",
          "params": {
            "type": "string"
          },
          "message": "must be string"
        }
      ],
      "validationContext": "body",
      "message": "body/media must be string",
      "name": "Error",
      "stack":
          Error: body/media must be string
              at defaultSchemaErrorFormatter (/pulse/node_modules/fastify/lib/context.js:107:10)
              at wrapValidationError (/pulse/node_modules/fastify/lib/validation.js:135:17)
              at validate (pulse/node_modules/fastify/lib/validation.js:115:12)
              at preValidationCallback (/pulse/node_modules/fastify/lib/handleRequest.js:92:18)
              at next (/pulse/node_modules/fastify/lib/hooks.js:173:7)
              at Immediate._onImmediate (/pulse/node_modules/@fastify/multipart/index.js:68:5)
              at processImmediate (node:internal/timers:466:21)

How can we simultaneously:

climba03003 commented 1 year ago

You can add a preValidation hook to intercept the body first. Then, revert in preHandler hook.

It is a known pain of json schema as it does not provide a binary type only format (which is restricted by the type) That's also why I created fastify-formidable to provide a relative easy way to solve the issue.

Rohland commented 1 year ago

Thanks, that's my current workaround. Thanks for sharing your library, will check that out.

For others, the workaround I have in place currently is something like:

{
    schema: { .... },
    preValidation: (request, reply, done) => {
        request.body._media = request.body.media;
        request.body.media = "";
        done();
    },
    preHandler: (request, reply, done) => {
        request.body.media = request.body._media;
        delete request.body._media;
        done();
    }
}