Skip to content

Commit

Permalink
refactor: mutate config in normalizeConfig and handleLocalBackend fns (
Browse files Browse the repository at this point in the history
  • Loading branch information
Vladislav Shkodin authored Feb 7, 2021
1 parent 319a538 commit 3924cfb
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {

jest.spyOn(console, 'log').mockImplementation(() => {});
jest.spyOn(console, 'warn').mockImplementation(() => {});
jest.mock('coreSrc/backend', () => {
jest.mock('../../backend', () => {
return {
resolveBackend: jest.fn(() => ({ isGitBackend: jest.fn(() => true) })),
};
Expand Down Expand Up @@ -452,8 +452,8 @@ describe('config', () => {
test('should convert camel case to snake case', () => {
expect(
applyDefaults(
normalizeConfig(
fromJS({
fromJS(
normalizeConfig({
collections: [
{
sortableFields: ['title'],
Expand Down Expand Up @@ -922,7 +922,7 @@ describe('config', () => {
window.location = { hostname: 'localhost' };
global.fetch = jest.fn().mockRejectedValue(new Error());

const config = fromJS({ local_backend: true, backend: { name: 'github' } });
const config = { local_backend: true, backend: { name: 'github' } };
const actual = await handleLocalBackend(config);

expect(actual).toEqual(config);
Expand Down
192 changes: 101 additions & 91 deletions packages/netlify-cms-core/src/actions/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,32 @@ import yaml from 'yaml';
import { Map, fromJS } from 'immutable';
import deepmerge from 'deepmerge';
import { trimStart, trim, get, isPlainObject, isEmpty } from 'lodash';
import * as publishModes from 'Constants/publishModes';
import { validateConfig } from 'Constants/configSchema';
import { SIMPLE as SIMPLE_PUBLISH_MODE } from '../constants/publishModes';
import { validateConfig } from '../constants/configSchema';
import { selectDefaultSortableFields, traverseFields } from '../reducers/collections';
import { getIntegrations, selectIntegration } from '../reducers/integrations';
import { resolveBackend } from 'coreSrc/backend';
import { resolveBackend } from '../backend';
import { I18N, I18N_FIELD, I18N_STRUCTURE } from '../lib/i18n';

export const CONFIG_REQUEST = 'CONFIG_REQUEST';
export const CONFIG_SUCCESS = 'CONFIG_SUCCESS';
export const CONFIG_FAILURE = 'CONFIG_FAILURE';

const traverseFieldsJS = (fields, updater) => {
return fields.map(field => {
let newField = updater(field);
if (newField.fields) {
newField = { ...newField, fields: traverseFieldsJS(newField.fields, updater) };
} else if (newField.field) {
newField = { ...newField, field: traverseFieldsJS([newField.field], updater)[0] };
} else if (newField.types) {
newField = { ...newField, types: traverseFieldsJS(newField.types, updater) };
}

return newField;
});
};

const getConfigUrl = () => {
const validTypes = { 'text/yaml': 'yaml', 'application/x-yaml': 'yaml' };
const configLinkEl = document.querySelector('link[rel="cms-config-url"]');
Expand All @@ -32,31 +47,30 @@ const setDefaultPublicFolder = map => {
return map;
};

const setSnakeCaseConfig = field => {
// Mapping between existing camelCase and its snake_case counterpart
const widgetKeyMap = {
dateFormat: 'date_format',
timeFormat: 'time_format',
pickerUtc: 'picker_utc',
editorComponents: 'editor_components',
valueType: 'value_type',
valueField: 'value_field',
searchFields: 'search_fields',
displayFields: 'display_fields',
optionsLength: 'options_length',
};
// Mapping between existing camelCase and its snake_case counterpart
const WIDGET_KEY_MAP = {
dateFormat: 'date_format',
timeFormat: 'time_format',
pickerUtc: 'picker_utc',
editorComponents: 'editor_components',
valueType: 'value_type',
valueField: 'value_field',
searchFields: 'search_fields',
displayFields: 'display_fields',
optionsLength: 'options_length',
};

Object.entries(widgetKeyMap).forEach(([camel, snake]) => {
if (field.has(camel)) {
field = field.set(snake, field.get(camel));
console.warn(
`Field ${field.get(
'name',
)} is using a deprecated configuration '${camel}'. Please use '${snake}'`,
);
}
const setSnakeCaseConfig = field => {
const deprecatedKeys = Object.keys(WIDGET_KEY_MAP).filter(camel => camel in field);
const snakeValues = deprecatedKeys.map(camel => {
const snake = WIDGET_KEY_MAP[camel];
console.warn(
`Field ${field.name} is using a deprecated configuration '${camel}'. Please use '${snake}'`,
);
return { [snake]: field[camel] };
});
return field;

return Object.assign({}, field, ...snakeValues);
};

const setI18nField = field => {
Expand Down Expand Up @@ -141,7 +155,7 @@ const setViewPatternsDefaults = (key, collection) => {
};

const defaults = {
publish_mode: publishModes.SIMPLE,
publish_mode: SIMPLE_PUBLISH_MODE,
};

const hasIntegration = (config, collection) => {
Expand All @@ -151,45 +165,38 @@ const hasIntegration = (config, collection) => {
};

export function normalizeConfig(config) {
return Map(config).withMutations(map => {
map.set(
'collections',
map.get('collections').map(collection => {
const folder = collection.get('folder');
if (folder) {
collection = collection.set(
'fields',
traverseFields(collection.get('fields'), setSnakeCaseConfig),
);
}

const files = collection.get('files');
if (files) {
collection = collection.set(
'files',
files.map(file => {
file = file.set('fields', traverseFields(file.get('fields'), setSnakeCaseConfig));
return file;
}),
);
}

if (collection.has('sortableFields')) {
collection = collection
.set('sortable_fields', collection.get('sortableFields'))
.delete('sortableFields');

console.warn(
`Collection ${collection.get(
'name',
)} is using a deprecated configuration 'sortableFields'. Please use 'sortable_fields'`,
);
}

return collection;
}),
);
const { collections = [] } = config;

const normalizedCollections = collections.map(collection => {
const { fields, files } = collection;

let normalizedCollection = collection;
if (fields) {
const normalizedFields = traverseFieldsJS(fields, setSnakeCaseConfig);
normalizedCollection = { ...normalizedCollection, fields: normalizedFields };
}

if (files) {
const normalizedFiles = files.map(file => {
const normalizedFileFields = traverseFieldsJS(file.fields, setSnakeCaseConfig);
return { ...file, fields: normalizedFileFields };
});
normalizedCollection = { ...normalizedCollection, files: normalizedFiles };
}

if (normalizedCollection.sortableFields) {
const { sortableFields, ...rest } = normalizedCollection;
normalizedCollection = { ...rest, sortable_fields: sortableFields };

console.warn(
`Collection ${collection.name} is using a deprecated configuration 'sortableFields'. Please use 'sortable_fields'`,
);
}

return normalizedCollection;
});

return { ...config, collections: normalizedCollections };
}

export function applyDefaults(config) {
Expand Down Expand Up @@ -383,36 +390,38 @@ export async function detectProxyServer(localBackend) {
return {};
}

export async function handleLocalBackend(originalConfig) {
if (!originalConfig.local_backend) {
return originalConfig;
const getPublishMode = (config, publishModes, backendType) => {
if (config.publish_mode && publishModes && !publishModes.includes(config.publish_mode)) {
const newPublishMode = publishModes[0];
console.log(
`'${config.publish_mode}' is not supported by '${backendType}' backend, switching to '${newPublishMode}'`,
);
return newPublishMode;
}

const { proxyUrl, publish_modes, type } = await detectProxyServer(originalConfig.local_backend);
return config.publish_mode;
};

if (!proxyUrl) {
return originalConfig;
export const handleLocalBackend = async config => {
if (!config.local_backend) {
return config;
}

let mergedConfig = deepmerge(originalConfig, {
backend: { name: 'proxy', proxy_url: proxyUrl },
});
const { proxyUrl, publish_modes: publishModes, type: backendType } = await detectProxyServer(
config.local_backend,
);

if (
mergedConfig.publish_mode &&
publish_modes &&
!publish_modes.includes(mergedConfig.publish_mode)
) {
const newPublishMode = publish_modes[0];
mergedConfig = deepmerge(mergedConfig, {
publish_mode: newPublishMode,
});
console.log(
`'${mergedConfig.publish_mode}' is not supported by '${type}' backend, switching to '${newPublishMode}'`,
);
if (!proxyUrl) {
return config;
}
return mergedConfig;
}

const publishMode = getPublishMode(config, publishModes, backendType);
return {
...config,
...(publishMode && { publish_mode: publishMode }),
backend: { ...config.backend, name: 'proxy', proxy_url: proxyUrl },
};
};

export function loadConfig(manualConfig = {}, onLoad) {
if (window.CMS_CONFIG) {
Expand All @@ -430,13 +439,14 @@ export function loadConfig(manualConfig = {}, onLoad) {
: await getConfigYaml(configUrl, hasManualConfig);

// Merge manual config into the config.yml one
let mergedConfig = deepmerge(configYaml, manualConfig);
const mergedConfig = deepmerge(configYaml, manualConfig);

validateConfig(mergedConfig);

mergedConfig = await handleLocalBackend(mergedConfig);
const withLocalBackend = await handleLocalBackend(mergedConfig);
const normalizedConfig = normalizeConfig(withLocalBackend);

const config = applyDefaults(normalizeConfig(fromJS(mergedConfig)));
const config = applyDefaults(fromJS(normalizedConfig));

dispatch(configLoaded(config));

Expand Down

0 comments on commit 3924cfb

Please sign in to comment.