Skip to content

Commit

Permalink
Subsite support (#674)
Browse files Browse the repository at this point in the history
* add subsite support

* fix eslint

* update bootstrap to run parent every time, then run subsite

* merge parent bootstrap/controller into subsite

* subsite support

* concat config arrays instead of overriding

* reference subsite-support branch of amphora-auth

* fixing eslint

* adding tests

* adding tests

* more tests

* more tests

* test coverage 100

* get rid of amphoraKey and set subsiteSlug for subsites only

* pr fixes

* add null check for subsiteController

* save pages/layouts with subsiteSlug

* default subsiteSlug to null - undefined was throwing errors in pg

* add test

* use clayutils getPrefix
  • Loading branch information
jpope19 authored and james-owen committed Aug 19, 2019
1 parent 8b4593c commit 29ee8ed
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 31 deletions.
29 changes: 24 additions & 5 deletions lib/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,28 @@ function bootstrapError(component, e) {
return bluebird.reject();
}

/**
* Bootstraps a site, and if it fails, falls back to the parent sites' bootstrap (for subsites)
*
* @param {Object} site
* @return {Promise}
*/
function bootstrapSite(site) {
// fetches itself if it is the main site
const parent = siteService.getParentSite(site.slug);

// using module.exports for test
return module.exports.bootstrapPath(parent.dir, site)
.catch(() => {
log('debug', `No bootstrap file found for parent site: ${site.slug}`);
})
.then(() => site.subsite ? module.exports.bootstrapPath(site.dir, site) : site)
.catch(() => {
log('debug', `No bootstrap file found for subsite: ${site.slug} ${site.subsite}`);
return site;
});
}

/**
* Bootstrap all sites and the individual
* bootstrap files in each component's directory
Expand All @@ -305,11 +327,7 @@ function bootrapSitesAndComponents() {
})
.flatMap(site => {
return highland(
bootstrapPath(site.dir, site)
.catch(function () {
log('debug', `No bootstrap file found for site: ${site.slug}`);
return site;
})
bootstrapSite(site)
);
})
.collect()
Expand Down Expand Up @@ -338,3 +356,4 @@ module.exports.bootstrapPath = bootstrapPath;
// For testing
module.exports.setLog = mock => log = mock;
module.exports.setDb = mock => db = mock;
module.exports.bootstrapSite = bootstrapSite;
42 changes: 40 additions & 2 deletions lib/bootstrap.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ describe(_.startCase(filename), function () {
sitesFake = [{
host: 'example1.com',
dir: 'example1',
prefix: 'example1.com'
prefix: 'example1.com',
slug: 'a'
}, {
host: 'example2.com',
path: '/',
Expand All @@ -33,6 +34,12 @@ describe(_.startCase(filename), function () {
path: '/some-path',
dir: 'example3',
prefix: 'example1.com/some-path'
}, {
host: 'example4.com',
path: '/',
dir: 'example4',
prefix: 'example4.com',
subsite: 'example4'
}];

bootstrapFake = files.getYaml(path.resolve('./test/fixtures/config/bootstrap'));
Expand Down Expand Up @@ -75,14 +82,15 @@ describe(_.startCase(filename), function () {
it('bootstraps', function () {
files.getComponents.returns(['a', 'b', 'c']);
files.getYaml.withArgs('example1').returns({});

siteService.getParentSite.returns(_.cloneDeep(sitesFake[0]));
return fn();
});

it('hits the `then` function when bootstrapPath is run successfully', function () {
files.getComponents.returns(['a', 'b', 'c']);
files.getYaml.withArgs('example1').returns({});
sandbox.stub(lib, 'bootstrapPath').returns(Promise.resolve({}));
siteService.getParentSite.returns(_.cloneDeep(sitesFake[0]));
return fn();
});

Expand All @@ -94,6 +102,36 @@ describe(_.startCase(filename), function () {
});
});

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

it('catches and logs if missing bootstrap', function () {
siteService.getParentSite.returns(_.cloneDeep(sitesFake[0]));
sandbox.stub(lib, 'bootstrapPath').returns(Promise.reject());
return fn(_.cloneDeep(sitesFake[0])).then(() => {
sinon.assert.calledWith(fakeLog,'debug', 'No bootstrap file found for parent site: a');
});
});

it('if no subsites, bootstrapPath runs once', function () {
siteService.getParentSite.returns(_.cloneDeep(sitesFake[0]));
sandbox.stub(lib, 'bootstrapPath').returns(Promise.resolve({}));

return fn(_.cloneDeep(sitesFake[0])).then(() => {
sinon.assert.calledOnce(lib.bootstrapPath);
});
});

it('if subsite, bootstrapPath runs twice', function () {
siteService.getParentSite.returns(_.cloneDeep(sitesFake[3]));
sandbox.stub(lib, 'bootstrapPath').returns(Promise.resolve({}));

return fn(_.cloneDeep(sitesFake[3])).then(() => {
sinon.assert.calledTwice(lib.bootstrapPath);
});
});
});

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

Expand Down
21 changes: 19 additions & 2 deletions lib/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,25 @@ function addCORS(site) {
* @returns {express.Router}
*/
function addSiteController(router, site, providers) {
if (site.dir) {
const controller = files.tryRequire(site.dir);
// fetches itself if it is the main site
const parent = siteService.getParentSite(site.slug);

if (parent && parent.dir) {
const controller = files.tryRequire(parent.dir);

// merge in subsiteController options
if (site.subsite) {
const subsiteController = files.tryRequire(site.dir);

if (subsiteController) {
// default _.merge overrides values in arrays :facepalm:
_.mergeWith(controller, subsiteController, (par, sub) => {
if (Array.isArray(par) && Array.isArray(sub)) {
return par.concat(sub);
}
});
}
}

if (controller) {
if (Array.isArray(controller.middleware)) {
Expand Down
64 changes: 58 additions & 6 deletions lib/routes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ describe(_.startCase(filename), function () {
describe('addSite', function () {
const fn = lib[this.title];

beforeEach(function () {
sandbox.stub(siteService);
});

it('adds controllers', function () {
const router = createMockRouter(),
innerRouter = createMockRouter();
Expand All @@ -119,7 +123,6 @@ describe(_.startCase(filename), function () {
sandbox.stub(files, 'tryRequire');
sandbox.stub(files, 'getFiles');
sandbox.stub(files, 'getComponents');
sandbox.stub(siteService, 'sites');

files.fileExists.returns(true);
files.tryRequire.returns(_.noop);
Expand All @@ -143,7 +146,7 @@ describe(_.startCase(filename), function () {
sandbox.stub(files, 'tryRequire');
sandbox.stub(files, 'getFiles');
sandbox.stub(files, 'getComponents');
sandbox.stub(siteService, 'sites');
siteService.getParentSite.returns(_.cloneDeep(siteStub));

files.fileExists.returns(true);
files.tryRequire.returns(_.noop);
Expand All @@ -168,7 +171,7 @@ describe(_.startCase(filename), function () {
sandbox.stub(files, 'tryRequire');
sandbox.stub(files, 'getFiles');
sandbox.stub(files, 'getComponents');
sandbox.stub(siteService, 'sites');
siteService.getParentSite.returns(_.cloneDeep(siteStub));

files.fileExists.returns(true);
files.tryRequire.onCall(0).returns({
Expand All @@ -193,7 +196,7 @@ describe(_.startCase(filename), function () {
sandbox.stub(files, 'tryRequire');
sandbox.stub(files, 'getFiles');
sandbox.stub(files, 'getComponents');
sandbox.stub(siteService, 'sites');
siteService.getParentSite.returns(_.cloneDeep(siteStub));

files.fileExists.returns(true);
files.tryRequire.onCall(0).returns(null);
Expand All @@ -217,7 +220,6 @@ describe(_.startCase(filename), function () {
sandbox.stub(files, 'fileExists');
sandbox.stub(files, 'tryRequire');
sandbox.stub(files, 'getComponents');
sandbox.stub(siteService, 'sites');

files.fileExists.returns(true);
siteService.sites.returns({
Expand All @@ -240,7 +242,6 @@ describe(_.startCase(filename), function () {
sandbox.stub(files, 'fileExists');
sandbox.stub(files, 'tryRequire');
sandbox.stub(files, 'getComponents');
sandbox.stub(siteService, 'sites');

files.fileExists.returns(false);
siteService.sites.returns({
Expand Down Expand Up @@ -309,10 +310,16 @@ describe(_.startCase(filename), function () {
describe('addSiteController', function () {
const fn = lib[this.title];

beforeEach(function () {
sandbox.stub(siteService);
});

it('does nothing if there is no site directory', function () {
const router = {},
site = {};

siteService.getParentSite.returns(_.cloneDeep(site));

fn(router, site);
});

Expand All @@ -321,6 +328,7 @@ describe(_.startCase(filename), function () {
router = {},
site = { dir: siteDir };

siteService.getParentSite.returns(_.cloneDeep(site));
sandbox.stub(files, 'tryRequire');

files.tryRequire.returns(false);
Expand All @@ -333,6 +341,7 @@ describe(_.startCase(filename), function () {
router = {},
site = { dir: siteDir };

siteService.getParentSite.returns(_.cloneDeep(site));
sandbox.stub(files, 'tryRequire');

files.tryRequire.returns({});
Expand All @@ -350,6 +359,7 @@ describe(_.startCase(filename), function () {
},
site = { dir: siteDir };

siteService.getParentSite.returns(_.cloneDeep(site));
sandbox.stub(files, 'tryRequire');

files.tryRequire.returns({
Expand All @@ -375,6 +385,7 @@ describe(_.startCase(filename), function () {
},
site = { dir: siteDir };

siteService.getParentSite.returns(_.cloneDeep(site));
sandbox.stub(files, 'tryRequire');

files.tryRequire.returns({
Expand All @@ -391,12 +402,53 @@ describe(_.startCase(filename), function () {
router = {},
site = { dir: siteDir };

siteService.getParentSite.returns(_.cloneDeep(site));
sandbox.stub(files, 'tryRequire');

files.tryRequire.returns({});

fn(router, site);
});

it('merges in subsite controller', function () {
const siteDir = 'some-location',
paths = [],
router = {
get(route) {
paths.push(route);
}
},
site = { dir: siteDir, subsite: 'some-subsite' };

siteService.getParentSite.returns(_.cloneDeep(site));
sandbox.stub(files, 'tryRequire');

files.tryRequire.onFirstCall().returns({ routes: [{ path: '/foo' }], foo: 'bar' });
files.tryRequire.onSecondCall().returns({ routes: [{ path: '/bar' }], foo: 'bat' });

fn(router, site);
expect(paths).to.deep.equal(['/foo', '/bar']);
});

it('uses parent controller if subsite controller does not exist', function () {
const siteDir = 'some-location',
paths = [],
router = {
get(route) {
paths.push(route);
}
},
site = { dir: siteDir, subsite: 'some-subsite' };

siteService.getParentSite.returns(_.cloneDeep(site));
sandbox.stub(files, 'tryRequire');

files.tryRequire.onFirstCall().returns({ routes: [{ path: '/foo' }], foo: 'bar' });
files.tryRequire.onSecondCall().returns(null);

fn(router, site);
expect(paths).to.deep.equal(['/foo']);
});
});

describe('addAuthenticationRoutes', function () {
Expand Down
10 changes: 7 additions & 3 deletions lib/services/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const _ = require('lodash'),
bus = require('./bus'),
{ replaceVersion } = require('clayutils'),
{ replaceVersion, getPrefix } = require('clayutils'),
sitesService = require('./sites');
var db = require('./db');

Expand Down Expand Up @@ -59,6 +59,7 @@ function publishPage(uri, publishMeta, user) {
function createPage(ref, user) {
const NOW = new Date().toISOString(),
users = [userOrRobot(user)],
site = sitesService.getSiteFromPrefix(getPrefix(ref)),
meta = {
createdAt: NOW,
archived: false,
Expand All @@ -72,7 +73,8 @@ function createPage(ref, user) {
authors: [],
users,
history: [{action: 'create', timestamp: NOW, users }],
siteSlug: sitesService.getSiteFromPrefix(ref.substring(0, ref.indexOf('/_pages'))).slug
siteSlug: site.slug,
subsiteSlug: site.subsiteSlug || null
};

return putMeta(ref, meta);
Expand All @@ -81,6 +83,7 @@ function createPage(ref, user) {
function createLayout(ref, user) {
const NOW = new Date().toISOString(),
users = [userOrRobot(user)],
site = sitesService.getSiteFromPrefix(getPrefix(ref)),
meta = {
createdAt: NOW,
published: false,
Expand All @@ -89,7 +92,8 @@ function createLayout(ref, user) {
firstPublishTime: null,
title: '',
history: [{ action: 'create', timestamp: NOW, users }],
siteSlug: sitesService.getSiteFromPrefix(ref.substring(0, ref.indexOf('/_layouts'))).slug
siteSlug: site.slug,
subsiteSlug: site.subsiteSlug || null
};

return putMeta(ref, meta);
Expand Down
22 changes: 22 additions & 0 deletions lib/services/metadata.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,17 @@ describe(_.startCase(filename), function () {
sinon.assert.calledOnce(bus.publish);
});
});

it('creates page meta with subsite', function () {
db.putMeta.returns(Promise.resolve());
siteService.getSiteFromPrefix.returns({ slug: 'foo', subsiteSlug: 'foo/bar' });

return fn('domain.com/_pages/id/meta', { username: 'foo', provider: 'bar' })
.then(() => {
sinon.assert.calledOnce(db.putMeta);
sinon.assert.calledOnce(bus.publish);
});
});
});

describe('createLayout', function () {
Expand All @@ -146,6 +157,17 @@ describe(_.startCase(filename), function () {
sinon.assert.calledOnce(bus.publish);
});
});

it('creates layout meta with subsite', function () {
db.putMeta.returns(Promise.resolve());
siteService.getSiteFromPrefix.returns({ slug: 'foo', subsiteSlug: 'foo/bar' });

return fn('domain.com/_pages/id/meta', { username: 'foo', provider: 'bar' })
.then(() => {
sinon.assert.calledOnce(db.putMeta);
sinon.assert.calledOnce(bus.publish);
});
});
});

describe('publishPage', function () {
Expand Down
Loading

0 comments on commit 29ee8ed

Please sign in to comment.