Skip to content
This repository has been archived by the owner on Oct 20, 2023. It is now read-only.

Commit

Permalink
feat: Add support for sending files as response
Browse files Browse the repository at this point in the history
The new option `sendFile` lets you sends the file as response instead of returning the file content.

Closes #11
  • Loading branch information
mischah authored Dec 4, 2017
1 parent bbd445b commit 70d535f
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 62 deletions.
76 changes: 43 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

> Build a fake backend by providing the content of JSON files or JavaScript objects through configurable routes.
*It actually can serve the content of other file types as well as sending the files itself as response.*

Comes as a Node.js server. Useful for mocking, testing and developing independent of the »real« backend.

## Example
Expand Down Expand Up @@ -93,6 +95,36 @@ module.exports = SetupEndpoint({
});
```

#### Advanced Example

`/server/api/anotherExample.js`:

```js
module.exports = SetupEndpoint({
name: 'anotherExample',
urls: [{
params: '/read',
requests: [{
method: 'GET',
response: '/response-files/anotherExample.json'
}]
}, {
params: '/update/{id}',
requests: [{
method: ['PUT', 'PATCH'],
response: {
success: true
}
}, {
method: 'DELETE',
response: {
deleted: true
}
}]
}, ]
});
```

#### Serving different content types

`/server/api/fileTypes.js`:
Expand All @@ -115,38 +147,13 @@ module.exports = SetupEndpoint({
params: '/html',
requests: [{
response: '/response-files/example.html',
statusCode: 201,
mimeType: 'text/html'
}]
}]
});
```

#### Advanced Example

`/server/api/anotherExample.js`:

```js
module.exports = SetupEndpoint({
name: 'anotherExample',
urls: [{
params: '/read',
requests: [{
method: 'GET',
response: '/response-files/anotherExample.json'
}]
}, {
params: '/update/{id}',
params: '/pdf',
requests: [{
method: ['PUT', 'PATCH'],
response: {
success: true
}
}, {
method: 'DELETE',
response: {
deleted: true
}
response: '/response-files/example.pdf',
sendFile: true
}]
}]
});
Expand Down Expand Up @@ -213,16 +220,19 @@ The configuration object in Detail:
* `string`, or `array` of strings.
* is used to define the http method(s) to which the endpoint will listen.
* `urls.requests.response`
* Could be a string pointing to a JSON template:
* Could be a string pointing to a file:
* `response: '/response-files/articles.json'`
* Or just a JavaScript object:
* `response: { success: true }`
* `urls.requests.mimeType`
* optional (string). Defaults to `application/json`.
* is used to set the `content-type` response header.
* Optional (string). Defaults to `application/json`.
* Is used to set the `content-type` response header.
* `urls.requests.sendFile`
* Optional (boolean). Defaults to `false`.
* Sends the file as response instead of returning the file content.
* `urls.requests.statusCode`
* Optional
* A status code (number)
* Optional (boolean). Defaults to `200`
* The HTTP status code of the response.
* Will return:
* a status code with a self defined response if you provide a response property
* a status code with a predefined error object provided by [boom](https://github.com/hapijs/boom) if you dont provide a response property for that request.
Expand Down
Binary file added response-files/example.pdf
Binary file not shown.
6 changes: 6 additions & 0 deletions server/api/fileTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,11 @@ module.exports = SetupEndpoint({
statusCode: 201,
mimeType: 'text/html'
}]
}, {
params: '/pdf',
requests: [{
response: '/response-files/example.pdf',
sendFile: true
}]
}]
});
7 changes: 7 additions & 0 deletions server/api/setup/lib/getContentDisposition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

module.exports = function (filePath) {

const fileName = /\/([^\/]+\.\S+)$/g.exec(filePath);
return 'attachment; filename=' + fileName[1];
};
14 changes: 13 additions & 1 deletion server/api/setup/supportedMethod.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ const Boom = require('boom');
const Fs = require('fs');
const Path = require('path');

const GetContentDisposition = require('./lib/getContentDisposition.js');

module.exports = function (server, proposedRequest, settings, params, path) {

const method = proposedRequest.method || 'GET';
const mimeType = proposedRequest.mimeType || 'application/json';
const sendFile = proposedRequest.sendFile | false;
const isFile = typeof proposedRequest.response === 'string';
const mimeType = proposedRequest.mimeType || (sendFile ? 'application/octet-stream' : 'application/json');

return {
method,
Expand Down Expand Up @@ -36,13 +40,21 @@ module.exports = function (server, proposedRequest, settings, params, path) {
}

if (proposedRequest.statusCode && proposedRequest.response) {

if (sendFile && isFile) {
return reply(response).code(proposedRequest.statusCode).type(mimeType).header('Content-Disposition', GetContentDisposition(proposedRequest.response));
}
return reply(response).code(proposedRequest.statusCode).type(mimeType);
}

if (response.isBoom === true) {
return reply(response);
}

if (sendFile && isFile) {
return reply(response).type(mimeType).header('Content-Disposition', GetContentDisposition(proposedRequest.response));
}

return reply(response).type(mimeType);
}
};
Expand Down
114 changes: 86 additions & 28 deletions test/server/api/fileTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ const Endpoint = SetupEndpoint({
statusCode: 201,
mimeType: 'text/html'
}]
}, {
params: '/pdf',
requests: [{
response: '/response-files/example.pdf',
sendFile: true
}]
}, {
params: '/pdf/tweaked',
requests: [{
response: '/response-files/example.pdf',
sendFile: true,
statusCode: 201,
mimeType: 'application/pdf'
}]
}]
});

Expand Down Expand Up @@ -60,52 +74,96 @@ lab.experiment('Different file types', () => {
});


lab.test('content-type header defaults to `application/json`', (done) => {
lab.experiment('send file contents ', () => {

request = {
method: 'GET',
url: apiUrlPrefix + '/fileTypes/json'
};
lab.test('content-type header defaults to `application/json`', (done) => {

server.inject(request, (response) => {
request = {
method: 'GET',
url: apiUrlPrefix + '/fileTypes/json'
};

Code.expect(response.headers['content-type']).to.equal('application/json; charset=utf-8');
server.inject(request, (response) => {

done();
Code.expect(response.headers['content-type']).to.equal('application/json; charset=utf-8');

done();
});
});
});

lab.test('return text files with the defined content-type header`', (done) => {
lab.test('return text files with the defined content-type header`', (done) => {

request = {
method: 'GET',
url: apiUrlPrefix + '/fileTypes/text'
};

server.inject(request, (response) => {

Code.expect(response.headers['content-type']).to.equal('text/plain; charset=utf-8');
Code.expect(response.result).to.equal('This is just a plain old text file.\n');

done();
});
});

