Socket
Socket
Sign inDemoInstall

hapi-csv

Package Overview
Dependencies
3
Maintainers
3
Versions
41
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 4.0.1 to 4.1.0

60

lib/index.js

@@ -6,2 +6,3 @@ 'use strict';

const Async = require('async');
const XLSX = require('xlsx');

@@ -16,2 +17,3 @@ const internals = {

internals.maximumElementsInArray = options.maximumElementsInArray || 5;
internals.enableExcel = !!options.enableExcel;

@@ -44,2 +46,7 @@ // Build up routeMap for all routes on all connections

if (internals.enableExcel && path.endsWith('.xlsx')) {
request.setUrl(`${path.substring(0, path.length - 5)}${request.url.search ? request.url.search : ''}`);
request.headers.accept = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
}
return reply.continue();

@@ -49,3 +56,3 @@ });

// Check for header and route mapping and convert and reply with csv if needed
const allowedTypesRegex = /(text\/csv)|(application\/csv)/i;
const allowedTypesRegex = /(text\/csv)|(application\/csv)|(application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet)/i;

@@ -66,2 +73,6 @@ server.ext('onPreResponse', (request, reply) => {

if (!internals.enableExcel && preferredType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
return reply.continue();
}
if (!(preferredType && internals.routeMap.has(internals.createRouteMethodString(request.route.path, request.route.method)))) {

@@ -94,4 +105,13 @@ return reply.continue();

const schema = internals.routeMap.get(internals.createRouteMethodString(request.route.path, request.route.method));
const csv = internals.schemaToCsv(schema, resolvedSchemasObject, request.response.source, options.resultKey);
if (internals.enableExcel && preferredType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
const fileName = request.path.substring(request.path.lastIndexOf('/') + 1 || 0, request.path.length);
const xlsx = internals.processSchema(schema, resolvedSchemasObject, request.response.source, options.resultKey, internals.headerQueryMapToExcel);
const excel = XLSX.write(xlsx, { bookType:'xlsx', bookSST:true, type: 'base64' });
const response = reply(excel);
return response.encoding('base64').type(`${preferredType}; charset=${response.settings.charset}; header=present;`).header(`'content-disposition', 'attachment'; filename=${fileName}.xlsx;`).header('Content-Length', excel.length);
}
const csv = internals.processSchema(schema, resolvedSchemasObject, request.response.source, options.resultKey, internals.headerQueryMapToCsv);
const response = reply(csv);

@@ -110,3 +130,3 @@ return response.type(`${preferredType}; charset=${response.settings.charset}; header=present;`).header('content-disposition', 'attachment;');

internals.schemaToCsv = (schema, dynamicSchemas, dataset, resultKey) => {
internals.processSchema = (schema, dynamicSchemas, dataset, resultKey, callback) => {

@@ -128,3 +148,3 @@ // We return the dataset if the dataset is not an array or an object, just a primitive type

return internals.headerQueryMapToCsv(headerQueryMap, dataset);
return callback(headerQueryMap, dataset);
};

@@ -276,2 +296,34 @@

internals.headerQueryMapToExcel = (headerQueryMap, dataset) => {
const dataRows = [];
if (!Array.isArray(dataset)) {
dataset = [dataset];
}
for (let i = 0; i < dataset.length; ++i) {
const dataRow = {};
for (const key of headerQueryMap.keys()) {
const query = headerQueryMap.get(key);
let temp = dataset[i];
for (const queryPart of query) {
temp = temp[queryPart];
}
temp = internals.dateToISOString(temp);
dataRow[key] = internals.escapeQuotesInString(temp);
}
dataRows.push(dataRow);
}
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(dataRows, headerQueryMap.keys()));
return wb;
};
internals.escapeQuotesInString = (str) => {

@@ -278,0 +330,0 @@

17

package.json
{
"name": "hapi-csv",
"version": "4.0.1",
"version": "4.1.0",
"description": "Hapi plugin for converting a Joi response schema and dataset to csv",
"main": "lib/index.js",
"scripts": {
"test": "lab -a code -vL --lint-fix -t 100 -I URL,URLSearchParams"
"test": "lab -a code -vL --lint-fix -t 95 -I cptable"
},

@@ -24,9 +24,10 @@ "repository": {

"async": "^2.6.1",
"hoek": "5.x.x"
"hoek": "^6.0.3",
"xlsx": "^0.14.1"
},
"devDependencies": {
"code": "4.x.x",
"hapi": "^16.6.3",
"joi": "^13.0.0",
"lab": "^14.3.4"
"code": "5.x.x",
"hapi": "^16.7.0",
"joi": "^14.1.0",
"lab": "^18.0.0"
},

@@ -38,3 +39,3 @@ "peerDependencies": {

"engines": {
"node": ">=6.0.0"
"node": ">=8.0.0"
},

@@ -41,0 +42,0 @@ "bugs": {

@@ -20,3 +20,3 @@ 'use strict';

it('Registers', (done) => {
it('Registers', () => {

@@ -26,9 +26,12 @@ const server = new Hapi.Server();

server.register({
register: HapiCsv
}, (err) => {
return new Promise((resolve) => {
expect(err, 'error').to.not.exists();
return server.register({
register: HapiCsv
}, (err) => {
return done();
expect(err, 'error').to.not.exists();
return resolve();
});
});

@@ -60,3 +63,3 @@ });

before((done) => {
before(() => {

@@ -111,14 +114,17 @@ simpleServer = new Hapi.Server();

return simpleServer.register({
register: HapiCsv
}, (err) => {
return new Promise((resolve) => {
expect(err, 'error').to.not.exist();
return simpleServer.register({
register: HapiCsv
}, (err) => {
// initialize is needed for hapi-csv route mapping to trigger
return simpleServer.initialize((err) => {
expect(err, 'error').to.not.exist();
return done();
// initialize is needed for hapi-csv route mapping to trigger
return simpleServer.initialize((err) => {
expect(err, 'error').to.not.exist();
return resolve();
});
});

@@ -128,178 +134,218 @@ });

after((done) => {
after(() => {
return simpleServer.stop(done);
return new Promise((resolve, reject) => {
return simpleServer.stop((err) => {
if (err) {
return reject(err);
}
return resolve();
});
});
});
it('Converts with text/csv header', (done) => {
it('Converts with text/csv header', () => {
return simpleServer.inject({
method: 'GET',
url: '/user',
headers: {
'Accept': 'text/csv'
}
}, (res) => {
return new Promise((resolve) => {
expect(res.result).to.equal(userCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
return simpleServer.inject({
method: 'GET',
url: '/user',
headers: {
'Accept': 'text/csv'
}
}, (res) => {
return done();
expect(res.result).to.equal(userCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
return resolve();
});
});
});
it('Converts with application/csv header', (done) => {
it('Converts with application/csv header', () => {
return simpleServer.inject({
method: 'GET',
url: '/user',
headers: {
'Accept': 'application/csv'
}
}, (res) => {
return new Promise((resolve) => {
expect(res.result).to.equal(userCSV);
expect(res.headers['content-type']).to.equal('application/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
return simpleServer.inject({
method: 'GET',
url: '/user',
headers: {
'Accept': 'application/csv'
}
}, (res) => {
return done();
expect(res.result).to.equal(userCSV);
expect(res.headers['content-type']).to.equal('application/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
return resolve();
});
});
});
it('Converts when route ends with .csv', (done) => {
it('Converts when route ends with .csv', () => {
return simpleServer.inject({
method: 'GET',
url: '/user.csv'
}, (res) => {
return new Promise((resolve) => {
expect(res.result).to.equal(userCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
return simpleServer.inject({
method: 'GET',
url: '/user.csv'
}, (res) => {
return done();
expect(res.result).to.equal(userCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
return resolve();
});
});
});
it('Converts when route ends with .csv and has query params', (done) => {
it('Converts when route ends with .csv and has query params', () => {
return simpleServer.inject({
method: 'GET',
url: '/user.csv?q=1'
}, (res) => {
return new Promise((resolve) => {
expect(res.result).to.equal(userCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
expect(res.request.url.path).to.equal('/user?q=1');
return simpleServer.inject({
method: 'GET',
url: '/user.csv?q=1'
}, (res) => {
return done();
expect(res.result).to.equal(userCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
expect(res.request.url.path).to.equal('/user?q=1');
return resolve();
});
});
});
it('Still replies with JSON when asked', (done) => {
it('Still replies with JSON when asked', () => {
return simpleServer.inject({
method: 'GET',
url: '/user',
headers: {
Accept: 'application/json'
}
}, (res) => {
return new Promise((resolve) => {
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.result).to.equal(user);
return simpleServer.inject({
method: 'GET',
url: '/user',
headers: {
Accept: 'application/json'
}
}, (res) => {
return done();
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.result).to.equal(user);
return resolve();
});
});
});
it('Still replies with JSON when no accept header present', (done) => {
it('Still replies with JSON when no accept header present', () => {
return simpleServer.inject({
method: 'GET',
url: '/user',
headers: {
Accept: ''
}
}, (res) => {
return new Promise((resolve) => {
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.result).to.equal(user);
return simpleServer.inject({
method: 'GET',
url: '/user',
headers: {
Accept: ''
}
}, (res) => {
return done();
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.result).to.equal(user);
return resolve();
});
});
});
it('Still replies with JSON when no response schema is specified', (done) => {
it('Still replies with JSON when no response schema is specified', () => {
return simpleServer.inject({
method: 'GET',
url: '/userWithoutSchema',
headers: {
Accept: 'text/csv'
}
}, (res) => {
return new Promise((resolve) => {
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.result).to.equal(user);
return simpleServer.inject({
method: 'GET',
url: '/userWithoutSchema',
headers: {
Accept: 'text/csv'
}
}, (res) => {
return done();
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.result).to.equal(user);
return resolve();
});
});
});
it('Still replies with JSON when Accept header contains wildcard', (done) => {
it('Still replies with JSON when Accept header contains wildcard', () => {
return simpleServer.inject({
method: 'GET',
url: '/user',
headers: {
Accept: 'application/json, */*'
}
}, (res) => {
return new Promise((resolve) => {
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.result).to.equal(user);
return simpleServer.inject({
method: 'GET',
url: '/user',
headers: {
Accept: 'application/json, */*'
}
}, (res) => {
return done();
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.result).to.equal(user);
return resolve();
});
});
});
it('Passes on errors', (done) => {
it('Passes on errors', () => {
return simpleServer.inject({
method: 'GET',
url: '/error',
headers: {
Accept: 'text/csv'
}
}, (res) => {
return new Promise((resolve) => {
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.result).to.equal({
statusCode: 500,
error: 'Internal Server Error',
message: 'An internal server error occurred'
return simpleServer.inject({
method: 'GET',
url: '/error',
headers: {
Accept: 'text/csv'
}
}, (res) => {
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.result).to.equal({
statusCode: 500,
error: 'Internal Server Error',
message: 'An internal server error occurred'
});
return resolve();
});
return done();
});
});
it('Replies with the right response when there are similar routes with different methods', (done) => {
it('Replies with the right response when there are similar routes with different methods', () => {
return simpleServer.inject({
method: 'POST',
url: '/user',
headers: {
'Accept': 'text/csv'
}
}, (res) => {
return new Promise((resolve) => {
expect(res.result).to.equal(postUserCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
return simpleServer.inject({
method: 'POST',
url: '/user',
headers: {
'Accept': 'text/csv'
}
}, (res) => {
return done();
expect(res.result).to.equal(postUserCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
return resolve();
});
});

@@ -311,3 +357,3 @@ });

it('Converts more advanced, nested schema', (done) => {
it('Converts more advanced, nested schema', () => {

@@ -360,39 +406,324 @@ const server = new Hapi.Server();

return server.register(HapiCsv, (err) => {
return new Promise((resolve, reject) => {
expect(err, 'error').to.not.exist();
return server.register(HapiCsv, (err) => {
server.route([{
method: 'GET',
path: '/test',
config: {
handler: function (request, reply) {
expect(err, 'error').to.not.exist();
return reply(dataset);
},
response: {
schema: testResponseSchema
server.route([{
method: 'GET',
path: '/test',
config: {
handler: function (request, reply) {
return reply(dataset);
},
response: {
schema: testResponseSchema
}
}
}]);
return server.initialize((err) => {
expect(err, 'error').to.not.exist();
return server.inject({
method: 'GET',
url: '/test',
headers: {
'Accept': 'text/csv'
}
}, (res) => {
const expectedResult = 'testObject.testPropOne,testObject.testPropTwo,testObject.testPropThree,testNumber,testString,testEmail,testDate,testDateObject,testArray_0.testPropOne,testArray_0.testPropTwo,testArray_1.testPropOne,testArray_1.testPropTwo,testArray_2.testPropOne,testArray_2.testPropTwo,testArray_3.testPropOne,testArray_3.testPropTwo,testArray_4.testPropOne,testArray_4.testPropTwo,testPrimitiveArray_0,testPrimitiveArray_1,testPrimitiveArray_2,testPrimitiveArray_3,testPrimitiveArray_4,\n,,,"5","test","test@testprovider.com","2016-07-04T13:56:31.000Z","2016-07-04T13:56:31.000Z","1","One","2","Two","3","Three","4","Four",,,"5","5",,,,';
expect(res.result).to.equal(expectedResult);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
return server.stop((err) => {
if (err) {
return reject(err);
}
return resolve();
});
});
});
});
});
});
it('Test plugin with schema existing of primitive type', () => {
const server = new Hapi.Server();
server.connection();
return new Promise((resolve, reject) => {
return server.register(HapiCsv, (err) => {
expect(err, 'error').to.not.exist();
server.route([{
method: 'GET',
path: '/test',
config: {
handler: function (request, reply) {
return reply(5);
},
response: {
schema: Joi.number()
}
}
}]);
return server.initialize((err) => {
expect(err, 'error').to.not.exist();
return server.inject({
method: 'GET',
url: '/test',
headers: {
'Accept': 'text/csv'
}
}, (res) => {
expect(res.result).to.equal(5);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
return server.stop((err) => {
if (err) {
return reject(err);
}
return resolve();
});
});
});
});
});
});
it('Parse a value containing embedded double quotes', () => {
const server = new Hapi.Server();
server.connection();
return new Promise((resolve, reject) => {
return server.register(HapiCsv, (err) => {
expect(err, 'error').to.not.exist();
server.route([{
method: 'GET',
path: '/test',
config: {
handler: function (request, reply) {
return reply('I said: "Hello"');
},
response: {
schema: Joi.string()
}
}
}]);
return server.initialize((err) => {
expect(err, 'error').to.not.exist();
return server.inject({
method: 'GET',
url: '/test',
headers: {
'Accept': 'text/csv'
}
}, (res) => {
expect(res.result).to.equal('I said: ""Hello""');
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
return server.stop((err) => {
if (err) {
return reject(err);
}
return resolve();
});
});
});
});
});
});
});
// todo add array depth test
describe('Options', () => {
it('Uses the passed options', () => {
const user = {
first_name: 'firstName',
last_name: 'lastName',
age: 25,
tags: ['person', 'guitar']
};
const userCSV = 'first_name+last_name+age+tags_0+\n"firstName"+"lastName"+"25"+"person"+';
const server = new Hapi.Server();
server.connection();
return new Promise((resolve, reject) => {
return server.register({
register: HapiCsv,
options: {
separator: '+',
maximumElementsInArray: '1'
}
}]);
}, (err) => {
return server.initialize((err) => {
expect(err, 'error').to.not.exist();
server.route([{
method: 'GET',
path: '/test',
config: {
handler: function (request, reply) {
return reply(user);
},
response: {
schema: Joi.object().keys({
first_name: Joi.string(),
last_name: Joi.string(),
age: Joi.number(),
tags: Joi.array().items(Joi.string())
})
}
}
}]);
return server.initialize((err) => {
expect(err, 'error').to.not.exist();
return server.inject({
method: 'GET',
url: '/test',
headers: {
'Accept': 'text/csv'
}
}, (res) => {
expect(res.result, 'result').to.equal(userCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
return server.stop((err) => {
if (err) {
return reject(err);
}
return resolve();
});
});
});
});
});
});
});
describe('Dynamic schemas', () => {
it('Uses dynamic schemas', () => {
const user = {
first_name: 'firstName',
last_name: 'lastName',
age: 25,
tag: { id: 1, name: 'guitar' }
};
const userCSV = 'first_name,last_name,age,tag.id,tag.name,\n"firstName","lastName","25","1","guitar",';
const server = new Hapi.Server();
server.connection();
return new Promise((resolve, reject) => {
return server.register({
register: HapiCsv
}, (err) => {
expect(err, 'error').to.not.exist();
return server.inject({
server.route([{
method: 'GET',
url: '/test',
headers: {
'Accept': 'text/csv'
path: '/test',
config: {
handler: function (request, reply) {
return reply(user);
},
response: {
schema: Joi.object().keys({
first_name: Joi.string(),
last_name: Joi.string(),
age: Joi.number(),
tag: Joi.object()
})
},
plugins: {
'hapi-csv': {
'tag': (request, callback) => {
const schema = Joi.object().keys({
id: Joi.number(),
name: Joi.string()
});
return callback(null, schema);
}
}
}
}
}, (res) => {
}]);
const expectedResult = 'testObject.testPropOne,testObject.testPropTwo,testObject.testPropThree,testNumber,testString,testEmail,testDate,testDateObject,testArray_0.testPropOne,testArray_0.testPropTwo,testArray_1.testPropOne,testArray_1.testPropTwo,testArray_2.testPropOne,testArray_2.testPropTwo,testArray_3.testPropOne,testArray_3.testPropTwo,testArray_4.testPropOne,testArray_4.testPropTwo,testPrimitiveArray_0,testPrimitiveArray_1,testPrimitiveArray_2,testPrimitiveArray_3,testPrimitiveArray_4,\n,,,"5","test","test@testprovider.com","2016-07-04T13:56:31.000Z","2016-07-04T13:56:31.000Z","1","One","2","Two","3","Three","4","Four",,,"5","5",,,,';
return server.initialize((err) => {
expect(res.result).to.equal(expectedResult);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
expect(err, 'error').to.not.exist();
return server.stop(done);
return server.inject({
method: 'GET',
url: '/test',
headers: {
'Accept': 'text/csv'
}
}, (res) => {
expect(res.result, 'result').to.equal(userCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
return server.stop((err) => {
if (err) {
return reject(err);
}
return resolve();
});
});
});

@@ -403,8 +734,17 @@ });

it('Test plugin with schema existing of primitive type', (done) => {
it('Uses dynamic schemas: resolver function throws an error', (done) => {
const user = {
first_name: 'firstName',
last_name: 'lastName',
age: 25,
tag: { id: 1, name: 'guitar' }
};
const server = new Hapi.Server();
server.connection();
return server.register(HapiCsv, (err) => {
return server.register({
register: HapiCsv
}, (err) => {

@@ -419,6 +759,19 @@ expect(err, 'error').to.not.exist();

return reply(5);
return reply(user);
},
response: {
schema: Joi.number()
schema: Joi.object().keys({
first_name: Joi.string(),
last_name: Joi.string(),
age: Joi.number(),
tag: Joi.object()
})
},
plugins: {
'hapi-csv': {
'tag': (request, callback) => {
return callback(new Error('ERROR'));
}
}
}

@@ -440,5 +793,3 @@ }

expect(res.result).to.equal(5);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
expect(res.statusCode, 'statusCode').to.equal(500);

@@ -450,9 +801,32 @@ return server.stop(done);

});
});
it('Parse a value containing embedded double quotes', (done) => {
describe('Result key (e.g. for pagination)', () => {
it('Uses the result key', (done) => {
const result = {
page: 1,
items: [{
first_name: 'firstName1',
last_name: 'lastName1',
age: 25
}, {
first_name: 'firstName2',
last_name: 'lastName2',
age: 27
}]
};
const userCSV = 'first_name,last_name,age,\n"firstName1","lastName1","25",\n"firstName2","lastName2","27",';
const server = new Hapi.Server();
server.connection();
return server.register(HapiCsv, (err) => {
return server.register({
register: HapiCsv,
options: {
resultKey: 'items'
}
}, (err) => {

@@ -467,6 +841,15 @@ expect(err, 'error').to.not.exist();

return reply('I said: "Hello"');
return reply(result);
},
response: {
schema: Joi.string()
schema: Joi.object({
page: Joi.number(),
items: Joi.array().items(
Joi.object().keys({
first_name: Joi.string(),
last_name: Joi.string(),
age: Joi.number()
})
)
})
}

@@ -488,3 +871,3 @@ }

expect(res.result).to.equal('I said: ""Hello""');
expect(res.result, 'result').to.equal(userCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');

@@ -498,16 +881,16 @@ expect(res.headers['content-disposition']).to.equal('attachment;');

});
});
// todo add array depth test
describe('Options', () => {
it('Ignores the result key if not used in the response', (done) => {
it('Uses the passed options', (done) => {
const result = [{
first_name: 'firstName1',
last_name: 'lastName1',
age: 25
}, {
first_name: 'firstName2',
last_name: 'lastName2',
age: 27
}];
const user = {
first_name: 'firstName',
last_name: 'lastName',
age: 25,
tags: ['person', 'guitar']
};
const userCSV = 'first_name+last_name+age+tags_0+\n"firstName"+"lastName"+"25"+"person"+';
const userCSV = 'first_name,last_name,age,\n"firstName1","lastName1","25",\n"firstName2","lastName2","27",';

@@ -520,4 +903,3 @@ const server = new Hapi.Server();

options: {
separator: '+',
maximumElementsInArray: '1'
resultKey: 'items'
}

@@ -534,11 +916,12 @@ }, (err) => {

return reply(user);
return reply(result);
},
response: {
schema: Joi.object().keys({
first_name: Joi.string(),
last_name: Joi.string(),
age: Joi.number(),
tags: Joi.array().items(Joi.string())
})
schema: Joi.array().items(
Joi.object().keys({
first_name: Joi.string(),
last_name: Joi.string(),
age: Joi.number()
})
)
}

@@ -571,14 +954,17 @@ }

describe('Dynamic schemas', () => {
describe('xlsx export', () => {
it('Uses dynamic schemas', (done) => {
it('Transforms the response to an xlsx format', (done) => {
const user = {
first_name: 'firstName',
last_name: 'lastName',
age: 25,
tag: { id: 1, name: 'guitar' }
};
const result = [{
first_name: 'firstName1',
last_name: 'lastName1',
age: 25
}, {
first_name: 'firstName2',
last_name: 'lastName2',
age: 27
}];
const userCSV = 'first_name,last_name,age,tag.id,tag.name,\n"firstName","lastName","25","1","guitar",';
const expectedString = '<si><t>first_name</t></si><si><t>last_name</t></si><si><t>age</t></si><si><t>firstName1</t></si><si><t>lastName1</t></si><si><t>firstName2</t></si><si><t>lastName2</t></si>';

@@ -589,3 +975,7 @@ const server = new Hapi.Server();

return server.register({
register: HapiCsv
register: HapiCsv,
options: {
resultKey: 'items',
enableExcel: true
}
}, (err) => {

@@ -601,24 +991,12 @@

return reply(user);
return reply(result);
},
response: {
schema: Joi.object().keys({
first_name: Joi.string(),
last_name: Joi.string(),
age: Joi.number(),
tag: Joi.object()
})
},
plugins: {
'hapi-csv': {
'tag': (request, callback) => {
const schema = Joi.object().keys({
id: Joi.number(),
name: Joi.string()
});
return callback(null, schema);
}
}
schema: Joi.array().items(
Joi.object().keys({
first_name: Joi.string(),
last_name: Joi.string(),
age: Joi.number()
})
)
}

@@ -634,11 +1012,10 @@ }

method: 'GET',
url: '/test',
url: '/test.xlsx',
headers: {
'Accept': 'text/csv'
'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
}
}, (res) => {
expect(res.result, 'result').to.equal(userCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
expect(res.payload, 'payload').to.include(expectedString);
expect(res.headers['content-type']).to.equal('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8; header=present;');

@@ -651,11 +1028,12 @@ return server.stop(done);

it('Uses dynamic schemas: resolver function throws an error', (done) => {
it('Transforms the response to an xlsx format', (done) => {
const user = {
first_name: 'firstName',
const result = {
first_name: null,
last_name: 'lastName',
age: 25,
tag: { id: 1, name: 'guitar' }
age: 27
};
const expectedString = '<si><t>first_name</t></si><si><t>last_name</t></si><si><t>age</t></si><si><t>lastName</t></si>';
const server = new Hapi.Server();

@@ -665,3 +1043,7 @@ server.connection();

return server.register({
register: HapiCsv
register: HapiCsv,
options: {
resultKey: 'items',
enableExcel: true
}
}, (err) => {

@@ -677,19 +1059,12 @@

return reply(user);
return reply(result);
},
response: {
schema: Joi.object().keys({
first_name: Joi.string(),
last_name: Joi.string(),
age: Joi.number(),
tag: Joi.object()
})
},
plugins: {
'hapi-csv': {
'tag': (request, callback) => {
return callback(new Error('ERROR'));
}
}
schema: Joi.array().items(
Joi.object().keys({
first_name: Joi.string().allow(null),
last_name: Joi.string(),
age: Joi.number()
})
).single()
}

@@ -705,9 +1080,10 @@ }

method: 'GET',
url: '/test',
url: '/test.xlsx',
headers: {
'Accept': 'text/csv'
'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
}
}, (res) => {
expect(res.statusCode, 'statusCode').to.equal(500);
expect(res.payload, 'payload').to.include(expectedString);
expect(res.headers['content-type']).to.equal('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8; header=present;');

@@ -719,23 +1095,15 @@ return server.stop(done);

});
});
describe('Result key (e.g. for pagination)', () => {
it('Ignores the xlsx when enableExcel is false', (done) => {
it('Uses the result key', (done) => {
const result = [{
first_name: 'firstName1',
last_name: 'lastName1',
age: 25
}, {
first_name: 'firstName2',
last_name: 'lastName2',
age: 27
}];
const result = {
page: 1,
items: [{
first_name: 'firstName1',
last_name: 'lastName1',
age: 25
}, {
first_name: 'firstName2',
last_name: 'lastName2',
age: 27
}]
};
const userCSV = 'first_name,last_name,age,\n"firstName1","lastName1","25",\n"firstName2","lastName2","27",';
const server = new Hapi.Server();

@@ -747,3 +1115,4 @@ server.connection();

options: {
resultKey: 'items'
resultKey: 'items',
enableExcel: false
}

@@ -763,12 +1132,9 @@ }, (err) => {

response: {
schema: Joi.object({
page: Joi.number(),
items: Joi.array().items(
Joi.object().keys({
first_name: Joi.string(),
last_name: Joi.string(),
age: Joi.number()
})
)
})
schema: Joi.array().items(
Joi.object().keys({
first_name: Joi.string(),
last_name: Joi.string(),
age: Joi.number()
})
)
}

@@ -786,9 +1152,8 @@ }

headers: {
'Accept': 'text/csv'
'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
}
}, (res) => {
expect(res.result, 'result').to.equal(userCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
expect(res.statusCode, 'statusCode').to.equal(200);
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');

@@ -801,3 +1166,3 @@ return server.stop(done);

it('Ignores the result key if not used in the response', (done) => {
it('Ignores the xlsx when there is no xlsx extension or xlsx accept header', (done) => {

@@ -814,4 +1179,2 @@ const result = [{

const userCSV = 'first_name,last_name,age,\n"firstName1","lastName1","25",\n"firstName2","lastName2","27",';
const server = new Hapi.Server();

@@ -823,3 +1186,4 @@ server.connection();

options: {
resultKey: 'items'
resultKey: 'items',
enableExcel: true
}

@@ -858,9 +1222,8 @@ }, (err) => {

headers: {
'Accept': 'text/csv'
'Accept': 'application/json'
}
}, (res) => {
expect(res.result, 'result').to.equal(userCSV);
expect(res.headers['content-type']).to.equal('text/csv; charset=utf-8; header=present;');
expect(res.headers['content-disposition']).to.equal('attachment;');
expect(res.statusCode, 'statusCode').to.equal(200);
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');

@@ -867,0 +1230,0 @@ return server.stop(done);

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc