From b5907d8781338ec083fc5f3ac2531f3e4eda56ca Mon Sep 17 00:00:00 2001 From: Federico Pinna Date: Tue, 13 Sep 2022 17:58:18 +0200 Subject: [PATCH] fix: extract nested props in arrays as x.0.y also unwind empty arrays, prepending ! to the unwind opt --- src/util.ts | 11 ++++++- test/ts/pipeline.test.ts | 68 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/util.ts b/src/util.ts index 14de994..f58de9b 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,6 @@ import { AnyMongoAbility, ForbiddenError, subject } from '@casl/ability'; import { deleteProperty, getProperty, setProperty } from 'dot-prop'; +import { dot } from 'eredita'; import _ from 'lodash'; import { DateTime } from 'luxon'; import { ObjectId } from 'mongodb'; @@ -338,6 +339,7 @@ export function checkAbility(ability: AnyMongoAbility, resource: string, action: export interface CSVOptions { fields: string[] | { [key: string]: string }; unwind?: string; + forceUnwind?: boolean; separator?: string; decimal?: string; quotes?: boolean; @@ -367,6 +369,10 @@ export function toCSV(data: any[], options: CSVOptions): string { data.forEach((originalItem) => { const unwound: any[] = []; if (options.unwind) { + if (options.unwind[0] === '!') { + options.forceUnwind = true; + options.unwind = options.unwind.substring(1); + } if (originalItem[options.unwind]?.length) { originalItem[options.unwind].forEach((i) => { unwound.push({ @@ -374,6 +380,9 @@ export function toCSV(data: any[], options: CSVOptions): string { [options.unwind!]: i, }); }); + } else if (options.forceUnwind) { + originalItem[options.unwind] = undefined; + unwound.push(originalItem); } } else { unwound.push(originalItem); @@ -382,7 +391,7 @@ export function toCSV(data: any[], options: CSVOptions): string { const l: string[] = []; // TODO optimize with a transversal map for (let k in fieldMap) { - const value: any = getProperty(item, k); + const value: any = dot(item, k); if (options.decimal && typeof value === 'number') { l.push(value.toString().replace('.', options.decimal)); } else if (value instanceof Date) { diff --git a/test/ts/pipeline.test.ts b/test/ts/pipeline.test.ts index eaca344..d7043e0 100644 --- a/test/ts/pipeline.test.ts +++ b/test/ts/pipeline.test.ts @@ -383,7 +383,7 @@ XXX,YYY,qqq`); }); }); - it('should ship an object if the unwind field is not an array with at least an element (1)', function () { + it('should skip an object if the unwind field is not an array with at least an element (1)', function () { csv_options = { fields: ['a', 'b', 'c'], header: true, @@ -404,7 +404,7 @@ XXX,YYY,qqq`); }); }); - it('should ship an object if the unwind field is not an array with at least an element (2)', function () { + it('should skip an object if the unwind field is not an array with at least an element (2)', function () { csv_options = { fields: ['a', 'b', 'c'], header: true, @@ -425,7 +425,7 @@ XXX,YYY,qqq`); }); }); - it('should ship an object if the unwind field is not an array with at least an element (3)', function () { + it('should skip an object if the unwind field is not an array with at least an element (3)', function () { csv_options = { fields: ['a', 'b', 'c'], header: true, @@ -446,6 +446,68 @@ XXX,YYY,qqq`); }); }); + it('should unwind an empty array, if forced', function () { + csv_options = { + fields: ['a', 'b', 'c'], + header: true, + unwind: '!c', + }; + csv_data = [ + { a: 'AAA', b: 'BBB' }, + { a: 'XXX', b: 'YYY', c: ['ppp', 'qqq'] }, + ]; + return request + .get('/tests') + .expect(200) + .expect('Content-Type', /text\/csv/) + .then(({ text: data }) => { + data.should.equal(`a,b,c +AAA,BBB, +XXX,YYY,ppp +XXX,YYY,qqq`); + }); + }); + + it('should return the requested nested fields as csv (1)', function () { + csv_options = { + fields: ['a', 'b', 'c.d'], + }; + csv_data = [ + { a: 'AAA', b: 'BBB', c: { d: 1 } }, + { a: 'XXX', b: 'YYY', c: { d: 2 } }, + ]; + return request + .get('/tests') + .expect(200) + .expect('Content-Type', /text\/csv/) + .then(({ text: data }) => { + data.should.equal( + `AAA,BBB,1 +XXX,YYY,2` + ); + }); + }); + + it('should return the requested nested fields as csv (2)', function () { + csv_options = { + fields: ['a', 'b', 'c.0.d'], + }; + csv_data = [ + { a: 'AAA', b: 'BBB', c: [{ d: 1 }, { d: 2 }] }, + { a: 'XXX', b: 'YYY', c: [{ d: 3 }, { d: 4 }] }, + ]; + return request + .get('/tests') + .expect(200) + .expect('Content-Type', /text\/csv/) + .then(({ text: data }) => { + data.should.equal( + `AAA,BBB,1 +XXX,YYY,3` + ); + }); + }); + it('should format dates as ISO', function () { csv_options = { fields: ['a', 'b'],