diff --git a/api.js b/api.js index 056b6fcb2..3c6b5c518 100644 --- a/api.js +++ b/api.js @@ -22,21 +22,15 @@ exports.api = function(args, opts = {}) { actionObj.run(config, info); }; -exports.load = function(config, action = 'start') { +exports.load = function(config, action = 'help') { const file = path.join(__dirname, 'lib', `${action}.js`); - if (utils.fileExists(file)) { + try { // eslint-disable-next-line global-require, import/no-dynamic-require return require(file); + } catch (e) { + console.error('Action not found.'.red); + console.warn(`Type ${`${config.cli} help`.yellow} to see all commands`); + process.exitCode = 1; + return undefined; } - - const alias = utils.getAliasFile(action); - if (alias) { - // eslint-disable-next-line global-require, import/no-dynamic-require - return require(path.join(__dirname, 'lib', `${alias}.js`)); - } - - console.error('Action not found.'.red); - console.warn(`Type ${`${config.cli} help`.yellow} to see all commands`); - process.exitCode = 1; - return undefined; }; diff --git a/lib/add.js b/lib/add.js deleted file mode 100644 index 87e0d1fd2..000000000 --- a/lib/add.js +++ /dev/null @@ -1,29 +0,0 @@ -const request = require('request'); -const jsonfile = require('jsonfile'); - -exports.swagger = true; -exports.login = true; -exports.desc = 'Add a user'; - -exports.run = function(config, info) { - const email = info.args[1]; - console.log(`Granting ${email.yellow} push access to ${info.swagger['x-api-id'].yellow}!`); - console.log(''); - - const user = jsonfile.readFileSync(config.apiFile); - - request.post( - `${config.host.url}/add`, - { - form: { - user: user.token, - email, - repo: info.swagger['x-api-id'], - }, - }, - () => { - console.log(`${'Success! '.green}User has been added.`); - process.exit(); - }, - ); -}; diff --git a/lib/docs.js b/lib/docs.js deleted file mode 100644 index abd4bc4ca..000000000 --- a/lib/docs.js +++ /dev/null @@ -1,22 +0,0 @@ -const open = require('open'); - -exports.swagger = true; -exports.swaggerUrl = true; -exports.login = true; -exports.desc = 'Host your docs on ReadMe'; -exports.category = 'services'; - -exports.run = function(config, info) { - console.log(''); - console.log( - `${'Success! '.green}You can now access your Swagger from the following publicly sharable URL:`, - ); - console.log(''); - console.log(` ${info.swaggerUrl}?docs`); - console.log(''); - console.log('To use in ReadMe for documentation, follow the URL for setup information.'); - - open(`${info.swaggerUrl}?docs`, info); - - process.exit(); -}; diff --git a/lib/endpoint.js b/lib/endpoint.js deleted file mode 100644 index 314805a79..000000000 --- a/lib/endpoint.js +++ /dev/null @@ -1,32 +0,0 @@ -const utils = require('../utils'); - -exports.swagger = false; -exports.login = false; -exports.category = 'basic'; -exports.desc = 'Learn how to document an endpoint'; -exports.weight = 3; - -exports.run = function() { - console.log('You can document each endpoint right above the code. Just use the'); - console.log('following syntax in a comment above the code:'); - console.log(''); - - console.log(utils.swaggerInlineExample(utils.guessLanguage())); - - console.log(''); - console.log(`${'Param shorthand: '.blue}Since params are very verbose, we have a shorthand`); - console.log('for describing them.'); - - console.log(''); - console.log(' - (in) name=default* {type:format} description'.grey); - console.log(''); - - console.log('This will be expanded when the Swagger file is compiled.'); - - console.log(''); - console.log( - 'For more information on this syntax, see https://github.com/readmeio/swagger-inline', - ); - - process.exit(); -}; diff --git a/lib/generate.js b/lib/generate.js deleted file mode 100644 index 17da623b3..000000000 --- a/lib/generate.js +++ /dev/null @@ -1,11 +0,0 @@ -const cardinal = require('cardinal'); - -exports.swagger = true; -exports.login = false; -exports.desc = 'Output your Swagger file'; -exports.category = 'utility'; - -exports.run = function(config, info) { - console.log(cardinal.highlight(JSON.stringify(info.swagger, undefined, 2))); - process.exit(); -}; diff --git a/lib/help.js b/lib/help.js index 4d8897727..a4be3b121 100644 --- a/lib/help.js +++ b/lib/help.js @@ -1,20 +1,19 @@ +// TODO check this? const glob = require('glob'); const path = require('path'); const _ = require('lodash'); -exports.swagger = false; -exports.login = false; exports.category = 'basic'; exports.desc = 'Learn what you can do with this tool'; exports.weight = 2; function pad(text) { - return `${text} `.substr(0, 15); + return text.padEnd(15); } exports.run = function(config) { console.log(''); - console.log(`Usage: ${config.cli} [swagger url]`); + console.log(`Usage: ${config.cli} [arguments]`); const files = glob.sync(path.join(__dirname, '*')); const categories = { @@ -26,10 +25,6 @@ exports.run = function(config) { desc: `Hosted third-party services ${'(Will post to the Internet)'.grey}`, commands: [], }, - utility: { - desc: 'Utility functions', - commands: [], - }, }; _.each(files, file => { @@ -54,10 +49,7 @@ exports.run = function(config) { }); }); - console.log(''); - console.log('Just getting started?'.green); - console.log(`Run ${`${config.cli} init`.yellow} to create your Swagger file.`); console.log(''); - process.exit(); + process.exitCode = 0; }; diff --git a/lib/init.js b/lib/init.js deleted file mode 100644 index 7b1acd1f6..000000000 --- a/lib/init.js +++ /dev/null @@ -1,189 +0,0 @@ -const path = require('path'); -const inquirer = require('inquirer'); -const fs = require('fs'); -const crypto = require('crypto'); -const YAML = require('json2yaml'); -const utils = require('../utils'); -const uslug = require('uslug'); - -exports.swagger = false; -exports.login = false; -exports.category = 'basic'; -exports.desc = 'Create a new API specification'; -exports.weight = 0; - -function getDefaultSwagger() { - /* eslint-disable */ - let i = 0; - while ((file = utils.fileExists(_file(i)))) { - i++; - } - return _file(i); - - function _file(i) { - return `swagger${i || ''}.json`; - } -} - -const types = [ - { name: 'application/json', checked: true }, - { name: 'application/xml' }, - { name: 'application/x-www-form-urlencoded' }, - { name: 'multipart/form-data' }, -]; - -exports.run = function() { - console.log("This will help you set up an 'Open API' (formerly 'Swagger') spec in your"); - console.log('repo, so you can start documenting your API!'); - - console.log(''); - - let pkg = {}; - try { - // eslint-disable-next-line - pkg = require(path.join(process.cwd(), '/package.json')); - } catch (e) {} // eslint-disable-line - - const questions = [ - { - type: 'input', - name: 'info.title', - message: 'Name of the API', - default: - pkg.name || - process - .cwd() - .split('/') - .slice(-1)[0], - }, - { - type: 'input', - name: 'info.version', - message: 'Version number', - default: pkg.version || '1.0.0', - }, - { - type: 'input', - name: 'info.license', - message: 'License', - default: pkg.license, - }, - { - type: 'input', - name: 'url', - message: 'Full Base URL', - validate(value) { - const pass = /^(http|https|ws|wss):\/\/[^ "]+$/.test(value); - - if (pass) { - return true; - } - - return 'Please enter a valid URL, including protocol'; - }, - }, - { - type: 'checkbox', - name: 'consumes', - message: 'Consumes content types', - choices: types, - }, - { - type: 'checkbox', - name: 'produces', - message: 'Produces content types', - choices: types, - }, - { - type: 'input', - name: 'output', - message: 'Output JSON or YAML file', - default: getDefaultSwagger(), - choices: types, - validate(value) { - const pass = /.(json|yaml|yml)$/.test(value); - const doesntExist = !utils.fileExists(value); - - if (pass && doesntExist) { - return true; - } - - if (!pass) { - return 'Your file must end with .json or .yaml'; - } - if (!doesntExist) { - return 'This file already exists'; - } - }, - }, - ]; - - inquirer.prompt(questions).then(answers => { - const swagger = { - swagger: '2.0', - 'x-api-id': uslug(answers['info.title']) || crypto.randomBytes(7).toString('hex'), - info: { - version: answers['info.version'], - title: answers['info.title'], - }, - paths: {}, - }; - - if (answers['info.license']) { - swagger.info.license = { - name: answers['info.license'], - }; - } - - if (answers.produces.length) { - swagger.produces = answers.produces; - } - - if (answers.consumes.length) { - swagger.consumes = answers.consumes; - } - - const url = answers.url.match(/^(.*):\/\/([^\/]*)(.*)?$/); - swagger.schemes = [url[1]]; - swagger.host = url[2]; - if (url[3]) { - swagger.basePath = url[3]; - } - - writeFile(answers.output, swagger); - - console.log(''); - console.log('======================'); - console.log(''); - console.log('SUCCESS!'.green); - console.log(''); - console.log(`We've created your new Open API file at ${answers.output.yellow}.`); - console.log(''); - console.log('You can document each endpoint right above the code. Just use the'); - console.log('following syntax in a comment above the code:'); - console.log(''); - - console.log(utils.swaggerInlineExample(utils.guessLanguage())); - - console.log(''); - console.log( - 'For more information on this syntax, see https://github.com/readmeio/swagger-inline', - ); - console.log(''); - console.log(`To see what you can do with your API, type ${'oas help'.yellow}.`); - console.log(''); - console.log(`To publish your OAS file, type ${'oas host'.yellow}!`); - console.log(''); - - process.exit(); - }); -}; - -function writeFile(output, swagger) { - let body = JSON.stringify(swagger, undefined, 2); - if (output.match(/.(yaml|yml)/)) { - body = YAML.stringify(swagger); - body = body.replace(/^\s\s/gm, '').replace(/^---\n/, ''); - } - fs.writeFileSync(output, body); -} diff --git a/lib/login.js b/lib/login.js deleted file mode 100644 index 4135101d7..000000000 --- a/lib/login.js +++ /dev/null @@ -1,9 +0,0 @@ -exports.swagger = false; -exports.login = false; -exports.desc = 'Authenticate this computer'; -exports.category = 'utility'; - -exports.run = function() { - console.log('Log in to your ReadMe account!'); - console.log(''); -}; diff --git a/lib/manage.js b/lib/manage.js deleted file mode 100644 index 55aef0df7..000000000 --- a/lib/manage.js +++ /dev/null @@ -1,17 +0,0 @@ -exports.swagger = false; -exports.login = false; -exports.category = 'utility'; -exports.desc = 'Manage users and versions'; - -exports.run = function() { - console.log('You can modify your settings from here!'); - console.log(''); - console.log('GRANT PUSH ACCESS'.cyan); - console.log('Run the following command to add them (must match their GitHub email):'); - console.log(''); - console.log(' $ oas add user@email.com'.grey); - console.log(''); - console.log(''); - - process.exit(); -}; diff --git a/lib/start.js b/lib/start.js deleted file mode 100644 index cf2003ca7..000000000 --- a/lib/start.js +++ /dev/null @@ -1,15 +0,0 @@ -exports.swagger = false; -exports.login = false; -// exports.category = "basic"; -// exports.desc = "Learn what you can do with oas"; -exports.weight = 2; - -exports.run = function(config) { - console.log('Welcome to the OpenAPI/Swagger uploader for ReadMe!'); - console.log(''); - console.log( - `Have a Swagger file? ${`${config.cli} swagger swagger.json --token=[token]`.yellow}`, - ); - console.log(`Need a Swagger file? ${`${config.cli} init`.yellow}`); - process.exit(); -}; diff --git a/lib/swagger.js b/lib/swagger.js index 8686967e7..70e5f6a33 100644 --- a/lib/swagger.js +++ b/lib/swagger.js @@ -2,8 +2,6 @@ const request = require('request'); const fs = require('fs'); const path = require('path'); -exports.swagger = true; -exports.login = true; exports.desc = 'Upload your swagger file to ReadMe'; exports.category = 'services'; diff --git a/lib/validate.js b/lib/validate.js deleted file mode 100644 index 3ec2bb961..000000000 --- a/lib/validate.js +++ /dev/null @@ -1,11 +0,0 @@ -const figures = require('figures'); - -exports.swagger = true; -exports.login = false; -exports.desc = 'Validate your Swagger file'; -exports.category = 'utility'; - -exports.run = function() { - console.log(`${figures.tick.green + ' Success!'.green} Valid Swagger file`); - process.exit(); -}; diff --git a/test/utils.test.js b/test/utils.test.js index b05721d09..3080c633d 100644 --- a/test/utils.test.js +++ b/test/utils.test.js @@ -1,38 +1,9 @@ const assert = require('assert'); -const path = require('path'); const utils = require('../utils'); -describe('utils.js', () => { - describe('#findSwagger()', () => { - it.skip('find a YAML file', done => { - utils.findSwagger( - (err, swagger, file) => { - if (err) return done(err); - assert(file.endsWith('PetStore.yaml')); - assert.equal('2.0', swagger.swagger); - return done(); - }, - { - dir: path.join(__dirname, 'fixtures', 'yaml'), - }, - ); - }); - - it.skip('find a JSON file', done => { - utils.findSwagger( - (err, swagger, file) => { - if (err) return done(err); - assert(file.endsWith('swagger.json')); - assert.equal('2.0', swagger.swagger); - return done(); - }, - { - dir: path.join(__dirname, 'fixtures', 'json'), - }, - ); - }); - +describe('utils', () => { + describe('#config()', () => { it('loads main config', () => { const config = utils.config('config'); assert(Object.keys(config).length > 0); diff --git a/utils.js b/utils.js index 49a36a37a..3cbeedb70 100644 --- a/utils.js +++ b/utils.js @@ -1,164 +1,4 @@ -const fs = require('fs'); -const cardinal = require('cardinal'); -const os = require('os'); -const path = require('path'); -const glob = require('glob'); -const figures = require('figures'); - -const _ = require('lodash'); -const swagger = require('swagger-parser'); -const swaggerInline = require('swagger-inline'); - -exports.config = function(env) { +exports.config = function(env = 'config') { // eslint-disable-next-line global-require, import/no-dynamic-require - const config = require(`./config/${env || 'config'}`); - - // TODO: Make config a JS file; do this there. - config.apiFile = path.join(os.homedir(), '.readme.json'); - - return config; -}; - -exports.findSwagger = function(info, cb) { - swaggerInline('**/*', { - format: '.json', - metadata: true, - base: info.opts.in, - }).then(generatedSwaggerString => { - const generatedSwagger = JSON.parse(generatedSwaggerString); - - if (!generatedSwagger['x-si-base']) { - console.log("We couldn't find a Swagger file.".red); - console.log( - `Don't worry, it's easy to get started! Run ${'oas init'.yellow} to get started.`, - ); - process.exit(); - } - - const generatedSwaggerClone = JSON.parse(generatedSwaggerString); // Becasue swagger.validate modifies the original JSON - swagger.validate(generatedSwaggerClone, err => { - if (err) { - // TODO: We should go through the crappy validation stuff - // and try to make it easier to understand - - if (info.opts.v) { - console.log(cardinal.highlight(JSON.stringify(generatedSwagger, undefined, 2))); - } - - console.log(''); - console.log('Error validating Swagger!'.red); - console.log(''); - if (!info.opts.v) { - console.log(`Run with ${'-v'.grey} to see the invalid Swagger`); - console.log(''); - } - if (err.details) { - _.each(err.details, detail => { - const at = detail.path && detail.path.length ? ` (at ${detail.path.join('.')})` : ''; - console.log(` ${figures.cross.red} ${detail.message}${at.grey}`); - }); - } else { - console.log(`${figures.cross.red} ${err.message}`); - } - console.log(''); - process.exit(); - return; - } - - cb(undefined, generatedSwagger, generatedSwagger['x-si-base']); - }); - }); -}; - -exports.getAliasFile = function(unknownAction) { - const files = glob.sync(path.join(__dirname, 'lib', '*')); - let foundAction = false; - _.each(files, file => { - // eslint-disable-next-line global-require, import/no-dynamic-require - const actionInfo = require(file); - if (actionInfo.aliases && actionInfo.aliases.indexOf(unknownAction) >= 0) { - [foundAction] = file.match(/(\w+).js/); - } - }); - return foundAction; -}; - -exports.fileExists = function(file) { - try { - return fs.statSync(file).isFile(); - } catch (err) { - return false; - } -}; - -exports.guessLanguage = function() { - // Really simple way at guessing the language. - // If we're wrong, it's not a big deal... and - // way better than asking them what language - // they're writing (since the UI was confusing). - - let language = 'js'; - const languages = { - rb: 0, - coffee: 0, - py: 0, - js: 0, - java: 0, - php: 0, - go: 0, - }; - - const files = glob.sync('*'); - _.each(files, f => { - const ext = f.split('.').slice(-1)[0]; - if (typeof languages[ext] !== 'undefined') { - languages[ext] += 1; - } - }); - - _.each(languages, (i, l) => { - if (i > languages[language]) { - language = l; - } - }); - - return language; -}; - -exports.swaggerInlineExample = function(lang) { - const prefix = ' '; - - const annotation = [ - '@api [get] /pet/{petId}', - 'description: Returns all pets from the system that the user has access to', - 'parameters:', - ' - (path) petId=2* {Integer} The pet ID', - ' - (query) limit {Integer:int32} The number of resources to return', - ]; - - const languages = { - js: ['/*', ' * ', ' */', 'route.get("/pet/:petId", pet.show);'], - java: ['/*', ' * ', ' */', 'public String getPet(id) {'], - php: ['/*', ' * ', ' */', 'function showPet($id) {'], - coffee: ['###', '', '###', "route.get '/pet/:petId', pet.show"], - rb: ['=begin', '', '=end', "get '/pet/:petId' do"], - py: ['"""', '', '"""', 'def getPet(id):'], - go: ['/*', ' * ', ' */', 'func getPet(id) {'], - }; - - let lowercaseLang = lang.toLowerCase(); - if (!lang || !languages[lang]) lowercaseLang = 'javascript'; - - const language = languages[lowercaseLang]; - - const out = [prefix + language[0].cyan]; - - _.each(annotation, line => { - out.push(prefix + language[1].cyan + line.cyan); - }); - - out.push(prefix + language[2].cyan); - out.push(prefix + language[3].grey); - - return out.join('\n'); + return require(`./config/${env}`); };