diff --git a/app.js b/app.js index b2e9674..68278b7 100644 --- a/app.js +++ b/app.js @@ -17,7 +17,9 @@ app.set('view engine', 'jten'); // uncomment after placing your favicon in /public app.use(morgan('dev')); app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({ extended: false })); +app.use(bodyParser.urlencoded({ + extended: false, +})); app.use('/', require('./routes/index')); app.use('/', require('./routes/service-discovery')); diff --git a/lib/citizen.js b/lib/citizen.js index c4f2edc..98a4062 100644 --- a/lib/citizen.js +++ b/lib/citizen.js @@ -1,8 +1,12 @@ /* eslint-disable no-console */ -const { format } = require('util'); +const { + format, +} = require('util'); const commander = require('commander'); const Listr = require('listr'); -const { red } = require('colors'); +const { + red, +} = require('colors'); const debug = require('debug'); const citizen = require('../package.json'); @@ -56,33 +60,32 @@ const runPublish = (namespace, name, provider, version, cmd) => { const targetDir = process.cwd(); const moduleFullPath = `${namespace}/${name}/${provider}/${moduleversion}`; - const tasks = new Listr([ - { - title: 'compress the terraform module', - task: async (ctx) => { - const files = await makeFileList(targetDir); - verbose(`files to compress: ${files}`); - const tarball = await makeTarball(targetDir, files); - ctx.tarball = tarball; - }, + const tasks = new Listr([{ + title: 'compress the terraform module', + task: async (ctx) => { + const files = await makeFileList(targetDir); + verbose(`files to compress: ${files}`); + const tarball = await makeTarball(targetDir, files); + ctx.tarball = tarball; }, - { - title: `publish ${moduleFullPath}`, - task: async (ctx) => { - try { - const res = await publish(registryAddr, moduleFullPath, ctx.tarball, owner); - verbose(`response status code form registry: ${res.statusCode}`); - if (res.statusCode >= 400) { - throw new Error(`The registry server is something wrong: ${res.statusMessage}`); - } - } catch (err) { - if (err.code === 'ECONNREFUSED') { - throw new Error('The registry server doesn\'t response. Please check the registry.'); - } - throw err; + }, + { + title: `publish ${moduleFullPath}`, + task: async (ctx) => { + try { + const res = await publish(registryAddr, moduleFullPath, ctx.tarball, owner); + verbose(`response status code form registry: ${res.statusCode}`); + if (res.statusCode >= 400) { + throw new Error(`The registry server is something wrong: ${res.statusMessage}`); + } + } catch (err) { + if (err.code === 'ECONNREFUSED') { + throw new Error('The registry server doesn\'t response. Please check the registry.'); } - }, + throw err; + } }, + }, ]); tasks.run().catch((err) => { @@ -125,7 +128,9 @@ commander }); commander - .command('*', { noHelp: true }) + .command('*', { + noHelp: true, + }) .action((env) => { console.error(`No command: ${env}`); }); diff --git a/lib/logger.js b/lib/logger.js index f292e62..99a98f7 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -2,6 +2,9 @@ const pino = require('pino'); const loggerName = process.env.npm_package_name || 'citizen'; -const logger = pino({ name: loggerName, level: 'error' }); +const logger = pino({ + name: loggerName, + level: 'error', +}); module.exports = logger; diff --git a/lib/module.js b/lib/module.js index 0872fbd..2138d00 100644 --- a/lib/module.js +++ b/lib/module.js @@ -2,7 +2,9 @@ const globby = require('globby'); const tar = require('tar'); const semver = require('semver'); const request = require('request'); -const { promisify } = require('util'); +const { + promisify, +} = require('util'); const verbose = require('debug')('citizen:client'); const post = promisify(request.post); @@ -32,13 +34,11 @@ const makeFileList = async (target = __dirname) => { }; const makeTarball = (target = __dirname, filelist = []) => new Promise((resolve, reject) => { - const compressed = tar.create( - { - cwd: target, - gzip: true, - }, - filelist, - ); + const compressed = tar.create({ + cwd: target, + gzip: true, + }, + filelist); const buffers = []; compressed diff --git a/lib/module.spec.js b/lib/module.spec.js index ed2ba8c..3dd8f68 100644 --- a/lib/module.spec.js +++ b/lib/module.spec.js @@ -1,9 +1,15 @@ /* eslint-disable no-unused-expressions */ const fs = require('fs'); const path = require('path'); -const { expect } = require('chai'); -const { Duplex } = require('stream'); -const { promisify } = require('util'); +const { + expect, +} = require('chai'); +const { + Duplex, +} = require('stream'); +const { + promisify, +} = require('util'); const tar = require('tar'); const recursive = require('recursive-readdir'); const rimraf = require('rimraf'); @@ -113,7 +119,9 @@ describe('module\'s', () => { tarball.push(compressed); tarball.push(null); - tarball.pipe(tar.x({ cwd: UNTAR_DIR })) + tarball.pipe(tar.x({ + cwd: UNTAR_DIR, + })) .on('finish', () => { recursive(UNTAR_DIR, (err, list) => { expect(list).to.have.members([ @@ -142,7 +150,9 @@ describe('module\'s', () => { tarball.push(compressed); tarball.push(null); - tarball.pipe(tar.x({ cwd: UNTAR_DIR })) + tarball.pipe(tar.x({ + cwd: UNTAR_DIR, + })) .on('finish', () => { recursive(UNTAR_DIR, (err, list) => { expect(list).to.have.members([ diff --git a/lib/storage.js b/lib/storage.js index 24d9543..a94b56c 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -6,11 +6,17 @@ let getModule; if (process.env.CITIZEN_STORAGE === 's3') { ({ - type, saveModule, hasModule, getModule, + type, + saveModule, + hasModule, + getModule, } = require('../storages/s3')); } else { ({ - type, saveModule, hasModule, getModule, + type, + saveModule, + hasModule, + getModule, } = require('../storages/file')); } diff --git a/lib/store.js b/lib/store.js index a2dc369..05ae6fb 100644 --- a/lib/store.js +++ b/lib/store.js @@ -9,11 +9,23 @@ let increaseDownload; if (process.env.CITIZEN_DATABASE === 'mongodb') { ({ - db, save, findOne, findAll, getVersions, getLatestVersion, increaseDownload, + db, + save, + findOne, + findAll, + getVersions, + getLatestVersion, + increaseDownload, } = require('../stores/mongodb')); } else { ({ - db, save, findOne, findAll, getVersions, getLatestVersion, increaseDownload, + db, + save, + findOne, + findAll, + getVersions, + getLatestVersion, + increaseDownload, } = require('../stores/nedb')); } diff --git a/lib/util.js b/lib/util.js index 4c6db7a..59b1e36 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1,15 +1,6 @@ -const { readFile } = require('fs'); -const { basename, extname, join } = require('path'); -const { promisify } = require('util'); -const hcl = require('gopher-hcl'); -const tar = require('tar'); -const { Duplex } = require('stream'); -const { URLSearchParams } = require('url'); -const recursive = require('recursive-readdir'); -const _ = require('lodash'); -const tmp = require('tmp'); - -const readFileProm = promisify(readFile); +const { + URLSearchParams, +} = require('url'); const makeUrl = (req, search) => { const params = new URLSearchParams(search); @@ -19,120 +10,6 @@ const makeUrl = (req, search) => { return `${req.baseUrl}${req._parsedUrl.pathname}${searchStr}`; }; -const ignore = (file, stats) => { - if ((stats.isDirectory() || extname(file) === '.tf') && !basename(file).startsWith('._')) { - return false; - } - return true; -}; - -const hclToJson = async (filePath) => { - const content = await readFileProm(filePath); - const json = hcl.parse(content.toString()); - - return json; -}; - -const extractDefinition = async (files, targetPath) => { - const tfFiles = files.filter((f) => { - const relativePath = f.replace(targetPath, ''); - if (relativePath.lastIndexOf('/') > 0) { - return false; - } - return true; - }); - - const promises = tfFiles.map(async (r) => { - const j = await hclToJson(r); - return j; - }); - const list = await Promise.all(promises); - - return _.reduce(list, (l, accum) => _.merge(accum, l), {}); -}; - -const nomarlizeModule = module => ({ - path: '', - name: module.name || '', - readme: '', - empty: !module, - inputs: module.variable - ? Object.keys(module.variable).map(name => ({ name, ...module.variable[name] })) - : [], - outputs: module.output - ? Object.keys(module.output).map(name => ({ name, ...module.output[name] })) - : [], - dependencies: [], - resources: module.resource - ? Object.keys(module.resource).map(type => ({ name: Object.keys(module.resource)[0], type })) - : [], -}); - -const extractSubmodules = async (definition, files, targetPath) => { - let pathes = []; - if (definition.module) { - const submodules = Object.keys(definition.module).map(key => definition.module[key].source); - pathes = _.uniq(submodules); - } - - const promises = pathes.map(async (p) => { - const data = await extractDefinition(files, join(targetPath, p)); - data.name = p.substr(p.lastIndexOf('/') + 1); - - let result = [data]; - if (data.module) { - const m = await extractSubmodules(data, files, join(targetPath, p)); - result = result.concat(m); - } - return result; - }); - - const submodules = _.flatten(await Promise.all(promises)); - return submodules; -}; - -const parseHcl = (moduleName, compressedModule) => new Promise((resolve, reject) => { - const stream = new Duplex(); - stream.push(compressedModule); - stream.push(null); - - tmp.dir({ unsafeCleanup: true }, (err, tempDir, cleanupCallback) => { - if (err) { return reject(err); } - - return stream.pipe(tar.x({ cwd: tempDir })) - .on('finish', async () => { - try { - const files = await recursive(tempDir, [ignore]); - - // make a root module definition - const rootData = await extractDefinition(files, tempDir); - rootData.name = moduleName; - const rootDefinition = nomarlizeModule(rootData); - - // make submodules definition - const submodulesData = await extractSubmodules(rootData, files, tempDir); - const submodulesDefinition = submodulesData.map(s => nomarlizeModule(s)); - submodulesDefinition.forEach((s) => { - let modulePath = files.find(f => f.includes(`/${s.name}/`)); - modulePath = modulePath.replace(`${tempDir}/`, ''); - modulePath = modulePath.substr(0, modulePath.lastIndexOf('/')); - s.path = modulePath; // eslint-disable-line no-param-reassign - }); - - resolve({ - root: rootDefinition, - submodules: submodulesDefinition, - }); - } catch (e) { - reject(e); - } finally { - cleanupCallback(); - } - }); - }); -}); - module.exports = { makeUrl, - parseHcl, }; diff --git a/lib/util.spec.js b/lib/util.spec.js index fb3d57a..92cd87a 100644 --- a/lib/util.spec.js +++ b/lib/util.spec.js @@ -1,11 +1,10 @@ -const { expect } = require('chai'); -const { promisify } = require('util'); -const fs = require('fs'); -const path = require('path'); +const { + expect, +} = require('chai'); -const { makeUrl, parseHcl } = require('./util'); - -const readFile = promisify(fs.readFile); +const { + makeUrl, +} = require('./util'); describe('util\'s', () => { describe('makeUrl()', () => { @@ -17,7 +16,10 @@ describe('util\'s', () => { }, }; - const result = makeUrl(req, { offset: 20, limit: 10 }); + const result = makeUrl(req, { + offset: 20, + limit: 10, + }); expect(result).to.equal('/v1/modules/hashicorp/consul?offset=20&limit=10'); }); @@ -33,22 +35,4 @@ describe('util\'s', () => { expect(result).to.equal('/v1/modules/hashicorp/consul'); }); }); - - describe('parseHcl()', () => { - let tarball; - - before(async () => { - const tarballPath = path.join(__dirname, '../test/fixture/complex.tar.gz'); - tarball = await readFile(tarballPath); - }); - - it('should make JSON from HCL in a compressed module file ', async () => { - const result = await parseHcl('citizen', tarball); - - expect(result).to.have.property('root'); - expect(result.root).to.have.property('name').to.equal('citizen'); - expect(result).to.have.property('submodules').to.be.an.instanceof(Array); - expect(result.submodules).to.have.lengthOf(3); - }); - }); }); diff --git a/package-lock.json b/package-lock.json index 3ce4130..0c55dbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "citizen", - "version": "0.3.3", + "version": "0.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -83,6 +83,12 @@ "any-observable": "^0.3.0" } }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -108,6 +114,37 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.44.tgz", "integrity": "sha512-HY3SK7egERHGUfY8p6ztXIEQWcIPHouYhCGcLAPQin7gE2G/fALFz+epnMwcxKUS6aKqTVoAFdi+t1llQd3xcw==" }, + "@types/request": { + "version": "2.48.2", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.2.tgz", + "integrity": "sha512-gP+PSFXAXMrd5PcD7SqHeUjdGshAI8vKQ3+AvpQr3ht9iQea+59LOKvKITcQI+Lg+1EIkDP6AFSBUJPWG8GDyA==", + "dev": true, + "requires": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + }, + "dependencies": { + "form-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.0.tgz", + "integrity": "sha512-WXieX3G/8side6VIqx44ablyULoGruSde5PNTxoUyo5CeyAMX6nVWUd0rgist/EuX655cjhUhTo1Fo3tRYqbcA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/tough-cookie": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz", + "integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==", + "dev": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -3010,14 +3047,6 @@ } } }, - "gopher-hcl": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/gopher-hcl/-/gopher-hcl-0.1.0.tgz", - "integrity": "sha1-BYKgIiku4Ka6msYW+SRbXcL2MU8=", - "requires": { - "mixin-deep": "^1.2.0" - } - }, "got": { "version": "6.7.1", "resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz", @@ -4571,16 +4600,25 @@ "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, "ngrok": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/ngrok/-/ngrok-3.1.1.tgz", - "integrity": "sha512-dCW/Ni12GRBL7XIyiFmilKOfCW7UVFf65I/HpE8FC5rXGJwdhIYLc9Qr05GRb6hNs6fZGwyLpcDLnDhUSgZasQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/ngrok/-/ngrok-3.2.5.tgz", + "integrity": "sha512-FWWQJSg8A1L6prZmT53onZMiFiaY+CfDgS9YStKjbE3qf2WDmRdi6kNBFvQKD2ARSv/te+rqeizAOGSUH5X56w==", "dev": true, "requires": { - "@types/node": "^8.10.30", + "@types/node": "^8.10.50", + "@types/request": "^2.48.2", "decompress-zip": "^0.3.2", "request": "^2.88.0", - "request-promise-native": "^1.0.5", + "request-promise-native": "^1.0.7", "uuid": "^3.3.2" + }, + "dependencies": { + "@types/node": { + "version": "8.10.52", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.52.tgz", + "integrity": "sha512-2RbW7WXeLex6RI+kQSxq6Ym0GiVcODeQ4Km7MnnTX5BHdOGQnqVa+s6AUmAW+OFYAJ8wv9QxvNZXm7/kBdGTVw==", + "dev": true + } } }, "nice-try": { @@ -4900,7 +4938,8 @@ "os-tmpdir": { "version": "1.0.2", "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true }, "p-defer": { "version": "1.0.0", @@ -5451,7 +5490,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { @@ -6186,7 +6225,7 @@ }, "string_decoder": { "version": "0.10.31", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true }, @@ -6424,6 +6463,7 @@ "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, "requires": { "os-tmpdir": "~1.0.2" } diff --git a/package.json b/package.json index 9c20b26..84da5c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "citizen", - "version": "0.3.3", + "version": "0.4.0", "private": true, "bin": { "citizen": "./bin/citizen" @@ -12,7 +12,7 @@ "test": "mocha --exit --recursive -t 20000 $(find . -name '*.spec.js' -not -path './node_modules/*')", "clean": "rimraf dist", "prebuild": "npm run clean", - "build": "pkg . --out-path dist --targets node10-linux-x64,node10-macos-x64,node10-win-x64" + "build": "pkg . --out-path dist --targets node10-alpine-x64,node10-linux-x64,node10-macos-x64,node10-win-x64" }, "dependencies": { "aws-sdk": "^2.425.0", @@ -23,7 +23,6 @@ "express": "^4.16.4", "glob-gitignore": "^1.0.14", "globby": "^9.1.0", - "gopher-hcl": "^0.1.0", "helmet": "^3.16.0", "jten": "^0.2.0", "listr": "^0.14.2", @@ -39,7 +38,6 @@ "rimraf": "^2.6.3", "semver": "^5.6.0", "tar": "^4.4.6", - "tmp": "0.0.33", "uuid": "^3.3.2" }, "devDependencies": { @@ -50,7 +48,7 @@ "eslint-plugin-mocha": "^5.3.0", "get-port": "^4.2.0", "mocha": "^6.0.2", - "ngrok": "^3.1.1", + "ngrok": "^3.2.5", "nock": "^10.0.6", "nodemon": "^1.18.10", "pkg": "^4.3.7", diff --git a/routes/download.js b/routes/download.js index bef6611..86b107b 100644 --- a/routes/download.js +++ b/routes/download.js @@ -1,13 +1,23 @@ -const { Router } = require('express'); +const { + Router, +} = require('express'); -const { findOne, getLatestVersion, increaseDownload } = require('../lib/store'); -const { getModule } = require('../lib/storage'); +const { + findOne, + getLatestVersion, + increaseDownload, +} = require('../lib/store'); +const { + getModule, +} = require('../lib/storage'); const router = Router(); // https://www.terraform.io/docs/registry/api.html#download-source-code-for-a-specific-module-version router.get('/:namespace/:name/:provider/:version/download', async (req, res, next) => { - const options = { ...req.params }; + const options = { + ...req.params, + }; const module = await findOne(options); @@ -21,7 +31,9 @@ router.get('/:namespace/:name/:provider/:version/download', async (req, res, nex // https://www.terraform.io/docs/registry/api.html#download-the-latest-version-of-a-module router.get('/:namespace/:name/:provider/download', async (req, res, next) => { - const options = { ...req.params }; + const options = { + ...req.params, + }; const module = await getLatestVersion(options); @@ -35,7 +47,9 @@ router.get('/:namespace/:name/:provider/download', async (req, res, next) => { // download a module router.get('/tarball/:namespace/:name/:provider/:version/*.tar.gz', async (req, res, next) => { - const options = { ...req.params }; + const options = { + ...req.params, + }; const module = await findOne(options); diff --git a/routes/download.spec.js b/routes/download.spec.js index 232065f..8cf5341 100644 --- a/routes/download.spec.js +++ b/routes/download.spec.js @@ -1,18 +1,31 @@ const request = require('supertest'); -const { expect } = require('chai'); +const { + expect, +} = require('chai'); const fs = require('fs'); const path = require('path'); -const { promisify } = require('util'); +const { + promisify, +} = require('util'); const rimraf = promisify(require('rimraf')); const app = require('../app'); -const { deleteDbAll } = require('../test/helper'); -const { db, save } = require('../lib/store'); +const { + deleteDbAll, +} = require('../test/helper'); +const { + db, + save, +} = require('../lib/store'); describe('GET /v1/modules/:namespace/:name/:provider/:version/download', () => { before(async () => { await save({ - namespace: 'download', name: 'source', provider: 'aws', version: '1.2.0', location: 'download/source/aws/1.2.0/module.tar.gz', + namespace: 'download', + name: 'source', + provider: 'aws', + version: '1.2.0', + location: 'download/source/aws/1.2.0/module.tar.gz', }); }); @@ -40,10 +53,18 @@ describe('GET /v1/modules/:namespace/:name/:provider/:version/download', () => { describe('GET /v1/modules/:namespace/:name/:provider/download', () => { before(async () => { await save({ - namespace: 'download', name: 'source', provider: 'aws', version: '1.2.0', location: 'download/source/aws/1.2.0/module.tar.gz', + namespace: 'download', + name: 'source', + provider: 'aws', + version: '1.2.0', + location: 'download/source/aws/1.2.0/module.tar.gz', }); await save({ - namespace: 'download', name: 'source', provider: 'aws', version: '1.3.0', location: 'download/source/aws/1.3.0/module.tar.gz', + namespace: 'download', + name: 'source', + provider: 'aws', + version: '1.3.0', + location: 'download/source/aws/1.3.0/module.tar.gz', }); }); @@ -101,7 +122,9 @@ describe('GET /v1/modules/tarball/:namespace/:name/:provider/*.tar.gz', () => { provider: 'aws', version: `${version}`, }, (err, docs) => { - if (err) { return done(err); } + if (err) { + return done(err); + } expect(docs[0]).to.have.property('downloads').to.equal(1); return done(); }); diff --git a/routes/index.js b/routes/index.js index ffa1d7f..56f52d5 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,4 +1,6 @@ -const { Router } = require('express'); +const { + Router, +} = require('express'); const router = Router(); diff --git a/routes/list.js b/routes/list.js index 3d38a11..d8d1f2b 100644 --- a/routes/list.js +++ b/routes/list.js @@ -1,8 +1,15 @@ -const { Router } = require('express'); +const { + Router, +} = require('express'); const _ = require('lodash'); -const { findAll, getVersions } = require('../lib/store'); -const { makeUrl } = require('../lib/util'); +const { + findAll, + getVersions, +} = require('../lib/store'); +const { + makeUrl, +} = require('../lib/util'); const router = Router(); @@ -42,7 +49,9 @@ router.get('/search', async (req, res) => { // https://www.terraform.io/docs/registry/api.html#list-modules router.get(['/', '/:namespace'], async (req, res) => { - const options = { ...req.query }; + const options = { + ...req.query, + }; if (req.params.namespace) { options.namespace = req.params.namespace; } @@ -59,7 +68,9 @@ router.get(['/', '/:namespace'], async (req, res) => { // https://www.terraform.io/docs/registry/api.html#list-available-versions-for-a-specific-module router.get('/:namespace/:name/:provider/versions', async (req, res, next) => { - const options = { ...req.params }; + const options = { + ...req.params, + }; try { const versions = await getVersions(options); @@ -106,7 +117,10 @@ router.get('/:namespace/:name', async (req, res) => { limit, currentOffset: offset, nextOffset: hasNext ? nextOffset : null, - nextUrl: hasNext ? makeUrl(req, { limit, offset: nextOffset }) : null, + nextUrl: hasNext ? makeUrl(req, { + limit, + offset: nextOffset, + }) : null, }, modules: pagedModules, }); diff --git a/routes/list.spec.js b/routes/list.spec.js index b2788cd..8ca3493 100644 --- a/routes/list.spec.js +++ b/routes/list.spec.js @@ -1,23 +1,46 @@ const request = require('supertest'); -const { expect } = require('chai'); +const { + expect, +} = require('chai'); const app = require('../app'); -const { db, save } = require('../lib/store'); -const { deleteDbAll } = require('../test/helper'); +const { + db, + save, +} = require('../lib/store'); +const { + deleteDbAll, +} = require('../test/helper'); describe('GET /v1/modules', () => { before(async () => { await save({ - namespace: 'GCP', name: 'lb-http', provider: 'google', version: '1.0.4', owner: '', + namespace: 'GCP', + name: 'lb-http', + provider: 'google', + version: '1.0.4', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.2.1', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.2.1', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.0', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.0', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.1', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.1', + owner: '', }); }); @@ -71,16 +94,32 @@ describe('GET /v1/modules', () => { describe('GET /v1/modules/:namespace', () => { before(async () => { await save({ - namespace: 'GCP', name: 'lb-http', provider: 'google', version: '1.0.4', owner: '', + namespace: 'GCP', + name: 'lb-http', + provider: 'google', + version: '1.0.4', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'microsoft', version: '1.2.1', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'microsoft', + version: '1.2.1', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'microsoft', version: '1.5.0', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'microsoft', + version: '1.5.0', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.1', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.1', + owner: '', }); }); @@ -112,16 +151,32 @@ describe('GET /v1/modules/:namespace', () => { describe('GET /v1/modules/search', () => { before(async () => { await save({ - namespace: 'GCP', name: 'lb-http', provider: 'google', version: '1.0.4', owner: '', + namespace: 'GCP', + name: 'lb-http', + provider: 'google', + version: '1.0.4', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'microsoft', version: '1.2.1', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'microsoft', + version: '1.2.1', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'microsoft', version: '1.5.0', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'microsoft', + version: '1.5.0', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.1', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.1', + owner: '', }); }); @@ -161,16 +216,64 @@ describe('GET /v1/modules/search', () => { describe('GET /v1/modules/:namespace/:name/:provider/versions', () => { before(async () => { await save({ - namespace: 'GCP', name: 'lb-http', provider: 'google', version: '1.0.4', owner: '', definition: { root: { name: 'lb-http' }, submodules: [{ name: 'example' }] }, + namespace: 'GCP', + name: 'lb-http', + provider: 'google', + version: '1.0.4', + owner: '', + definition: { + root: { + name: 'lb-http', + }, + submodules: [{ + name: 'example', + }], + }, }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.2.1', owner: '', definition: { root: { name: 'vpc' }, submodules: [{ name: 'example' }] }, + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.2.1', + owner: '', + definition: { + root: { + name: 'vpc', + }, + submodules: [{ + name: 'example', + }], + }, }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.0', owner: '', definition: { root: { name: 'vpc' }, submodules: [{ name: 'example' }] }, + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.0', + owner: '', + definition: { + root: { + name: 'vpc', + }, + submodules: [{ + name: 'example', + }], + }, }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.1', owner: '', definition: { root: { name: 'vpc' }, submodules: [{ name: 'example' }] }, + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.1', + owner: '', + definition: { + root: { + name: 'vpc', + }, + submodules: [{ + name: 'example', + }], + }, }); }); @@ -207,19 +310,39 @@ describe('GET /v1/modules/:namespace/:name/:provider/versions', () => { describe('GET /v1/modules/:namespace/:name', () => { before(async () => { await save({ - namespace: 'hashicorp', name: 'consul', provider: 'azurerm', version: '0.1.0', owner: '', + namespace: 'hashicorp', + name: 'consul', + provider: 'azurerm', + version: '0.1.0', + owner: '', }); await save({ - namespace: 'hashicorp', name: 'consul', provider: 'azurerm', version: '0.2.0', owner: '', + namespace: 'hashicorp', + name: 'consul', + provider: 'azurerm', + version: '0.2.0', + owner: '', }); await save({ - namespace: 'hashicorp', name: 'consul', provider: 'aws', version: '1.1.1', owner: '', + namespace: 'hashicorp', + name: 'consul', + provider: 'aws', + version: '1.1.1', + owner: '', }); await save({ - namespace: 'hashicorp', name: 'consul', provider: 'aws', version: '1.1.2', owner: '', + namespace: 'hashicorp', + name: 'consul', + provider: 'aws', + version: '1.1.2', + owner: '', }); await save({ - namespace: 'hashicorp', name: 'consul', provider: 'google', version: '1.1.2', owner: '', + namespace: 'hashicorp', + name: 'consul', + provider: 'google', + version: '1.1.2', + owner: '', }); }); diff --git a/routes/modules.js b/routes/modules.js index f129280..72e3ce1 100644 --- a/routes/modules.js +++ b/routes/modules.js @@ -1,10 +1,18 @@ -const { Router } = require('express'); +const { + Router, +} = require('express'); const multiparty = require('multiparty'); const logger = require('../lib/logger'); -const { parseHcl } = require('../lib/util'); -const { saveModule, hasModule } = require('../lib/storage'); -const { save, getLatestVersion, findOne } = require('../lib/store'); +const { + saveModule, + hasModule, +} = require('../lib/storage'); +const { + save, + getLatestVersion, + findOne, +} = require('../lib/store'); const router = Router(); @@ -49,7 +57,9 @@ router.post('/:namespace/:name/:provider/:version', (req, res, next) => { owner = Buffer.concat(ownerBuf).toString(); } if (part.filename) { - ({ filename } = part); + ({ + filename, + } = part); tarball = Buffer.concat(file); } }); @@ -66,7 +76,6 @@ router.post('/:namespace/:name/:provider/:version', (req, res, next) => { } const fileResult = await saveModule(`${destPath}/${filename}`, tarball); - const definition = await parseHcl(name, tarball); const metaResult = await save({ namespace, name, @@ -74,7 +83,6 @@ router.post('/:namespace/:name/:provider/:version', (req, res, next) => { version, owner, location: `${destPath}/${filename}`, - definition, }); if (fileResult && metaResult) { @@ -101,7 +109,9 @@ router.post('/:namespace/:name/:provider/:version', (req, res, next) => { // https://www.terraform.io/docs/registry/api.html#get-a-specific-module router.get('/:namespace/:name/:provider/:version', async (req, res, next) => { - const options = { ...req.params }; + const options = { + ...req.params, + }; const module = await findOne(options); @@ -114,7 +124,9 @@ router.get('/:namespace/:name/:provider/:version', async (req, res, next) => { // https://www.terraform.io/docs/registry/api.html#latest-version-for-a-specific-module-provider router.get('/:namespace/:name/:provider', async (req, res, next) => { - const options = { ...req.params }; + const options = { + ...req.params, + }; const module = await getLatestVersion(options); diff --git a/routes/modules.spec.js b/routes/modules.spec.js index c5189b4..2f22890 100644 --- a/routes/modules.spec.js +++ b/routes/modules.spec.js @@ -1,14 +1,23 @@ const request = require('supertest'); -const { expect } = require('chai'); -const { promisify } = require('util'); +const { + expect, +} = require('chai'); +const { + promisify, +} = require('util'); const rimraf = promisify(require('rimraf')); const fs = require('fs'); const path = require('path'); const mkdirp = promisify(require('mkdirp')); const app = require('../app'); -const { deleteDbAll } = require('../test/helper'); -const { db, save } = require('../lib/store'); +const { + deleteDbAll, +} = require('../test/helper'); +const { + db, + save, +} = require('../lib/store'); const writeFile = promisify(fs.writeFile); const readFile = promisify(fs.readFile); @@ -64,36 +73,16 @@ describe('POST /v1/modules/:namespace/:name/:provider/:version', () => { expect(res.body).to.have.property('modules').to.be.an('array'); expect(res.body.modules[0]).to.have.property('id').to.equal(modulePath); })); - - it('should register module information', (done) => { - request(app) - .post(`/v1/modules/${modulePath}`) - .attach('module', 'test/fixture/complex.tar.gz') - .expect('Content-Type', /application\/json/) - .expect(201) - .then((res) => { - db.find({ - namespace: res.body.modules[0].namespace, - name: res.body.modules[0].name, - provider: res.body.modules[0].provider, - version: res.body.modules[0].version, - }, (err, docs) => { - if (err) { return done(err); } - - expect(docs[0]).to.have.property('root'); - expect(docs[0].root).to.have.property('name').to.equal('consul'); - expect(docs[0]).to.have.property('submodules').to.be.an.instanceof(Array); - expect(docs[0].submodules).to.have.lengthOf(3); - return done(); - }); - }); - }); }); describe('GET /v1/modules/:namespace/:name/:provider/:version', () => { before(async () => { await save({ - namespace: 'router', name: 'specific', provider: 'aws', version: '1.1.2', owner: '', + namespace: 'router', + name: 'specific', + provider: 'aws', + version: '1.1.2', + owner: '', }); }); @@ -119,10 +108,34 @@ describe('GET /v1/modules/:namespace/:name/:provider/:version', () => { describe('GET /v1/modules/:namespace/:name/:provider', () => { before(async () => { await save({ - namespace: 'router', name: 'latest', provider: 'aws', version: '1.1.1', owner: '', definition: { root: { name: 'latest' }, submodules: [{ name: 'example' }] }, + namespace: 'router', + name: 'latest', + provider: 'aws', + version: '1.1.1', + owner: '', + definition: { + root: { + name: 'latest', + }, + submodules: [{ + name: 'example', + }], + }, }); await save({ - namespace: 'router', name: 'latest', provider: 'aws', version: '1.1.2', owner: '', definition: { root: { name: 'latest' }, submodules: [{ name: 'example' }] }, + namespace: 'router', + name: 'latest', + provider: 'aws', + version: '1.1.2', + owner: '', + definition: { + root: { + name: 'latest', + }, + submodules: [{ + name: 'example', + }], + }, }); }); diff --git a/routes/service-discovery.js b/routes/service-discovery.js index b09944a..51ddb0a 100644 --- a/routes/service-discovery.js +++ b/routes/service-discovery.js @@ -1,4 +1,6 @@ -const { Router } = require('express'); +const { + Router, +} = require('express'); const router = Router(); diff --git a/storages/file.js b/storages/file.js index c5a1c9a..c8e224f 100644 --- a/storages/file.js +++ b/storages/file.js @@ -1,6 +1,11 @@ const fs = require('fs'); -const { join, parse } = require('path'); -const { promisify } = require('util'); +const { + join, + parse, +} = require('path'); +const { + promisify, +} = require('util'); const debug = require('debug')('citizen:server'); const mkdirp = promisify(require('mkdirp')); @@ -11,8 +16,12 @@ const access = promisify(fs.access); module.exports = { type: () => 'file', saveModule: async (path, tarball) => { - if (!path) { throw new Error('path is required.'); } - if (!tarball) { throw new Error('tarball is required.'); } + if (!path) { + throw new Error('path is required.'); + } + if (!tarball) { + throw new Error('tarball is required.'); + } const pathToStore = join(process.env.CITIZEN_STORAGE_PATH, path); debug(`save the module into ${pathToStore}.`); diff --git a/storages/file.spec.js b/storages/file.spec.js index 45f4f1a..7fc2cf3 100644 --- a/storages/file.spec.js +++ b/storages/file.spec.js @@ -1,11 +1,19 @@ const path = require('path'); const fs = require('fs'); -const { promisify } = require('util'); -const { expect } = require('chai'); +const { + promisify, +} = require('util'); +const { + expect, +} = require('chai'); const mkdirp = promisify(require('mkdirp')); const rimraf = promisify(require('rimraf')); -const { saveModule, hasModule, getModule } = require('./file'); +const { + saveModule, + hasModule, + getModule, +} = require('./file'); const writeFile = promisify(fs.writeFile); const readFile = promisify(fs.readFile); diff --git a/storages/s3.js b/storages/s3.js index 9cb29cf..ebcbfa2 100644 --- a/storages/s3.js +++ b/storages/s3.js @@ -1,8 +1,12 @@ const AWS = require('aws-sdk'); -const { promisify } = require('util'); +const { + promisify, +} = require('util'); const debug = require('debug')('citizen:server'); -const s3 = new AWS.S3({ apiVersion: '2006-03-01' }); +const s3 = new AWS.S3({ + apiVersion: '2006-03-01', +}); const S3_BUCKET = process.env.CITIZEN_AWS_S3_BUCKET; if (process.env.CITIZEN_STORAGE === 's3' && !S3_BUCKET) { @@ -19,8 +23,12 @@ module.exports = { saveModule: async (path, tarball) => { debug(`save the module into ${path}.`); - if (!path) { throw new Error('path is required.'); } - if (!tarball) { throw new Error('tarball is required.'); } + if (!path) { + throw new Error('path is required.'); + } + if (!tarball) { + throw new Error('tarball is required.'); + } const params = { Bucket: S3_BUCKET, diff --git a/storages/s3.spec.js b/storages/s3.spec.js index 364a2af..9b7c959 100644 --- a/storages/s3.spec.js +++ b/storages/s3.spec.js @@ -1,13 +1,26 @@ /* eslint-disable no-unused-expressions */ const path = require('path'); const fs = require('fs'); -const { expect } = require('chai'); -const { promisify } = require('util'); +const { + expect, +} = require('chai'); +const { + promisify, +} = require('util'); const AWS = require('aws-sdk'); -const s3 = new AWS.S3({ apiVersion: '2006-03-01' }); -const { enableMock, clearMock } = require('../test/helper'); -const { saveModule, hasModule, getModule } = require('./s3'); +const s3 = new AWS.S3({ + apiVersion: '2006-03-01', +}); +const { + enableMock, + clearMock, +} = require('../test/helper'); +const { + saveModule, + hasModule, + getModule, +} = require('./s3'); const readFile = promisify(fs.readFile); s3.save = promisify(s3.putObject); @@ -19,7 +32,9 @@ describe('s3\'s', async () => { let moduleBuf; before(async () => { - enableMock({ modulePath }); + enableMock({ + modulePath, + }); moduleBuf = await readFile(tarballPath); }); diff --git a/stores/mongodb.js b/stores/mongodb.js index b4ab3b0..88e7a7d 100644 --- a/stores/mongodb.js +++ b/stores/mongodb.js @@ -3,18 +3,28 @@ const mongoose = require('mongoose'); const dbUri = process.env.CITIZEN_MONGO_DB_URI || 'mongodb://localhost:27017/citizen'; -mongoose.connect(dbUri, { useNewUrlParser: true }); +mongoose.connect(dbUri, { + useNewUrlParser: true, +}); const Module = mongoose.model('Module', { namespace: String, name: String, provider: String, version: String, - owner: { type: String, default: '' }, + owner: { + type: String, + default: '', + }, location: String, - definition: mongoose.Schema.Types.Mixed, - downloads: { type: Number, default: 0 }, - published_at: { type: Date, default: Date.now }, + downloads: { + type: Number, + default: 0, + }, + published_at: { + type: Date, + default: Date.now, + }, }); const db = Module; @@ -27,7 +37,6 @@ const save = data => new Promise((resolve, reject) => { version, owner, location, - definition = {}, } = data; const module = new Module({ @@ -37,7 +46,6 @@ const save = data => new Promise((resolve, reject) => { provider, version, location, - ...definition, }); module.save() @@ -73,10 +81,18 @@ const findAll = ({ nextOffset: +offset + +limit, prevOffset: +offset - +limit, }; - if (meta.prevOffset < 0) { meta.prevOffset = null; } - if (meta.nextOffset >= totalRows) { meta.nextOffset = null; } - - return Module.find(options, null, { sort: '_id', skip: +offset, limit: +limit }) + if (meta.prevOffset < 0) { + meta.prevOffset = null; + } + if (meta.nextOffset >= totalRows) { + meta.nextOffset = null; + } + + return Module.find(options, null, { + sort: '_id', + skip: +offset, + limit: +limit, + }) .then((docs) => { debug('search result from db: %o', docs); return resolve({ @@ -94,9 +110,15 @@ const getVersions = ({ name, provider, } = {}) => new Promise((resolve, reject) => { - if (!namespace) { reject(new Error('namespace required.')); } - if (!name) { reject(new Error('name required.')); } - if (!provider) { reject(new Error('provider required.')); } + if (!namespace) { + reject(new Error('namespace required.')); + } + if (!name) { + reject(new Error('name required.')); + } + if (!provider) { + reject(new Error('provider required.')); + } const options = { namespace, @@ -105,7 +127,9 @@ const getVersions = ({ }; debug('search versions in db with %o', options); - Module.find(options, null, { sort: '_id' }) + Module.find(options, null, { + sort: '_id', + }) .then((docs) => { const data = docs.map(d => ({ version: d.version, @@ -123,9 +147,15 @@ const getLatestVersion = async ({ name, provider, } = {}) => new Promise((resolve, reject) => { - if (!namespace) { reject(new Error('namespace required.')); } - if (!name) { reject(new Error('name required.')); } - if (!provider) { reject(new Error('provider required.')); } + if (!namespace) { + reject(new Error('namespace required.')); + } + if (!name) { + reject(new Error('name required.')); + } + if (!provider) { + reject(new Error('provider required.')); + } const options = { namespace, @@ -133,7 +163,10 @@ const getLatestVersion = async ({ provider, }; - Module.find(options, null, { sort: '-version', limit: 1 }) + Module.find(options, null, { + sort: '-version', + limit: 1, + }) .then((docs) => { debug('search latest version result from db: %o', docs); return resolve(docs.length > 0 ? docs[0] : null); @@ -147,10 +180,18 @@ const findOne = async ({ provider, version, } = {}) => new Promise((resolve, reject) => { - if (!namespace) { reject(new Error('namespace required.')); } - if (!name) { reject(new Error('name required.')); } - if (!provider) { reject(new Error('provider required.')); } - if (!version) { reject(new Error('version required.')); } + if (!namespace) { + reject(new Error('namespace required.')); + } + if (!name) { + reject(new Error('name required.')); + } + if (!provider) { + reject(new Error('provider required.')); + } + if (!version) { + reject(new Error('version required.')); + } const options = { namespace, @@ -171,10 +212,18 @@ const increaseDownload = async ({ provider, version, } = {}) => new Promise((resolve, reject) => { - if (!namespace) { reject(new Error('namespace required.')); } - if (!name) { reject(new Error('name required.')); } - if (!provider) { reject(new Error('provider required.')); } - if (!version) { reject(new Error('version required.')); } + if (!namespace) { + reject(new Error('namespace required.')); + } + if (!name) { + reject(new Error('name required.')); + } + if (!provider) { + reject(new Error('provider required.')); + } + if (!version) { + reject(new Error('version required.')); + } const options = { namespace, @@ -183,7 +232,13 @@ const increaseDownload = async ({ version, }; - Module.findOneAndUpdate(options, { $inc: { downloads: 1 } }, { new: true }) + Module.findOneAndUpdate(options, { + $inc: { + downloads: 1, + }, + }, { + new: true, + }) .then(doc => resolve(doc)) .catch(err => reject(err)); }); diff --git a/stores/mongodb.spec.js b/stores/mongodb.spec.js index a05af4b..2dec8c2 100644 --- a/stores/mongodb.spec.js +++ b/stores/mongodb.spec.js @@ -1,5 +1,7 @@ /* eslint-disable no-unused-expressions */ -const { expect } = require('chai'); +const { + expect, +} = require('chai'); const { db, @@ -10,7 +12,9 @@ const { getLatestVersion, increaseDownload, } = require('./mongodb'); -const { deleteDbAllMongo } = require('../test/helper'); +const { + deleteDbAllMongo, +} = require('../test/helper'); describe('mongodb store', async () => { describe('save()', () => { @@ -35,16 +39,32 @@ describe('mongodb store', async () => { describe('findAll()', () => { before(async () => { await save({ - namespace: 'store-GCP', name: 'store-lb-http', provider: 'store-google', version: '1.0.4', owner: '', + namespace: 'store-GCP', + name: 'store-lb-http', + provider: 'store-google', + version: '1.0.4', + owner: '', }); await save({ - namespace: 'store-aws-modules', name: 'store-vpc', provider: 'store-aws', version: '1.2.1', owner: '', + namespace: 'store-aws-modules', + name: 'store-vpc', + provider: 'store-aws', + version: '1.2.1', + owner: '', }); await save({ - namespace: 'store-aws-modules', name: 'store-vpc', provider: 'store-aws', version: '1.5.0', owner: '', + namespace: 'store-aws-modules', + name: 'store-vpc', + provider: 'store-aws', + version: '1.5.0', + owner: '', }); await save({ - namespace: 'store-aws-modules', name: 'store-vpc', provider: 'store-aws', version: '1.5.1', owner: '', + namespace: 'store-aws-modules', + name: 'store-vpc', + provider: 'store-aws', + version: '1.5.1', + owner: '', }); }); @@ -68,14 +88,20 @@ describe('mongodb store', async () => { }); it('should support pagination', async () => { - const result = await findAll({ offset: 2, limit: 2 }); + const result = await findAll({ + offset: 2, + limit: 2, + }); expect(result).to.have.property('modules').to.have.lengthOf(2); expect(result.modules[0]).to.have.property('namespace').to.equal('store-aws-modules'); expect(result.modules[0]).to.have.property('version').to.equal('1.5.0'); }); it('should return pagination information', async () => { - const result = await findAll({ offset: 2, limit: 1 }); + const result = await findAll({ + offset: 2, + limit: 1, + }); expect(result).to.have.property('meta'); expect(result.meta).to.have.property('limit').to.equal(1); @@ -85,7 +111,10 @@ describe('mongodb store', async () => { }); it('should prevOffset pagination information', async () => { - const result = await findAll({ offset: 0, limit: 2 }); + const result = await findAll({ + offset: 0, + limit: 2, + }); expect(result.meta).to.have.property('limit').to.equal(2); expect(result.meta).to.have.property('currentOffset').to.equal(0); @@ -94,7 +123,10 @@ describe('mongodb store', async () => { }); it('should return pagination information', async () => { - const result = await findAll({ offset: 2, limit: 2 }); + const result = await findAll({ + offset: 2, + limit: 2, + }); expect(result.meta).to.have.property('limit').to.equal(2); expect(result.meta).to.have.property('currentOffset').to.equal(2); @@ -114,13 +146,25 @@ describe('mongodb store', async () => { describe('getVersions()', () => { before(async () => { await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.2.1', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.2.1', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.0', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.0', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.1', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.1', + owner: '', }); }); @@ -142,10 +186,18 @@ describe('mongodb store', async () => { describe('getLatestVersion()', () => { before(async () => { await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.0', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.0', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.1', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.1', + owner: '', }); }); @@ -177,7 +229,12 @@ describe('mongodb store', async () => { describe('findOne()', () => { before(async () => { await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.1', owner: '', location: 'aws-modules/vpc/aws/1.5.1/module.tar.gz', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.1', + owner: '', + location: 'aws-modules/vpc/aws/1.5.1/module.tar.gz', }); }); @@ -211,7 +268,12 @@ describe('mongodb store', async () => { describe('increaseDownload()', () => { before(async () => { await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.1', owner: '', location: 'aws-modules/vpc/aws/1.5.1/module.tar.gz', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.1', + owner: '', + location: 'aws-modules/vpc/aws/1.5.1/module.tar.gz', }); }); diff --git a/stores/nedb.js b/stores/nedb.js index 3685cd5..4439387 100644 --- a/stores/nedb.js +++ b/stores/nedb.js @@ -1,11 +1,16 @@ const Datastore = require('nedb'); const uuid = require('uuid/v1'); -const { join } = require('path'); +const { + join, +} = require('path'); const debug = require('debug')('citizen:server'); const dbDir = process.env.CITIZEN_DB_DIR || 'data'; const dbPath = join(dbDir, 'citizen.db'); -const db = new Datastore({ filename: dbPath, autoload: true }); +const db = new Datastore({ + filename: dbPath, + autoload: true, +}); const save = data => new Promise((resolve, reject) => { const { @@ -32,7 +37,9 @@ const save = data => new Promise((resolve, reject) => { }; db.insert(module, (err, newDoc) => { - if (err) { return reject(err); } + if (err) { + return reject(err); + } debug('saved the module into db: %o', module); return resolve(newDoc); }); @@ -56,7 +63,9 @@ const findAll = ({ debug('search db with %o', options); db.find(options, (err, allDocs) => { - if (err) { return reject(err); } + if (err) { + return reject(err); + } const totalRows = allDocs.length; const meta = { @@ -65,12 +74,20 @@ const findAll = ({ nextOffset: +offset + +limit, prevOffset: +offset - +limit, }; - if (meta.prevOffset < 0) { meta.prevOffset = null; } - if (meta.nextOffset >= totalRows) { meta.nextOffset = null; } + if (meta.prevOffset < 0) { + meta.prevOffset = null; + } + if (meta.nextOffset >= totalRows) { + meta.nextOffset = null; + } - return db.find(options).sort({ _id: 1 }).skip(+offset).limit(+limit) + return db.find(options).sort({ + _id: 1, + }).skip(+offset).limit(+limit) .exec((error, docs) => { - if (error) { return reject(err); } + if (error) { + return reject(err); + } debug('search result from db: %o', docs); return resolve({ @@ -81,10 +98,20 @@ const findAll = ({ }); }); -const getVersions = ({ namespace, name, provider } = {}) => new Promise((resolve, reject) => { - if (!namespace) { reject(new Error('namespace required.')); } - if (!name) { reject(new Error('name required.')); } - if (!provider) { reject(new Error('provider required.')); } +const getVersions = ({ + namespace, + name, + provider, +} = {}) => new Promise((resolve, reject) => { + if (!namespace) { + reject(new Error('namespace required.')); + } + if (!name) { + reject(new Error('name required.')); + } + if (!provider) { + reject(new Error('provider required.')); + } const options = { namespace, @@ -93,8 +120,12 @@ const getVersions = ({ namespace, name, provider } = {}) => new Promise((resolve }; debug('search versions in db with %o', options); - db.find(options).sort({ _id: 1 }).exec((err, docs) => { - if (err) { return reject(err); } + db.find(options).sort({ + _id: 1, + }).exec((err, docs) => { + if (err) { + return reject(err); + } const data = docs.map(d => ({ version: d.version, @@ -106,11 +137,21 @@ const getVersions = ({ namespace, name, provider } = {}) => new Promise((resolve }); }); -const getLatestVersion = async ({ namespace, name, provider } = {}) => new Promise( +const getLatestVersion = async ({ + namespace, + name, + provider, +} = {}) => new Promise( (resolve, reject) => { - if (!namespace) { reject(new Error('namespace required.')); } - if (!name) { reject(new Error('name required.')); } - if (!provider) { reject(new Error('provider required.')); } + if (!namespace) { + reject(new Error('namespace required.')); + } + if (!name) { + reject(new Error('name required.')); + } + if (!provider) { + reject(new Error('provider required.')); + } const options = { namespace, @@ -118,8 +159,12 @@ const getLatestVersion = async ({ namespace, name, provider } = {}) => new Promi provider, }; - db.find(options).sort({ version: -1 }).limit(1).exec((err, docs) => { - if (err) { return reject(err); } + db.find(options).sort({ + version: -1, + }).limit(1).exec((err, docs) => { + if (err) { + return reject(err); + } debug('search latest version result from db: %o', docs); return resolve(docs.length > 0 ? docs[0] : null); @@ -133,10 +178,18 @@ const findOne = async ({ provider, version, } = {}) => new Promise((resolve, reject) => { - if (!namespace) { reject(new Error('namespace required.')); } - if (!name) { reject(new Error('name required.')); } - if (!provider) { reject(new Error('provider required.')); } - if (!version) { reject(new Error('version required.')); } + if (!namespace) { + reject(new Error('namespace required.')); + } + if (!name) { + reject(new Error('name required.')); + } + if (!provider) { + reject(new Error('provider required.')); + } + if (!version) { + reject(new Error('version required.')); + } const options = { namespace, @@ -147,7 +200,9 @@ const findOne = async ({ debug('search a module in db with %o', options); db.find(options, (err, docs) => { - if (err) { return reject(err); } + if (err) { + return reject(err); + } debug('search a module result from db: %o', docs); return resolve(docs.length > 0 ? docs[0] : null); @@ -160,10 +215,18 @@ const increaseDownload = async ({ provider, version, } = {}) => new Promise((resolve, reject) => { - if (!namespace) { reject(new Error('namespace required.')); } - if (!name) { reject(new Error('name required.')); } - if (!provider) { reject(new Error('provider required.')); } - if (!version) { reject(new Error('version required.')); } + if (!namespace) { + reject(new Error('namespace required.')); + } + if (!name) { + reject(new Error('name required.')); + } + if (!provider) { + reject(new Error('provider required.')); + } + if (!version) { + reject(new Error('version required.')); + } const options = { namespace, @@ -173,11 +236,17 @@ const increaseDownload = async ({ }; db.update( - options, - { $inc: { downloads: 1 } }, - { returnUpdatedDocs: true }, + options, { + $inc: { + downloads: 1, + }, + }, { + returnUpdatedDocs: true, + }, (err, numAffected, affectedDocuments) => { - if (err) { return reject(err); } + if (err) { + return reject(err); + } return resolve(affectedDocuments); }, diff --git a/stores/nedb.spec.js b/stores/nedb.spec.js index d65ecf5..44b3650 100644 --- a/stores/nedb.spec.js +++ b/stores/nedb.spec.js @@ -1,5 +1,7 @@ /* eslint-disable no-unused-expressions */ -const { expect } = require('chai'); +const { + expect, +} = require('chai'); const { db, @@ -10,7 +12,9 @@ const { getLatestVersion, increaseDownload, } = require('./nedb'); -const { deleteDbAll } = require('../test/helper'); +const { + deleteDbAll, +} = require('../test/helper'); describe('store\'s', async () => { describe('save()', () => { @@ -35,16 +39,32 @@ describe('store\'s', async () => { describe('findAll()', () => { before(async () => { await save({ - namespace: 'store-GCP', name: 'store-lb-http', provider: 'store-google', version: '1.0.4', owner: '', + namespace: 'store-GCP', + name: 'store-lb-http', + provider: 'store-google', + version: '1.0.4', + owner: '', }); await save({ - namespace: 'store-aws-modules', name: 'store-vpc', provider: 'store-aws', version: '1.2.1', owner: '', + namespace: 'store-aws-modules', + name: 'store-vpc', + provider: 'store-aws', + version: '1.2.1', + owner: '', }); await save({ - namespace: 'store-aws-modules', name: 'store-vpc', provider: 'store-aws', version: '1.5.0', owner: '', + namespace: 'store-aws-modules', + name: 'store-vpc', + provider: 'store-aws', + version: '1.5.0', + owner: '', }); await save({ - namespace: 'store-aws-modules', name: 'store-vpc', provider: 'store-aws', version: '1.5.1', owner: '', + namespace: 'store-aws-modules', + name: 'store-vpc', + provider: 'store-aws', + version: '1.5.1', + owner: '', }); }); @@ -68,14 +88,20 @@ describe('store\'s', async () => { }); it('should support pagination', async () => { - const result = await findAll({ offset: 2, limit: 2 }); + const result = await findAll({ + offset: 2, + limit: 2, + }); expect(result).to.have.property('modules').to.have.lengthOf(2); expect(result.modules[0]).to.have.property('namespace').to.equal('store-aws-modules'); expect(result.modules[0]).to.have.property('version').to.equal('1.5.0'); }); it('should return pagination information', async () => { - const result = await findAll({ offset: 2, limit: 1 }); + const result = await findAll({ + offset: 2, + limit: 1, + }); expect(result).to.have.property('meta'); expect(result.meta).to.have.property('limit').to.equal(1); @@ -85,7 +111,10 @@ describe('store\'s', async () => { }); it('should prevOffset pagination information', async () => { - const result = await findAll({ offset: 0, limit: 2 }); + const result = await findAll({ + offset: 0, + limit: 2, + }); expect(result.meta).to.have.property('limit').to.equal(2); expect(result.meta).to.have.property('currentOffset').to.equal(0); @@ -94,7 +123,10 @@ describe('store\'s', async () => { }); it('should return pagination information', async () => { - const result = await findAll({ offset: 2, limit: 2 }); + const result = await findAll({ + offset: 2, + limit: 2, + }); expect(result.meta).to.have.property('limit').to.equal(2); expect(result.meta).to.have.property('currentOffset').to.equal(2); @@ -114,13 +146,25 @@ describe('store\'s', async () => { describe('getVersions()', () => { before(async () => { await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.2.1', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.2.1', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.0', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.0', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.1', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.1', + owner: '', }); }); @@ -142,10 +186,18 @@ describe('store\'s', async () => { describe('getLatestVersion()', () => { before(async () => { await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.0', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.0', + owner: '', }); await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.1', owner: '', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.1', + owner: '', }); }); @@ -177,7 +229,12 @@ describe('store\'s', async () => { describe('findOne()', () => { before(async () => { await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.1', owner: '', location: 'aws-modules/vpc/aws/1.5.1/module.tar.gz', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.1', + owner: '', + location: 'aws-modules/vpc/aws/1.5.1/module.tar.gz', }); }); @@ -211,7 +268,12 @@ describe('store\'s', async () => { describe('increaseDownload()', () => { before(async () => { await save({ - namespace: 'aws-modules', name: 'vpc', provider: 'aws', version: '1.5.1', owner: '', location: 'aws-modules/vpc/aws/1.5.1/module.tar.gz', + namespace: 'aws-modules', + name: 'vpc', + provider: 'aws', + version: '1.5.1', + owner: '', + location: 'aws-modules/vpc/aws/1.5.1/module.tar.gz', }); }); diff --git a/test/fixture/complex.tar.gz b/test/fixture/complex.tar.gz deleted file mode 100644 index c45b495..0000000 Binary files a/test/fixture/complex.tar.gz and /dev/null differ diff --git a/test/helper.js b/test/helper.js index 900887d..08ee77d 100644 --- a/test/helper.js +++ b/test/helper.js @@ -1,7 +1,9 @@ const nock = require('nock'); module.exports = { - enableMock: ({ modulePath }) => { + enableMock: ({ + modulePath, + }) => { if (process.env.CITIZEN_MOCK_ENABLED) { nock('https://s3.amazonaws.com') .persist() @@ -16,7 +18,8 @@ module.exports = { 'Transfer-Encoding', 'chunked', 'Date', 'Mon, 21 May 2018 13:12:55 GMT', 'Connection', 'close', - 'Server', 'AmazonS3'], + 'Server', 'AmazonS3', + ], ); nock('https://s3.ap-northeast-1.amazonaws.com') @@ -30,7 +33,8 @@ module.exports = { 'Date', 'Sun, 21 Jan 2018 12:50:17 GMT', 'ETag', '"ed168b6114db5f54d38bb1bd9ba45106"', 'Content-Length', '0', - 'Server', 'AmazonS3'], + 'Server', 'AmazonS3', + ], ) .get(`/${process.env.CITIZEN_AWS_S3_BUCKET}/${modulePath}`) .reply( @@ -44,7 +48,8 @@ module.exports = { 'Accept-Ranges', 'bytes', 'Content-Type', 'application/octet-stream', 'Content-Length', '136', - 'Server', 'AmazonS3'], + 'Server', 'AmazonS3', + ], ) .get(`/${process.env.CITIZEN_AWS_S3_BUCKET}/${modulePath}/wrong`) .reply( @@ -55,7 +60,8 @@ module.exports = { 'Content-Type', 'application/xml', 'Transfer-Encoding', 'chunked', 'Date', 'Sun, 21 Jan 2018 16:47:34 GMT', - 'Server', 'AmazonS3'], + 'Server', 'AmazonS3', + ], ) .delete(`/${process.env.CITIZEN_AWS_S3_BUCKET}/${modulePath}`) .reply( @@ -64,7 +70,8 @@ module.exports = { ['x-amz-id-2', 'S0/qBL0imAEhHXwySaB9UxDzA025VeonKZa7A4lP5LZgDC6jiYRhmb5gpRhbbOO6e+SkfvVSLh0=', 'x-amz-request-id', 'B9DB7B4B906399D7', 'Date', 'Sun, 21 Jan 2018 12:50:18 GMT', - 'Server', 'AmazonS3'], + 'Server', 'AmazonS3', + ], ); } }, @@ -74,8 +81,12 @@ module.exports = { } }, deleteDbAll: db => new Promise((resolve, reject) => { - db.remove({}, { multi: true }, (err, numRemoved) => { - if (err) { return reject(err); } + db.remove({}, { + multi: true, + }, (err, numRemoved) => { + if (err) { + return reject(err); + } return resolve(numRemoved); }); }), diff --git a/test/integration/ngrok.js b/test/integration/ngrok.js index da4fc41..4669241 100644 --- a/test/integration/ngrok.js +++ b/test/integration/ngrok.js @@ -1,5 +1,7 @@ const ngrok = require('ngrok'); -const { parse } = require('url'); +const { + parse, +} = require('url'); const connect = async (port) => { const url = await ngrok.connect(port); diff --git a/test/integration/terraform-cli.spec.js b/test/integration/terraform-cli.spec.js index 9c7d069..d2d0239 100644 --- a/test/integration/terraform-cli.spec.js +++ b/test/integration/terraform-cli.spec.js @@ -1,16 +1,31 @@ const https = require('https'); const fs = require('fs'); -const { promisify } = require('util'); -const { expect } = require('chai'); +const { + promisify, +} = require('util'); +const { + expect, +} = require('chai'); const getPort = require('get-port'); -const { execFile } = require('child_process'); -const { join } = require('path'); +const { + execFile, +} = require('child_process'); +const { + join, +} = require('path'); const rimraf = promisify(require('rimraf')); -const { connect, disconnect } = require('./ngrok'); +const { + connect, + disconnect, +} = require('./ngrok'); const registry = require('./registry'); -const { db } = require('../../lib/store'); -const { deleteDbAll } = require('../helper'); +const { + db, +} = require('../../lib/store'); +const { + deleteDbAll, +} = require('../helper'); const writeFile = promisify(fs.writeFile); const unlink = promisify(fs.unlink); @@ -32,7 +47,9 @@ describe('terraform CLI', () => { const download = join(__dirname, 'download-terraform'); execFile(download, async (err) => { - if (err) { return done(err); } + if (err) { + return done(err); + } try { const port = await getPort(); @@ -101,7 +118,9 @@ describe('terraform CLI', () => { it('cli should connect the registry server with terraform-cli', (done) => { const cwd = join(__dirname, 'fixture'); - execFile(terraform, ['get'], { cwd }, (err, stdout, stderr) => { + execFile(terraform, ['get'], { + cwd, + }, (err, stdout, stderr) => { expect(stdout).to.include('- module.vpc'); expect(stderr).to.include('no versions found'); done(); @@ -121,10 +140,13 @@ describe('terraform CLI', () => { execFile( client, - ['publish', 'citizen-test', 'alb', 'aws', '0.1.0'], - { cwd: moduleDir }, + ['publish', 'citizen-test', 'alb', 'aws', '0.1.0'], { + cwd: moduleDir, + }, async (err) => { - if (err) { return done(err); } + if (err) { + return done(err); + } const content = definition .replace(/__MODULE_ADDRESS__/, `${url.host}/citizen-test/alb/aws`) @@ -145,8 +167,12 @@ describe('terraform CLI', () => { it('should download module from registry', (done) => { const cwd = join(__dirname, 'fixture'); - execFile(terraform, ['get'], { cwd }, async (err, stdout) => { - if (err) { return done(err); } + execFile(terraform, ['get'], { + cwd, + }, async (err, stdout) => { + if (err) { + return done(err); + } expect(stdout).to.include('Found version 0.1.0 of citizen-test/alb/aws on'); expect(stdout).to.include('Getting source');