diff --git a/src/actions/__tests__/config.spec.js b/src/actions/__tests__/config.spec.js index ffd102ea..3ff97f0f 100644 --- a/src/actions/__tests__/config.spec.js +++ b/src/actions/__tests__/config.spec.js @@ -1,15 +1,9 @@ -import * as config from '../config'; +import { applyDefaults, validateConfig } from '../config'; describe('config', () => { describe('applyDefaults', () => { - it('should throw if media_folder is not defined in config', () => { - expect(() => { - config.applyDefaults({ foo: 'bar' }); - }).toThrowError('Error in configuration file: A `media_folder` wasn\'t found. Check your config.yml file.'); - }); - it('should set publish_mode if not set', () => { - expect(config.applyDefaults({ + expect(applyDefaults({ foo: 'bar', media_folder: 'path/to/media', })).toEqual({ @@ -21,7 +15,7 @@ describe('config', () => { }); it('should set publish_mode from config', () => { - expect(config.applyDefaults({ + expect(applyDefaults({ foo: 'bar', publish_mode: 'complex', media_folder: 'path/to/media', @@ -34,7 +28,7 @@ describe('config', () => { }); it('should set public_folder based on media_folder if not set', () => { - expect(config.applyDefaults({ + expect(applyDefaults({ foo: 'bar', media_folder: 'path/to/media', })).toEqual({ @@ -46,7 +40,7 @@ describe('config', () => { }); it('should not overwrite public_folder if set', () => { - expect(config.applyDefaults({ + expect(applyDefaults({ foo: 'bar', media_folder: 'path/to/media', public_folder: '/publib/path', @@ -58,4 +52,67 @@ describe('config', () => { }); }); }); -}); + + describe('validateConfig', () => { + it('should return the config if no errors', () => { + const config = { foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz', collections: [{}] }; + expect( + validateConfig(config) + ).toEqual(config); + }); + + it('should throw if backend is not defined in config', () => { + expect(() => { + validateConfig({ foo: 'bar' }); + }).toThrowError('Error in configuration file: A `backend` wasn\'t found. Check your config.yml file.'); + }); + + it('should throw if backend name is not defined in config', () => { + expect(() => { + validateConfig({ foo: 'bar', backend: {} }); + }).toThrowError('Error in configuration file: A `backend.name` wasn\'t found. Check your config.yml file.'); + }); + + it('should throw if backend name is not a string in config', () => { + expect(() => { + validateConfig({ foo: 'bar', backend: { name: { } } }); + }).toThrowError('Error in configuration file: Your `backend.name` must be a string. Check your config.yml file.'); + }); + + it('should throw if media_folder is not defined in config', () => { + expect(() => { + validateConfig({ foo: 'bar', backend: { name: 'bar' } }); + }).toThrowError('Error in configuration file: A `media_folder` wasn\'t found. Check your config.yml file.'); + }); + + it('should throw if media_folder is not a string in config', () => { + expect(() => { + validateConfig({ foo: 'bar', backend: { name: 'bar' }, media_folder: {} }); + }).toThrowError('Error in configuration file: Your `media_folder` must be a string. Check your config.yml file.'); + }); + + it('should throw if collections is not defined in config', () => { + expect(() => { + validateConfig({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz' }); + }).toThrowError('Error in configuration file: A `collections` wasn\'t found. Check your config.yml file.'); + }); + + it('should throw if collections not an array in config', () => { + expect(() => { + validateConfig({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz', collections: {} }); + }).toThrowError('Error in configuration file: Your `collections` must be an array with at least one element. Check your config.yml file.'); + }); + + it('should throw if collections is an empty array in config', () => { + expect(() => { + validateConfig({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz', collections: [] }); + }).toThrowError('Error in configuration file: Your `collections` must be an array with at least one element. Check your config.yml file.'); + }); + + it('should throw if collections is an array with a single null element in config', () => { + expect(() => { + validateConfig({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz', collections: [null] }); + }).toThrowError('Error in configuration file: Your `collections` must be an array with at least one element. Check your config.yml file.'); + }); + }); +}); \ No newline at end of file diff --git a/src/actions/config.js b/src/actions/config.js index 330bee55..5f1c3f2d 100644 --- a/src/actions/config.js +++ b/src/actions/config.js @@ -1,5 +1,5 @@ import yaml from "js-yaml"; -import { set, defaultsDeep } from "lodash"; +import { set, defaultsDeep, get } from "lodash"; import { authenticateUser } from "../actions/auth"; import * as publishModes from "../constants/publishModes"; @@ -12,10 +12,6 @@ const defaults = { }; export function applyDefaults(config) { - if (!("media_folder" in config)) { - throw new Error("Error in configuration file: A `media_folder` wasn't found. Check your config.yml file."); - } - // Make sure there is a public folder set(defaults, "public_folder", @@ -24,20 +20,40 @@ export function applyDefaults(config) { return defaultsDeep(config, defaults); } -function parseConfig(data) { - const config = yaml.safeLoad(data); - if (typeof CMS_ENV === "string" && config[CMS_ENV]) { - // TODO: Add tests and refactor - for (const key in config[CMS_ENV]) { // eslint-disable-line no-restricted-syntax - if (config[CMS_ENV].hasOwnProperty(key)) { // eslint-disable-line no-prototype-builtins - config[key] = config[CMS_ENV][key]; - } - } +export function validateConfig(config) { + if (!get(config, 'backend')) { + throw new Error("Error in configuration file: A `backend` wasn't found. Check your config.yml file."); + } + if (!get(config, ['backend', 'name'])) { + throw new Error("Error in configuration file: A `backend.name` wasn't found. Check your config.yml file."); + } + if (typeof config.backend.name !== 'string') { + throw new Error("Error in configuration file: Your `backend.name` must be a string. Check your config.yml file."); + } + if (!get(config, 'media_folder')) { + throw new Error("Error in configuration file: A `media_folder` wasn\'t found. Check your config.yml file."); + } + if (typeof config.media_folder !== 'string') { + throw new Error("Error in configuration file: Your `media_folder` must be a string. Check your config.yml file."); + } + if (!get(config, 'collections')) { + throw new Error("Error in configuration file: A `collections` wasn\'t found. Check your config.yml file."); + } + if (!Array.isArray(config.collections) || config.collections.length === 0 || !config.collections[0]) { + throw new Error("Error in configuration file: Your `collections` must be an array with at least one element. Check your config.yml file."); } - return config; } +function parseConfig(data) { + const config = yaml.safeLoad(data); + if (typeof CMS_ENV === "string" && config[CMS_ENV]) { + Object.keys(config[CMS_ENV]).forEach((key) => { + config[key] = config[CMS_ENV][key]; + }); + } + return config; +} export function configLoaded(config) { return { @@ -81,6 +97,7 @@ export function loadConfig() { return response.text(); }) .then(parseConfig) + .then(validateConfig) .then(applyDefaults) .then((config) => { dispatch(configDidLoad(config)); diff --git a/src/reducers/collections.js b/src/reducers/collections.js index 4cf076c8..aa9860b8 100644 --- a/src/reducers/collections.js +++ b/src/reducers/collections.js @@ -1,20 +1,19 @@ import { OrderedMap, fromJS } from 'immutable'; +import { has } from 'lodash'; import consoleError from '../lib/consoleError'; import { CONFIG_SUCCESS } from '../actions/config'; import { FILES, FOLDER } from '../constants/collectionTypes'; import { INFERABLE_FIELDS } from '../constants/fieldInference'; -const hasProperty = (config, property) => ({}.hasOwnProperty.call(config, property)); - const collections = (state = null, action) => { const configCollections = action.payload && action.payload.collections; switch (action.type) { case CONFIG_SUCCESS: return OrderedMap().withMutations((map) => { (configCollections || []).forEach((configCollection) => { - if (hasProperty(configCollection, 'folder')) { + if (has(configCollection, 'folder')) { configCollection.type = FOLDER; // eslint-disable-line no-param-reassign - } else if (hasProperty(configCollection, 'files')) { + } else if (has(configCollection, 'files')) { configCollection.type = FILES; // eslint-disable-line no-param-reassign } else { throw new Error('Unknown collection type. Collections can be either Folder based or File based. Please verify your site configuration');