lab.test('return with the defined content-type header and custom status code`', (done) => {

request = {
method: 'GET',
url: apiUrlPrefix + '/fileTypes/text'
};
request = {
method: 'GET',
url: apiUrlPrefix + '/fileTypes/html'
};

server.inject(request, (response) => {
server.inject(request, (response) => {

Code.expect(response.headers['content-type']).to.equal('text/plain; charset=utf-8');
Code.expect(response.result).to.equal('This is just a plain old text file.\n');
Code.expect(response.headers['content-type']).to.equal('text/html; charset=utf-8');
Code.expect(response.result).to.equal('<a href="https://github.com">GitHub</a>\n');
Code.expect(response.statusCode).to.equal(201);

done();
done();
});
});

});

lab.test('return with the defined content-type header and custom status code`', (done) => {

request = {
method: 'GET',
url: apiUrlPrefix + '/fileTypes/html'
};

server.inject(request, (response) => {
lab.experiment('send files ', () => {

lab.test('send files with the default content-type and the correct name`', (done) => {

Code.expect(response.headers['content-type']).to.equal('text/html; charset=utf-8');
Code.expect(response.result).to.equal('<a href="https://github.com">GitHub</a>\n');
Code.expect(response.statusCode).to.equal(201);
request = {
method: 'GET',
url: apiUrlPrefix + '/fileTypes/pdf'
};

done();
server.inject(request, (response) => {

Code.expect(response.headers['content-type']).to.equal('application/octet-stream');
Code.expect(response.headers['content-disposition']).to.equal('attachment; filename=example.pdf');
Code.expect(response.statusCode).to.equal(200);

done();
});
});

lab.test('send files with a defined content-type and a custom status code`', (done) => {

request = {
method: 'GET',
url: apiUrlPrefix + '/fileTypes/pdf/tweaked'
};

server.inject(request, (response) => {

Code.expect(response.headers['content-type']).to.equal('application/pdf');
Code.expect(response.headers['content-disposition']).to.equal('attachment; filename=example.pdf');
Code.expect(response.statusCode).to.equal(201);

done();
});
});

});


Expand Down
Binary file added test/server/api/fixtures/example.pdf
Binary file not shown.

0 comments on commit 70d535f

Please sign in to comment.