diff --git a/data/dp3-zip/data/countries.csv b/data/dp3-zip/data/countries.csv new file mode 100644 index 0000000..bebe322 --- /dev/null +++ b/data/dp3-zip/data/countries.csv @@ -0,0 +1,4 @@ +name,size +gb,100 +us,200 +cn,300 diff --git a/data/dp3-zip/datapackage.json b/data/dp3-zip/datapackage.json new file mode 100644 index 0000000..4f73480 --- /dev/null +++ b/data/dp3-zip/datapackage.json @@ -0,0 +1,22 @@ +{ + "name": "abc", + "resources": [ + { + "name": "countries", + "format": "csv", + "path": "data/countries.csv", + "schema": { + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "size", + "type": "number" + } + ] + } + } + ] +} diff --git a/package.json b/package.json index 388bd51..b7cf544 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "glob": "^7.1.2", "jschardet": "^1.5.1", "json-pointer": "^0.6.0", + "jszip": "^3.1.5", "lodash": "^4.13.1", "regenerator-runtime": "^0.11.0", "stream-to-async-iterator": "^0.2.0", @@ -81,6 +82,7 @@ "nyc": "^11.1.0", "sinon": "^2.1.0", "sinon-chai": "^2.9.0", + "tempy": "^0.2.1", "webpack": "^2.2.0", "webpack-bundle-size-analyzer": "^2.7.0", "webpack-dev-server": "^2.2.0", diff --git a/src/package.js b/src/package.js index 378738a..61258e5 100644 --- a/src/package.js +++ b/src/package.js @@ -1,4 +1,5 @@ const fs = require('fs') +const JSZip = require('jszip') const isEqual = require('lodash/isEqual') const isBoolean = require('lodash/isBoolean') const cloneDeep = require('lodash/cloneDeep') @@ -177,8 +178,38 @@ class Package { */ save(target) { return new Promise((resolve, reject) => { - const contents = JSON.stringify(this._currentDescriptor, null, 4) - fs.writeFile(target, contents, error => (!error) ? resolve() : reject(error)) + + // Save descriptor to json + if (target.endsWith('.json')) { + const contents = JSON.stringify(this._currentDescriptor, null, 4) + fs.writeFile(target, contents, error => (!error) ? resolve() : reject(error)) + + // Save package to zip + } else { + + // Prepare zip + const zip = new JSZip() + const descriptor = cloneDeep(this._currentDescriptor) + for (const [index, resource] of this.resources.entries()) { + if (!resource.name) continue + if (!resource.local) continue + let path = `data/${resource.name}` + const format = resource.descriptor.format + if (format) path = `${path}.${format.toLowerCase()}` + descriptor.resources[index].path = path + zip.file(path, resource.rawRead()) + } + zip.file('datapackage.json', JSON.stringify(descriptor, null, 4)) + + // Write zip + zip + .generateNodeStream({type: 'nodebuffer', streamFiles: true}) + .pipe(fs.createWriteStream(target).on('error', error => reject(error))) + .on('error', error => reject(error)) + .on('finish', () => resolve(true)) + + } + }) } diff --git a/test/package.js b/test/package.js index f53aec1..cb4a609 100644 --- a/test/package.js +++ b/test/package.js @@ -1,7 +1,9 @@ const fs = require('fs') +const JSZip = require('jszip') const axios = require('axios') const sinon = require('sinon') const {assert} = require('chai') +const {promisify} = require('util') const {catchError} = require('./helpers') const cloneDeep = require('lodash/cloneDeep') const AxiosMock = require('axios-mock-adapter') @@ -674,4 +676,39 @@ describe('Package', () => { }) + describe('#zip', () => { + + it('should save package as a zip', async function() { + if (process.env.USER_ENV === 'browser') this.skip() + + // Save as a zip + const dp = await Package.load('data/dp3-zip/datapackage.json') + const target = require('tempy').file({extension: 'zip'}) + const result = await dp.save(target) + assert.ok(result) + + // Assert file names + const zip = JSZip() + await zip.loadAsync(promisify(fs.readFile)(target)) + assert.deepEqual(zip.file('datapackage.json').name, 'datapackage.json') + assert.deepEqual(zip.file('data/countries.csv').name, 'data/countries.csv') + + // Assert contents + const descContents = await zip.file('datapackage.json').async('string') + const dataContents = await zip.file('data/countries.csv').async('string') + assert.deepEqual(JSON.parse(descContents), dp.descriptor) + assert.deepEqual(dataContents, 'name,size\ngb,100\nus,200\ncn,300\n') + + }) + + it('should raise saving package as a zip to the bad path', async function() { + if (process.env.USER_ENV === 'browser') this.skip() + const dp = await Package.load('data/dp3-zip/datapackage.json') + const error = await catchError(dp.save.bind(dp), 'non-existent/datapackage.zip') + assert.include(error.message, 'no such file or directory') + assert.include(error.message, 'non-existent/datapackage.zip') + }) + + }) + })