diff --git a/.eslintrc.yml b/.eslintrc.yml index 0b42c9c..4f4b045 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -15,6 +15,7 @@ "rules": { "strict": 0, "import/no-extraneous-dependencies": 0, + "import/no-cycle": 0, "max-len": ["error", { "code": 120, "ignoreComments": true, diff --git a/package.json b/package.json index 4741e1f..7529a2a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@keboola/storage-api-js-client", - "version": "2.4.2", + "version": "2.5.0", "description": "Javascript client for Keboola Storage API", "repository": "https://github.com/keboola/storage-api-js-client", "author": "Jakub Matejka ", @@ -13,7 +13,7 @@ "aws-sdk": "^2.236.1", "axios": "^0.18.0", "axios-retry": "^3.1.0", - "csv-string": "^3.1.2", + "csv-parse": "^4.4.1", "http-errors": "^1.7.0", "lodash": "^4.17.10", "qs": "^6.7.0", @@ -30,13 +30,13 @@ "babel-loader": "^8.0.4", "eslint": "^5.4.0", "eslint-config-airbnb-base": "^13.1.0", - "eslint-plugin-flowtype": "^2.50.3", - "eslint-plugin-flowtype-errors": "^3.6.0", + "eslint-plugin-flowtype": "^3.9.0", + "eslint-plugin-flowtype-errors": "^4.1.0", "eslint-plugin-import": "^2.14.0", - "flow-bin": "^0.82.0", + "flow-bin": "^0.98.1", "flow-copy-source": "^2.0.2", - "mocha": "^5.1.1", - "unexpected": "^10.37.7" + "mocha": "^6.1.4", + "unexpected": "^11.5.1" }, "scripts": { "test": "./node_modules/.bin/mocha --bail --exit --timeout 0 --require @babel/register --require source-map-support test", diff --git a/src/Tables.js b/src/Tables.js index a628d6f..76a2fdf 100644 --- a/src/Tables.js +++ b/src/Tables.js @@ -1,13 +1,12 @@ // @flow import _ from 'lodash'; +import aws from 'aws-sdk'; import axios from 'axios'; import axiosRetry from 'axios-retry'; -import csvString from 'csv-string'; +import parse from 'csv-parse/lib/sync'; import fs from 'fs'; import Storage from './Storage'; -const aws = require('aws-sdk'); - axiosRetry(axios, { retries: 5 }); export default class Tables { @@ -78,6 +77,11 @@ export default class Tables { return this.storage.request('get', `tables/${id}`); } + async preview(tableId: string, options: Object = {}): Promise> { + const res = await this.storage.request('get', `tables/${tableId}/data-preview`, options); + return parse(res, { columns: true }); + } + async export(tableId: string, options: Object = {}): Promise> { const requestRes = await this.storage.request('post', `tables/${tableId}/export-async`, options); const jobRes = await this.storage.Jobs.wait(requestRes.id); @@ -99,9 +103,14 @@ export default class Tables { Bucket: file.s3Path.bucket, Key: sliceUrl.substr(sliceUrl.indexOf('/', 5) + 1), }).promise())); + + // Read contents of all slice files const csvFiles = _.map(s3Files, s3File => s3File.Body.toString('utf8')); - const csvSlices = _.map(csvFiles, csvFile => csvString.parse(csvFile)); - return _.reduce(csvSlices); + + // Parse csv of each slice to array + const csvSlices = _.map(csvFiles, csvFile => parse(csvFile), 1); + // Union all arrays + return _.flatten(csvSlices); } delete(id: string): Promise { diff --git a/test/Buckets.js b/test/Buckets.js index bb32453..032dead 100644 --- a/test/Buckets.js +++ b/test/Buckets.js @@ -41,26 +41,26 @@ describe('Storage.Buckets', () => { const res = await expect(storage.Buckets.list(), 'to be fulfilled'); expect(res, 'to be an', 'array'); if (_.size(res) > 0) { - expect(res, 'to have items satisfying', (item) => { + expect(res, 'to have items satisfying', expect.it((item) => { expect(item, 'to have key', 'id'); expect(item.id, 'not to be', bucketId); - }); + })); } await storage.request('post', 'buckets', { stage: 'in', name: bucketName }); const res2 = await expect(storage.Buckets.list(), 'to be fulfilled'); - expect(res2, 'to have an item satisfying', (item) => { + expect(res2, 'to have an item satisfying', expect.it((item) => { expect(item, 'to have key', 'id'); expect(item.id, 'to be', bucketId); expect(item, 'not to have key', 'metadata'); - }); + })); const res3 = await expect(storage.Buckets.list(['metadata']), 'to be fulfilled'); - expect(res3, 'to have an item satisfying', (item) => { + expect(res3, 'to have an item satisfying', expect.it((item) => { expect(item, 'to have key', 'id'); expect(item.id, 'to be', bucketId); expect(item, 'to have key', 'metadata'); - }); + })); await storage.request('delete', `buckets/${bucketId}`); }); diff --git a/test/Configurations.js b/test/Configurations.js index 536459e..084f251 100644 --- a/test/Configurations.js +++ b/test/Configurations.js @@ -75,9 +75,9 @@ describe('Storage.Configurations', () => { const res = await expect(storage.Configurations.listComponents(), 'to be fulfilled'); expect(_.size(res), 'to be greater than', 0); - expect(res, 'to have an item satisfying', (i) => { + expect(res, 'to have an item satisfying', expect.it((i) => { expect(i.id, 'to be', component); - }); + })); const res2 = await expect(storage.Configurations.list(component), 'to be fulfilled'); expect(res2, 'to have length', 1); diff --git a/test/Tables.js b/test/Tables.js index 76ca600..d806654 100644 --- a/test/Tables.js +++ b/test/Tables.js @@ -103,6 +103,17 @@ describe('Storage.Tables', () => { expect(res.rowsCount, 'to be', 5); }); + it('preview', async () => { + await createTestTable(storage, bucketId, tableName); + await expect(storage.request('get', `tables/${tableId}`), 'to be fulfilled'); + const res = await storage.Tables.preview(tableId); + expect(res, 'to be a', 'array'); + expect(_.keys(res), 'to have length', 5); + expect(res[0], 'to have key', 'id'); + expect(res[0], 'to have key', 'name'); + expect(res[0], 'to have key', 'price'); + }); + it('export', async () => { await createTestTable(storage, bucketId, tableName); await expect(storage.request('get', `tables/${tableId}`), 'to be fulfilled');