Skip to content

Commit

Permalink
Merge pull request #106 from nymag/components-list
Browse files Browse the repository at this point in the history
get components list
  • Loading branch information
nelsonpecora committed Jul 15, 2015
2 parents 09b76fe + 2d46049 commit dd285a1
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 39 deletions.
59 changes: 34 additions & 25 deletions lib/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,38 +59,42 @@ function getFolders(dir) {
function getComponents() {
var deps = pkg.dependencies,
npmComponents = _(deps).map(function (version, name) {
return _.contains(name, 'byline-') && name;
if (_.contains(name, 'byline-')) {
// this is a byline component
if (_.contains(name, path.sep)) {
// this is a scoped npm component!
// return the name without the scope/user
return name.split(path.sep)[1];
} else {
// this is an unscoped npm component
return name;
}
} // otherwise returns undefined, and is compacted below
}).compact().value();

return getFolders(path.resolve('components')).concat(npmComponents);
}

/**
* get a component name, so we can look it up in the file system
* internal components and regular npm components should quickly return their names
* scoped npm components will return the fully-scoped name
* @param {string} name
* @returns {string|null}
* @returns {string|false}
*/
function getComponentName(name) {
var allComponents = exports.getComponents(),
i = 0,
l = allComponents.length;
var allComponents = exports.getComponents();

if (_.contains(allComponents, name)) {
// do a quick check for the component, return quickly if found
return name;
} else {
// the component might be a scoped npm module, do a slower deep check
for (; i < l; i++) {
if (_.contains(allComponents[i], name)) {
return allComponents[i];
}
}
return _.contains(allComponents, name) && name;
}

// no matches?
return null;
}
/**
* get the full name of a possibly-scoped npm component
* @param {string} name, e.g. 'byline-editor'
* @returns {string|undefined} e.g. '@nymdev/byline-editor'
*/
function getNPMName(name) {
return _.findKey(pkg.dependencies, function (version, dep) {
return _.contains(dep, name);
});
}

/**
Expand All @@ -99,7 +103,7 @@ function getComponentName(name) {
* @return {string}
*/
function getComponentPath(name) {
var fullName = getComponentName(name);
var possInternalPath, possNpmPath;

// make sure it's a component we have (either in /components or package.json)
if (!getComponentName(name)) {
Expand All @@ -110,10 +114,15 @@ function getComponentPath(name) {
* we should not throw an exception.
*/
return null;
} else if (fs.existsSync(path.resolve('components', fullName))) {
return path.resolve('components', fullName);
} else if (fs.existsSync(path.resolve('node_modules', fullName))) {
return path.resolve('node_modules', fullName);
} else {
possInternalPath = path.resolve('components', name);
possNpmPath = path.resolve('node_modules', getNPMName(name));

if (fs.existsSync(possInternalPath)) {
return possInternalPath;
} else if (fs.existsSync(possNpmPath)) {
return possNpmPath;
}
}
}

Expand Down
45 changes: 43 additions & 2 deletions lib/files.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,55 @@ describe(_.startCase(filename), function () {
});

describe('getComponents', function () {
it('gets a list of components', function () {
it('gets a list of internal components', function () {
dirStub.withArgs('components').returns(['c1', 'c2']);
statStub.withArgs('components/c1').returns(createMockStat({isDirectory: true}));
statStub.withArgs('components/c2').returns(createMockStat({isDirectory: false}));
resolveStub.returnsArg(0);

expect(files.getComponents()).to.contain('c1', 'c2');
});

it('gets a list of npm components', function () {
dirStub.withArgs('components').returns([]);
resolveStub.returnsArg(0);
files.usePackage(pkg);

expect(files.getComponents()).to.contain('c1', 'c2', 'byline-c3', 'byline-c4', 'byline-c5');
expect(files.getComponents()).to.contain('byline-c3', 'byline-c4');
});
});

describe('getComponentPath', function () {
var fn = files[this.title],
existsStub;

beforeEach(function () {
existsStub = sandbox.stub(fs, 'existsSync');
sandbox.stub(files, 'getComponents').returns(['c1', 'byline-c5', 'byline-c3']);
existsStub.returns(false);
existsStub.withArgs('components/c1').returns(true);
existsStub.withArgs('node_modules/byline-c3').returns(true);
existsStub.withArgs('node_modules/@a/byline-c5').returns(true);
resolveStub.withArgs('components', 'c1').returns('components/c1');
resolveStub.withArgs('node_modules', 'byline-c3').returns('node_modules/byline-c3');
resolveStub.withArgs('node_modules', '@a/byline-c5').returns('node_modules/@a/byline-c5');
files.usePackage(pkg);
});

it('returns null if name isn\'t a component', function () {
expect(fn('c0')).to.equal(null);
});

it('gets an internal path', function () {
expect(fn('c1')).to.equal('components/c1');
});

it('gets an npm path', function () {
expect(fn('byline-c3')).to.equal('node_modules/byline-c3');
});

it('gets a scoped npm path', function () {
expect(fn('byline-c5')).to.equal('node_modules/@a/byline-c5');
});
});
});
11 changes: 9 additions & 2 deletions lib/routes/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ let route = _.bindAll({
}, res);
},

list: function (req, res) {
responses.expectJSON(function () {
return controller.list();
}, res);
},

/**
* @param req
* @param res
Expand Down Expand Up @@ -139,7 +145,8 @@ function routes(router) {
router.use(responses.onlyCachePublished);

router.all('/', responses.methodNotAllowed({allow: ['get']}));
router.get('/', responses.notImplemented);
router.all('/', responses.notAcceptable({accept: ['application/json']}));
router.get('/', route.list);

router.all('/:name*', validation.componentMustExist);
router.get('/:name.:ext', responses.onlyAcceptExtensions({extensions: acceptedExtensions}));
Expand Down Expand Up @@ -179,4 +186,4 @@ function routes(router) {
router.get('/:name/schema', route.schema);
}

module.exports = routes;
module.exports = routes;
9 changes: 9 additions & 0 deletions lib/services/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ function get(ref, locals) {
return promise;
}

/**
* return a list of all components
* @returns {array}
*/
function list() {
return files.getComponents();
}

/**
* PUT to just :id or @latest writes to both locations and creates a new version.
* @param {string} ref Assumes no @version
Expand Down Expand Up @@ -330,6 +338,7 @@ function getTemplate(ref) {

// outsiders can act on components too
module.exports.get = get;
module.exports.list = list;
module.exports.put = cascadingPut; // special: could lead to multiple put operations
module.exports.del = del;
module.exports.post = post;
Expand Down
12 changes: 11 additions & 1 deletion lib/services/components.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ describe(_.startCase(filename), function () {
});
});


describe('putLatest', function () {
var fn = lib[this.title];

Expand Down Expand Up @@ -228,4 +229,13 @@ describe(_.startCase(filename), function () {
return fn('/components/whatever');
});
});
});

describe('list', function () {
var fn = lib[this.title];

it('gets a list of components', function () {
sandbox.stub(files, 'getComponents').returns(bluebird.resolve([]));
return fn('/components');
});
});
});
18 changes: 10 additions & 8 deletions test/api/components/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ describe(endpointName, function () {
hostname = 'localhost.example.com',
acceptsJson = apiAccepts.acceptsJson(_.camelCase(filename)),
acceptsHtml = apiAccepts.acceptsHtml(_.camelCase(filename)),
data = { name: 'Manny', species: 'cat' };
data = { name: 'Manny', species: 'cat' },
componentList = [ 'byline-c5', 'byline-c3', 'byline-c4' ],
message406 = '406 text/html not acceptable';

beforeEach(function () {
sandbox = sinon.sandbox.create();
Expand All @@ -25,8 +27,8 @@ describe(endpointName, function () {

describe('/components', function () {
var path = this.title;
acceptsJson(path, {}, 501);
acceptsHtml(path, {}, 501);
acceptsJson(path, {}, 200, componentList);
acceptsHtml(path, {}, 406, message406);
});

describe('/components/:name', function () {
Expand All @@ -37,8 +39,8 @@ describe(endpointName, function () {
acceptsJson(path, {name: 'missing'}, 404);

acceptsHtml(path, {name: 'invalid'}, 404, '404 Not Found');
acceptsHtml(path, {name: 'valid'}, 406);
acceptsHtml(path, {name: 'missing'}, 406);
acceptsHtml(path, {name: 'valid'}, 406, message406);
acceptsHtml(path, {name: 'missing'}, 406, message406);
});

describe('/components/:name/schema', function () {
Expand All @@ -49,8 +51,8 @@ describe(endpointName, function () {
acceptsJson(path, {name: 'missing'}, 404);

acceptsHtml(path, {name: 'invalid'}, 404, '404 Not Found');
acceptsHtml(path, {name: 'valid'}, 406);
acceptsHtml(path, {name: 'missing'}, 406);
acceptsHtml(path, {name: 'valid'}, 406, message406);
acceptsHtml(path, {name: 'missing'}, 406, message406);
});

describe('/components/:name.html', function () {
Expand Down Expand Up @@ -125,4 +127,4 @@ describe(endpointName, function () {
acceptsHtml(path, {name: 'valid', version: 'missing', id: 'missing'}, 406);
});
});
});
});
2 changes: 1 addition & 1 deletion test/fixtures/config/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"dependencies": {
"@nymdev/byline-editor": "v1.1.1",
"@a/byline-c5": "v1.1.1",
"byline-c3": "v1.1.1",
"byline-c4": "v1.1.1"
}
Expand Down

0 comments on commit dd285a1

Please sign in to comment.