New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to set a file as parameter or response? #93

Open
ryo-nbx opened this Issue Apr 28, 2017 · 15 comments

Comments

Projects
None yet
8 participants
@ryo-nbx
Copy link

ryo-nbx commented Apr 28, 2017

How to write TSOA code that uses files as input parameter and response? I could not find any rerference that hints that TSOA is supporting the file datatype of the swagger definition. Is it missing? Will it be added in the future?

I try to recreate an existing swagger definition with TSOA and stumbled over a missing file keyword(?).
My swagger definition is like that:

/system/update/firmware:
post:
tags:
- System
- Update
summary: update system firmware with new firmware image
description: update system firmware with new firmware image
operationId: updateFirmware
parameters:
- in: formData
name: file
description: uploaded firmware image file
required: true
type: file
responses:
"200":
description: successfull operation
"400":
description: invalid firmware image provided
security:
- project_auth:
- system_install
/system/export:
post:
tags:
- System
- Backup
description: export system preferences and configuration to external file system
operationId: exportSystemBackup
responses:
"200":
description: export OK
schema:
type: file
description: file that contains the backup of system preferences and settings
"400":
description: export failure
security:
- project_auth:
- system_export

As you can see there is a file as input parameter and return value. The swagger.io definition supports files as own data type. I try to generate a similiar definition by generating Typescript code and adding TSOA anotations.

When generating TypeScript client code and modifying it to server side code I get this:

...
@post('export')
public async exportSystemBackup ( ) : Promise {
....
@post('update/firmware')
public async updateFirmware (@Body() file: any ) : Promise {
...

But when generating routes I get following error message:

tsoa swagger

Error: Unknown type: AnyKeyword
at ResolveType (.../resolveType.js:30:15)

I looked in the TSOA repository but could not find the any or file keyword handled in any way. I could not find an example for my use case either. So could someone help? Thank you.

@ezra-quemuel

This comment has been minimized.

Copy link

ezra-quemuel commented May 2, 2017

+1 This is preventing me from using tsoa. Though I'm not exactly sure as to how to solve the issue. Maybe using an @Upload decorator?
EDIT
On second thought, the solution to support the file type won't work with just an @Upload decorator :/

Some ideas...
routing-controllers uses an @UploadedFile(s) decorators to inject a file into the parameter of the controller
https://github.com/pleerock/routing-controllers#inject-uploaded-files
The only issue(?) is that it's using multer as a dependency to support files.
Also, when generating routes, it could look for the metadata generated by the @UploadedFile(s) decorators to know whether it should generate a route that supports file uploads. And when generating the swagger docs, the @UploadedFile(s) decorators would indicate when there is a parameter of type "file" required for a specific route.

While this would solve files as input parameters, I'm not exactly sure how it would be supported in the response. Any thoughts?

@ryo-nbx

This comment has been minimized.

Copy link

ryo-nbx commented May 3, 2017

I think a solution could be easier. TSOA already evaluates the used classes and types and transforms them to types that TSOA can handle. The class File should be added to that types and the generator should generate a proper swagger definition using the swagger "file" keyword.

I'm new to TypeScript and node.js services. So I'm not sure how a proper API that use file upload and download should look like. I tried some API variants replacing the "File" result and parameter with "Buffer" and "Blob". TSOA can generate routes and the API with this types but handles them as string (in the swagger definition). File parameter do not work because the class contains "any" member that can't be processed by TSOA.

@ezra-quemuel

This comment has been minimized.

Copy link

ezra-quemuel commented May 3, 2017

I don't think tsoa currently generates routes that correctly support file uploads when using the command

tsoa routes

It might generate some swagger definitions that have the parameter with the name "file" but with the incorrect type.

In order to fully support a file as a parameter, I believe tsoa should generate the correct routes (in routes.ts) with support for file uploads (which can be achieved in this case with multer) as well as the correct swagger definition with the type "file". The Express.Multer.File type could also be leveraged instead of creating a new class.

Again, this doesn't solve file as a return type, but I'm guessing that the express.static middleware can be used (I'm sure hapi and koa have similar middleware) when generating the routes.ts.

@lukeautry

This comment has been minimized.

Copy link
Owner

lukeautry commented May 4, 2017

Right now, File uploads isn't supported from a swagger OR a route standpoint. Ideally, it would be supported for both.

File returns would be...weird. File posts should be more straightforward, akin to the support for base64/binary.

I will try to get something together relatively soon, but PRs are always welcome if someone would like to take a stab at it.

@ezra-quemuel

This comment has been minimized.

Copy link

ezra-quemuel commented May 4, 2017

I'd be happy to take a stab at it @lukeautry

@egandro

This comment has been minimized.

Copy link
Contributor

egandro commented Jul 4, 2017

Swagger has specifications for "multipart/form-data" https://swagger.io/specification/#parameterObject

That is - in terms of HTML Forms - a form based file uploads.

Express.JS comes with a middle ware for this:
https://github.com/expressjs/multer

Multer needs some configuration and injection into the route.ts file. I think this is possible with the same ideas as we have them in the authorizsation and the IOC.

So pointing to the swagger docs I suggest the following new Parameter:


// The Client has to send us Content-Disposition: form-data; name="parameterName"
@Form('multipart/form-data', parameterName) parameterName

// The Client has to send us in the path "?parameterName=1" - this is like a query parameter
// and basically has nothing to do with a file upload at all!
@Form('application/x-www-form-urlencoded', parameterName) parameterName

// this defaults to the "multipart/form-data"
@Form() parameterName

@PatrickCharriere

This comment has been minimized.

Copy link

PatrickCharriere commented Oct 4, 2017

Hello,
How can we help to make this annotation work ?
Any news about merge into master ?

@egandro

This comment has been minimized.

Copy link
Contributor

egandro commented Oct 5, 2017

I am lazy :)))) - Or busy.

