Skip to content

Commit

Permalink
Add rdme login command
Browse files Browse the repository at this point in the history
  • Loading branch information
Dom Harrington committed Oct 1, 2018
1 parent 234bb18 commit de88b5e
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 18 deletions.
2 changes: 1 addition & 1 deletion lib/docs/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const unlink = promisify(fs.unlink);

exports.desc = 'Edit a single file from your ReadMe project without saving locally';
exports.category = 'services';
exports.weight = 3;
exports.weight = 4;
exports.action = 'docs:edit';

exports.run = async function({ args, opts }) {
Expand Down
2 changes: 1 addition & 1 deletion lib/docs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const readFile = promisify(fs.readFile);

exports.desc = 'Sync a folder of markdown files to your ReadMe project';
exports.category = 'services';
exports.weight = 2;
exports.weight = 3;
exports.action = 'docs';

exports.run = function({ args, opts }) {
Expand Down
25 changes: 11 additions & 14 deletions lib/help.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ const path = require('path');
const fs = require('fs');
const config = require('config');

exports.category = 'basic';
exports.desc = 'Learn what you can do with this tool';
exports.weight = 2;

function pad(text) {
return text.padEnd(15);
}
Expand All @@ -27,12 +23,12 @@ exports.run = function() {
.map(file => path.join(__dirname, file));

const categories = {
basic: {
desc: 'Commands for getting started',
services: {
desc: `Interact with your ReadMe project`,
commands: [],
},
services: {
desc: `Hosted third-party services ${'(Will post to the Internet)'.grey}`,
utilities: {
desc: `Other useful commands`,
commands: [],
},
};
Expand All @@ -43,12 +39,13 @@ exports.run = function() {
const f = require(file);
const info = f.desc || '';

if (f.category) {
categories[f.category].commands.push({
text: `${' $'.grey + pad(` ${config.cli} ${f.action || action}`)} ${info.grey}`,
weight: f.weight,
});
}
// Some commands dont have docs e.g. help
if (!f.category) return;

categories[f.category].commands.push({
text: `${' $'.grey + pad(` ${config.cli} ${f.action || action}`)} ${info.grey}`,
weight: f.weight,
});
});

Object.keys(categories).forEach(key => {
Expand Down
62 changes: 62 additions & 0 deletions lib/login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const request = require('request-promise-native');
const config = require('config');
const read = require('read');
const Configstore = require('configstore');
const { validate: isEmail } = require('isemail');

exports.desc = 'Login to a ReadMe project';
exports.category = 'services';
exports.weight = 1;

const pkg = require('../package.json');

const conf = new Configstore(pkg.name);

function getCredentials() {
return new Promise((resolve, reject) => {
read({ prompt: 'Email:', default: conf.get('email') }, (emailErr, email) => {
if (emailErr) return reject(emailErr);

return read({ prompt: 'Password:', silent: true }, (passwordErr, password) => {
if (passwordErr) return reject(passwordErr);

return resolve({ email, password });
});
})
});
}

exports.run = async function({ opts }) {
const { project } = opts;

if (!project) {
return Promise.reject(new Error('No project subdomain provided. Please use --project'));
}

let { email, password } = opts;

if (!email) {
({ email, password } = await getCredentials());
}

if (!isEmail(email)) {
return Promise.reject(new Error('You must provide a valid email address.'));
}

function badRequest(err) {
if (err.statusCode === 400) {
return Promise.reject(err.error);
}

return Promise.reject(err);
}

return request.post(`${config.host}/api/v1/login`, {
json: { email, password, project },
}).then((res) => {
conf.set('apiKey', res.apiKey);
conf.set('email', email);
conf.set('project', project);
return `Successfully logged in as ${email.green} in the ${project.blue} project`;
}).catch(badRequest);
}
2 changes: 1 addition & 1 deletion lib/oas.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const { promisify } = require('util');
const spawn = promisify(cp.spawn);

exports.desc = 'OAS related tasks. See https://www.npmjs.com/package/oas';
exports.category = 'services';
exports.category = 'utilities';
exports.weight = 4;

exports.run = function() {
Expand Down
2 changes: 1 addition & 1 deletion lib/swagger.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const config = require('config');

exports.desc = 'Upload your swagger file to ReadMe';
exports.category = 'services';
exports.weight = 1;
exports.weight = 2;

exports.run = function({ args, opts }) {
let { key, id } = opts;
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@
"dependencies": {
"colors": "^1.1.2",
"config": "^1.30.0",
"configstore": "^4.0.0",
"editor": "^1.0.0",
"gray-matter": "^4.0.1",
"isemail": "^3.1.3",
"minimist": "^1.2.0",
"oas": "^0.8.8",
"read": "^1.0.7",
"request": "^2.88.0",
"request-promise-native": "^1.0.5"
},
Expand Down
66 changes: 66 additions & 0 deletions test/login.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const nock = require('nock');
const config = require('config');
const assert = require('assert');
const Configstore = require('configstore');

const pkg = require('../package');

const login = require('../cli').bind(null, 'login');

describe('login command', () => {
beforeAll(() => nock.disableNetConnect());
afterAll(() => nock.cleanAll());

it('should error if no project provided', (done) =>
login([], {}).catch(err => {
assert.equal(err.message, 'No project subdomain provided. Please use --project');
return done();
}));

it('should error if email is invalid', (done) =>
login([], { project: 'subdomain', email: 'this-is-not-an-email' }).catch(err => {
assert.equal(err.message, 'You must provide a valid email address.');
return done();
}));

it('should post to /login on the API', () => {
const email = '[email protected]';
const password = '123456';
const project = 'subdomain';
const apiKey = 'abcdefg';

const mock = nock(config.host)
.post('/api/v1/login', { email, password, project })
.reply(200, { apiKey });

return login([], { email, password, project }).then(() => {
mock.done();
const conf = new Configstore(pkg.name);
assert.equal(conf.get('apiKey'), apiKey);
assert.equal(conf.get('email'), email);
assert.equal(conf.get('project'), project);
});
});

it('should error if invalid credentials are given', (done) => {
const email = '[email protected]';
const password = '123456';
const project = 'subdomain';

const mock = nock(config.host)
.post('/api/v1/login', { email, password, project })
.reply(400, {
description: 'Invalid email/password',
error: 'Bad Request',
});

return login([], { email, password, project }).catch((err) => {
mock.done();
assert.equal(err.error, 'Bad Request');
assert.equal(err.description, 'Invalid email/password');
return done();
});
});

it('should error if trying to access a project that is not yours', () => {});
});

0 comments on commit de88b5e

Please sign in to comment.