Comparing version 4.0.1 to 4.1.0
@@ -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 @@ |
{ | ||
"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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
57044
1193
5
+ Addedxlsx@^0.14.1
+ Addedadler-32@1.2.01.3.1(transitive)
+ Addedcfb@1.2.2(transitive)
+ Addedcodepage@1.14.0(transitive)
+ Addedcommander@2.14.12.17.1(transitive)
+ Addedcrc-32@1.2.2(transitive)
+ Addedexit-on-epipe@1.0.1(transitive)
+ Addedfrac@1.1.2(transitive)
+ Addedprintj@1.1.2(transitive)
+ Addedssf@0.10.3(transitive)
+ Addedxlsx@0.14.5(transitive)
- Removedhoek@5.0.4(transitive)
Updatedhoek@^6.0.3