I need this very very soon - but I don't have the time to fix it (yet).

My idea (well at last for express.js) is to multer as middleware
https://www.npmjs.com/package/multer

I currently create a manual route and create an instance of the controller out of the controll of router.ts

You can go with something like this (not currenty supported by tsoa) but at last you have a working solution until I am not longer lazy.

// https://github.com/expressjs/multer
const multer = require('multer');
const storage = multer.memoryStorage(); // we might have to change this
const multerUpload = multer({ storage: storage });

export class ManualRoute {
    public static create(app: any) {    
        app.post('/api/foo', multerUpload.single('binarydata'),
            (req: Request, res: Response, next: NextFunction) => {
           new BinaryDeviceRoute().upload(req, res, next);
        })
    }

    public async upload(reqorig: Request, res: Response, next: NextFunction) {
        const file: any = req.file;

        if (file == null || file === undefined) {
            next(new Error('missing data file'));
            return;
        }

        let data: Buffer = file.buffer;
        res.status(200);
        res.send('OK');
    }
}
@ryo-nbx

This comment has been minimized.

Copy link

ryo-nbx commented May 30, 2018

https://swagger.io/docs/specification/describing-responses/#response-that-returns-a-file

Version 3.0 of the Swagger OpenApi still supports file downloads. What version of the OAS will TSOA support in the future?

I managed to get a file upload with TSOA, multer and Aurelia running. Looks good. But now I dig into the download topic and found that there seems to be no way to get the Express response in the TSOA controller. So I could not set a content type or put a file into it. Any suggestions?

@lukeautry

This comment has been minimized.

Copy link
Owner

lukeautry commented May 30, 2018

The way to go here, for now, is to use a custom route template. In an app I use tsoa on, you can download CSVs, and there's a special route template to handle that case.

That said, it's pretty hacky, and I would like to implement first class support (or accept a pull request that does it)

@ryo-nbx

This comment has been minimized.

Copy link

ryo-nbx commented May 30, 2018

There is a PR open for almost a year adding file download to TSOA. It has merge conflicts. I fixed the conflicts and opened a new PR: #228

You might close other issues too, since the original unit tests show how to download a HTML file (I saw one or two request) with the new addition.

@AmazingTurtle

This comment has been minimized.

Copy link
Contributor

AmazingTurtle commented Jun 17, 2018

@lukeautry can we merge this?

@qqnc

This comment has been minimized.

Copy link
Contributor

qqnc commented Aug 12, 2018

Wonder if this is merged? looking a way to export file and download it. Any documents? I couldn't locate it. Thanks

@ryo-nbx

This comment has been minimized.

Copy link

ryo-nbx commented Sep 13, 2018

No, PR #228 is not merged yet.

@lukeautry
When will this feature be merged?

@ryo-nbx

This comment has been minimized.

Copy link

ryo-nbx commented Sep 21, 2018

https://blog.nubix.de/index.php/2018/09/21/file-download-and-upload-with-tsoa-hack/

I put an example online demonstrating how to achieve upload and download files with the current version of TSOA. Downloading is a little bit dirty since file responses are not handled yet by TSOA.

I hope the example might help some user here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment