From c52e42c644f6541cefc3c3dcacc4f97b4c1533e4 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 9 Oct 2017 16:51:27 +0700 Subject: [PATCH] Allow changing wordlist and domains using the updateSettings mutation --- client/coral-admin/src/graphql/index.js | 13 ++++++++- .../routes/Configure/containers/Configure.js | 14 ++++++++-- graph/typeDefs.graphql | 17 +++++++++-- services/settings.js | 28 ++++++++++++++++++- 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/client/coral-admin/src/graphql/index.js b/client/coral-admin/src/graphql/index.js index bb83199e5..d7656d9a5 100644 --- a/client/coral-admin/src/graphql/index.js +++ b/client/coral-admin/src/graphql/index.js @@ -1,4 +1,15 @@ import update from 'immutability-helper'; +import mapValues from 'lodash/mapValues'; + +// Map nested object leaves. Array objects are considered leaves. +function mapLeaves(o, mapper) { + return mapValues(o, (val) => { + if (typeof val === 'object' && !Array.isArray(val)) { + return mapLeaves(val, mapper); + } + return mapper(val); + }); +} export default { mutations: { @@ -33,7 +44,7 @@ export default { updateQueries: { TalkAdmin_Configure: (prev) => { const updated = update(prev, { - settings: {$merge: input}, + settings: mapLeaves(input, (leaf) => ({$set: leaf})), }); return updated; } diff --git a/client/coral-admin/src/routes/Configure/containers/Configure.js b/client/coral-admin/src/routes/Configure/containers/Configure.js index 4146c4ffe..8f80a3e24 100644 --- a/client/coral-admin/src/routes/Configure/containers/Configure.js +++ b/client/coral-admin/src/routes/Configure/containers/Configure.js @@ -6,7 +6,7 @@ import withQuery from 'coral-framework/hocs/withQuery'; import {Spinner} from 'coral-ui'; import {notify} from 'coral-framework/actions/notification'; import PropTypes from 'prop-types'; -import merge from 'lodash/merge'; +import assignWith from 'lodash/assignWith'; import {withUpdateSettings} from 'coral-framework/graphql/mutations'; import {getErrorMessages, getDefinitionName} from 'coral-framework/utils'; import StreamSettings from './StreamSettings'; @@ -16,10 +16,20 @@ import {clearPending, setActiveSection} from '../../../actions/configure'; import Configure from '../components/Configure'; +// Like lodash merge but does not recurse into arrays. +const mergeExcludingArrays = (objValue, srcValue) => { + if (typeof srcValue === 'object' && !Array.isArray(srcValue)) { + return assignWith({}, objValue, srcValue, mergeExcludingArrays); + } + return srcValue; +}; + class ConfigureContainer extends Component { // Merge current settings with pending settings. - getMergedSettings = (props = this.props) => merge({}, props.root.settings, props.pending); + getMergedSettings = (props = this.props) => { + return assignWith({}, props.root.settings, props.pending, mergeExcludingArrays); + } // Cached merged settings. mergedSettings = this.getMergedSettings(); diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index ca2a39871..338ce82ea 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -1217,6 +1217,12 @@ input UpdateSettingsInput { # editCommentWindowLength is the length of time (in milliseconds) after a # comment is posted that it can still be edited by the author. editCommentWindowLength: Int + + # wordlist allows chaninging the available wordlists. + wordlist: UpdateWordlistInput + + # domains allows changing the available lists of domains. + domains: UpdateDomainsInput } # UpdateSettingsResponse contains any errors that were rendered as a result @@ -1231,10 +1237,17 @@ type UpdateSettingsResponse implements Response { input UpdateWordlistInput { # banned words will by default reject the comment if it is found. - banned: [String!]! + banned: [String!] # suspect words will simply flag the comment. - suspect: [String!]! + suspect: [String!] +} + +# UpdateDomainsInput describes all the available lists of domains. +input UpdateDomainsInput { + + # whitelist is the list of domains that the embed is allowed to render on. + whitelist: [String!] } # UpdateWordlistResponse contains any errors that were rendered as a result diff --git a/services/settings.js b/services/settings.js index 3033701d8..4b5680b30 100644 --- a/services/settings.js +++ b/services/settings.js @@ -1,6 +1,32 @@ const SettingModel = require('../models/setting'); const errors = require('../errors'); +function dotizeRecurse(result, object, path = '') { + for (const key in object) { + const newPath = path ? `${path}.${key}` : key; + if (typeof object[key] === 'object' && !Array.isArray(object[key])) { + dotizeRecurse(result, object[key], newPath); + return; + } + result[newPath] = object[key]; + } +} + +/** + * Dotize turns a nested object into flattened object with + * dotized key notation. Arrays do not become dotized. + * + * e.g. {a: {b: 'c'}} becomes {'a.b': 'c} + * + * @param {Object} object + * @return {Object} dotized object + */ +function dotize(object) { + const result = {}; + dotizeRecurse(result, object); + return result; +} + /** * The selector used to uniquely identify the settings document. */ @@ -34,7 +60,7 @@ module.exports = class SettingsService { */ static update(settings) { return SettingModel.findOneAndUpdate(selector, { - $set: settings + $set: dotize(settings) }, { upsert: true, new: true,