<commit_before>'use strict'

const _ = require('lodash')

const { one, many } = require('easy-postgres/result')
const sql = require('easy-postgres/sql-helpers')

const factories = {
  count (db, table) {
    return function count (json, client = db) {
      let query = `SELECT count(id) FROM ${table}`

      let values

      if (Object.keys(json).length) {
        query += ` WHERE ${sql.where(json)}`
        values = _.values(_.omitBy(json, _.isNil))
      }

      query += ';'

      return client.query(query, values).then((result) => parseInt(result.rows[0].count, 10))
    }
  },

  create (db, table, columns, emitter) {
    const columnsString = sql.columns(columns)

    return function create (json, client = db) {
      json = _.pickBy(json, (value, key) => !_.isUndefined(value) && columns.includes(key))

      const keys = _.keys(json).map((key) => `"${_.snakeCase(key)}"`)
      const values = _.values(json)

      const query = `INSERT INTO ${table} (${keys.join(', ')}) VALUES (${values.map((v, i) => `$${i + 1}`).join(', ')}) RETURNING ${columnsString};`

      return client.query(query, values).then((result) => {
        const item = result.rows[0]

        if (emitter) emitter.emit('db', { table, action: 'create', item })

        return item
      })
    }
  },

  createMany (db, table, columns, emitter) {
    const columnsString = sql.columns(columns)

    return function create (collection, client = db) {
      if (!Array.isArray(collection)) {
        collection = [collection]
      }

      let keys = []

      const values = collection.map((item) => {
        const arr = []

        for (const key in item) {
          if (columns.includes(key)) {
            let index = keys.indexOf(key)

            if (index < 0) {
              keys.push(key)
              index = keys.length - 1
            }

            arr[index] = item[key] === null ? 'NULL' : item[key]
          }
        }

        return arr
      }).map((arr) => arr.map((value) => value !== undefined ? value : 'DEFAULT'))

      keys = keys.map((key) => `"${_.snakeCase(key)}"`)

      const str = values.map((arr) => arr.join(', ')).join('), (')

      const query = `INSERT INTO ${table} (${keys.join(', ')}) VALUES (${str}) RETURNING ${columnsString};`

      return client.query(query, values).then((result) => {
        if (emitter) emitter.emit('db', { table, action: 'create', item: result.rows })

        return result.rows
      })
    }
  },

  // use req.query to query database.
  // should probably be used with `midwest/middleware/format-query` and/or
  // `midwest/middleware/paginate`
  find (db, table, columns, emitter, defaults = {}) {
    const columnsString = sql.columns(columns)

    return function find (json, client = db) {
      const {
        offset = defaults.offset,
        limit = defaults.limit,
        sort = defaults.sort
      } = json

      json = _.omit(json, 'limit', 'sort', 'offset')

      let q = `SELECT ${columnsString} FROM ${table}`

      if (Object.keys(json).length) {
        q += ` WHERE ${sql.where(json)}`
      }

      if (sort) {
        q += ` ORDER BY ${sort}`
      } else {
        q += ' ORDER BY id DESC'
      }

      if (limit) {
        q += ` LIMIT ${limit} OFFSET ${offset}`
      }

      q += ';'

      const values = _.values(_.omitBy(json, _.isNil))

      return client.query(q, values).then(many)
    }
  },

  findById (db, table, columns) {
    columns = sql.columns(columns)

    return function findById (id, client = db) {
      // TODO do we need this here or only in middleware?
      // if (id === 'new') return cb();

      const query = `SELECT ${columns} FROM ${table} WHERE id = $1;`

      return client.query(query, [id]).then(one)
    }
  },

  findOne (db, table, columns) {
    columns = sql.columns(columns)

    return function findOne (json, client = db) {
      let query = `SELECT ${columns} FROM ${table}`

      let values

      if (!_.isEmpty(json)) {
        query += ` WHERE ${sql.where(json)}`
        values = _.values(_.omitBy(json, _.isNil))
      }

      query += ' LIMIT 1;'

      return client.query(query, values).then(one)
    }
  },

  getAll (db, table, columns) {
    columns = sql.columns(columns)

    const query = `SELECT ${columns} FROM ${table} ORDER BY id DESC;`

    return function getAll (client = db) {
      return client.query(query).then(many)
    }
  },

  remove (db, table, columns, emitter) {
    const query = `DELETE FROM ${table} WHERE id = $1;`

    return function remove (id, client = db) {
      return client.query(query, [id]).then((result) => {
        if (emitter) emitter.emit('db', { table, action: 'delete', item: result.rows[0] })

        return result.rowCount
      })
    }
  },

  // completely replaces the doc
  // SHOULD be used with PUT
  replace (db, table, columns, emitter) {
        // _.difference(columns, _.keys(json)).forEach((key) => {
        //   json[key] = null;
        // });
    return this.update(db, table, columns, emitter)
  },

  update (db, table, columns, emitter) {
    const columnsString = sql.columns(columns)

    // changes properties passed on req.body
    // SHOULD be used with PATCH
    return function update (id, json, client = db) {
      // enable using using _hid (not that _id MUST be a ObjectId)

      json = _.pickBy(json, (value, key) => columns.includes(key))

      const keys = _.keys(json).map((key) => `"${_.snakeCase(key)}"`)
      const values = _.values(json)

      const query = `UPDATE ${table} SET ${keys.map((key, i) => `${key}=$${i + 1}`).join(', ')} WHERE id = $${keys.length + 1} RETURNING ${columnsString};`

      return client.query(query, [...values, id]).then((result) => {
        if (emitter) emitter.emit('db', {table, action: 'update', item: result.rows[0]})

        return result.rows[0]
      })
    }
  }
}

const all = Object.keys(factories)

module.exports = ({ defaults, db, emitter, table, columns, exclude, include }) => {
  // if (db instanceof pg.Pool || _.isPlainObject(db)) db = dbFactory(db);

  include = include || _.difference(all, exclude)

  return include.reduce((result, value) => {
    if (factories[value]) {
      result[value] = factories[value](db, table, columns, emitter, defaults && Object.assign({}, defaults.all, defaults[value]))
    }

    return result
  }, {})
}

Object.assign(module.exports, factories)
<commit_msg>Indentation<commit_after>'use strict'

const _ = require('lodash')

const { one, many } = require('easy-postgres/result')
const sql = require('easy-postgres/sql-helpers')

const factories = {
  count (db, table) {
    return function count (json, client = db) {
      let query = `SELECT count(id) FROM ${table}`

      let values

      if (Object.keys(json).length) {
        query += ` WHERE ${sql.where(json)}`
        values = _.values(_.omitBy(json, _.isNil))
      }

      query += ';'

      return client.query(query, values).then((result) => parseInt(result.rows[0].count, 10))
    }
  },

  create (db, table, columns, emitter) {
    const columnsString = sql.columns(columns)

    return function create (json, client = db) {
      json = _.pickBy(json, (value, key) => !_.isUndefined(value) && columns.includes(key))

      const keys = _.keys(json).map((key) => `"${_.snakeCase(key)}"`)
      const values = _.values(json)

      const query = `INSERT INTO ${table} (${keys.join(', ')}) VALUES (${values.map((v, i) => `$${i + 1}`).join(', ')}) RETURNING ${columnsString};`

      return client.query(query, values).then((result) => {
        const item = result.rows[0]

        if (emitter) emitter.emit('db', { table, action: 'create', item })

        return item
      })
    }
  },

  createMany (db, table, columns, emitter) {
    const columnsString = sql.columns(columns)

    return function create (collection, client = db) {
      if (!Array.isArray(collection)) {
        collection = [collection]
      }

      let keys = []

      const values = collection.map((item) => {
        const arr = []

        for (const key in item) {
          if (columns.includes(key)) {
            let index = keys.indexOf(key)

            if (index < 0) {
              keys.push(key)
              index = keys.length - 1
            }

            arr[index] = item[key] === null ? 'NULL' : item[key]
          }
        }

        return arr
      }).map((arr) => arr.map((value) => value !== undefined ? value : 'DEFAULT'))

      keys = keys.map((key) => `"${_.snakeCase(key)}"`)

      const str = values.map((arr) => arr.join(', ')).join('), (')

      const query = `INSERT INTO ${table} (${keys.join(', ')}) VALUES (${str}) RETURNING ${columnsString};`

      return client.query(query, values).then((result) => {
        if (emitter) emitter.emit('db', { table, action: 'create', item: result.rows })

        return result.rows
      })
    }
  },

  // use req.query to query database.
  // should probably be used with `midwest/middleware/format-query` and/or
  // `midwest/middleware/paginate`
  find (db, table, columns, emitter, defaults = {}) {
    const columnsString = sql.columns(columns)

    return function find (json, client = db) {
      const {
        offset = defaults.offset,
        limit = defaults.limit,
        sort = defaults.sort
      } = json

      json = _.omit(json, 'limit', 'sort', 'offset')

      let q = `SELECT ${columnsString} FROM ${table}`

      if (Object.keys(json).length) {
        q += ` WHERE ${sql.where(json)}`
      }

      if (sort) {
        q += ` ORDER BY ${sort}`
      } else {
        q += ' ORDER BY id DESC'
      }

      if (limit) {
        q += ` LIMIT ${limit} OFFSET ${offset}`
      }

      q += ';'

      const values = _.values(_.omitBy(json, _.isNil))

      return client.query(q, values).then(many)
    }
  },

  findById (db, table, columns) {
    columns = sql.columns(columns)

    return function findById (id, client = db) {
      // TODO do we need this here or only in middleware?
      // if (id === 'new') return cb();

      const query = `SELECT ${columns} FROM ${table} WHERE id = $1;`

      return client.query(query, [id]).then(one)
    }
  },

  findOne (db, table, columns) {
    columns = sql.columns(columns)

    return function findOne (json, client = db) {
      let query = `SELECT ${columns} FROM ${table}`

      let values

      if (!_.isEmpty(json)) {
        query += ` WHERE ${sql.where(json)}`
        values = _.values(_.omitBy(json, _.isNil))
      }

      query += ' LIMIT 1;'

      return client.query(query, values).then(one)
    }
  },

  getAll (db, table, columns) {
    columns = sql.columns(columns)

    const query = `SELECT ${columns} FROM ${table} ORDER BY id DESC;`

    return function getAll (client = db) {
      return client.query(query).then(many)
    }
  },

  remove (db, table, columns, emitter) {
    const query = `DELETE FROM ${table} WHERE id = $1;`

    return function remove (id, client = db) {
      return client.query(query, [id]).then((result) => {
        if (emitter) emitter.emit('db', { table, action: 'delete', item: result.rows[0] })

        return result.rowCount
      })
    }
  },

  // completely replaces the doc
  // SHOULD be used with PUT
  replace (db, table, columns, emitter) {
    // _.difference(columns, _.keys(json)).forEach((key) => {
    //   json[key] = null;
    // });
    return this.update(db, table, columns, emitter)
  },

  update (db, table, columns, emitter) {
    const columnsString = sql.columns(columns)

    // changes properties passed on req.body
    // SHOULD be used with PATCH
    return function update (id, json, client = db) {
      // enable using using _hid (not that _id MUST be a ObjectId)

      json = _.pickBy(json, (value, key) => columns.includes(key))

      const keys = _.keys(json).map((key) => `"${_.snakeCase(key)}"`)
      const values = _.values(json)

      const query = `UPDATE ${table} SET ${keys.map((key, i) => `${key}=$${i + 1}`).join(', ')} WHERE id = $${keys.length + 1} RETURNING ${columnsString};`

      return client.query(query, [...values, id]).then((result) => {
        if (emitter) emitter.emit('db', {table, action: 'update', item: result.rows[0]})

        return result.rows[0]
      })
    }
  }
}

const all = Object.keys(factories)

module.exports = ({ defaults, db, emitter, table, columns, exclude, include }) => {
  // if (db instanceof pg.Pool || _.isPlainObject(db)) db = dbFactory(db);

  include = include || _.difference(all, exclude)

  return include.reduce((result, value) => {
    if (factories[value]) {
      result[value] = factories[value](db, table, columns, emitter, defaults && Object.assign({}, defaults.all, defaults[value]))
    }

    return result
  }, {})
}

Object.assign(module.exports, factories)
<|endoftext|>


<commit_before>module.exports = {};
const api = require('./api');
const emailValidator = require('email-validator');

module.exports.validateUsernameLocally = username => {
    if (!username || username === '') {
        return {valid: false, errMsgId: 'general.required'};
    } else if (username.length < 3) {
        return {valid: false, errMsgId: 'registration.validationUsernameMinLength'};
    } else if (username.length > 20) {
        return {valid: false, errMsgId: 'registration.validationUsernameMaxLength'};
    } else if (/\s/i.test(username)) {
        return {valid: false, errMsgId: 'registration.validationUsernameSpaces'};
    } else if (!/^[\w-]+$/i.test(username)) {
        return {valid: false, errMsgId: 'registration.validationUsernameRegexp'};
    }
    return {valid: true};
};

module.exports.validateUsernameRemotely = username => (
    new Promise(resolve => {
        api({
            host: '', // not handled by API; use existing infrastructure
            uri: `/accounts/check_username/${username}/`
        }, (err, body, res) => {
            if (err || res.statusCode !== 200) {
                resolve({requestSucceeded: false, valid: false, errMsgId: 'general.error'});
            }
            let msg = '';
            if (body && body[0]) {
                msg = body[0].msg;
            }
            switch (msg) {
            case 'valid username':
                resolve({requestSucceeded: true, valid: true});
                break;
            case 'username exists':
                resolve({requestSucceeded: true, valid: false, errMsgId: 'registration.validationUsernameExists'});
                break;
            case 'bad username': // i.e., vulgar
                resolve({requestSucceeded: true, valid: false, errMsgId: 'registration.validationUsernameNotAllowed'});
                break;
            case 'invalid username':
            default:
                resolve({requestSucceeded: true, valid: false, errMsgId: 'registration.validationUsernameNotAllowed'});
            }
        });
    })
);

/**
 * Validate password value, optionally also considering username value
 * @param  {string} password     password value to validate
 * @param  {string} username     username value to compare
 * @return {object}              {valid: boolean, errMsgId: string}
 */
module.exports.validatePassword = (password, username) => {
    if (!password) {
        return {valid: false, errMsgId: 'general.required'};
    // Using Array.from(string).length, instead of string.length, improves unicode
    // character counting for a subset of unicode characters, so that they are counted
    // as single characters by js.
    // However, this only helps with a subset of unicode. Characters combinations,
    // including diacritical marks or skintone/gender variations, will still appear
    // to be multiple characters. See discussions:
    // https://blog.jonnew.com/posts/poo-dot-length-equals-two
    // https://stackoverflow.com/a/54370584/2308190
    } else if (Array.from(password).length < 6) {
        return {valid: false, errMsgId: 'registration.validationPasswordLength'};
    } else if (password === 'password') {
        return {valid: false, errMsgId: 'registration.validationPasswordNotEquals'};
    } else if (username && password === username) {
        return {valid: false, errMsgId: 'registration.validationPasswordNotUsername'};
    }
    return {valid: true};
};

module.exports.validatePasswordConfirm = (password, passwordConfirm) => {
    if (!passwordConfirm) {
        return {valid: false, errMsgId: 'general.required'};
    } else if (password !== passwordConfirm) {
        return {valid: false, errMsgId: 'registration.validationPasswordConfirmNotEquals'};
    }
    return {valid: true};
};

module.exports.validateEmailLocally = email => {
    if (!email || email === '') {
        return {valid: false, errMsgId: 'general.required'};
    } else if (emailValidator.validate(email)) {
        return {valid: true};
    }
    return ({valid: false, errMsgId: 'registration.validationEmailInvalid'});
};

module.exports.validateEmailRemotely = email => (
    new Promise(resolve => {
        api({
            host: '', // not handled by API; use existing infrastructure
            params: {email: email},
            uri: '/accounts/check_email/'
        }, (err, body, res) => {
            if (err || res.statusCode !== 200 || !body || body.length < 1 || !body[0].msg) {
                resolve({requestSucceeded: false, valid: false, errMsgId: 'general.apiError'});
            }
            switch (body[0].msg) {
            case 'valid email':
                resolve({requestSucceeded: true, valid: true});
                break;
            case 'Scratch is not allowed to send email to this address.': // e.g., bad TLD or block-listed
            case 'Enter a valid email address.':
            default:
                resolve({requestSucceeded: true, valid: false, errMsgId: 'registration.validationEmailInvalid'});
                break;
            }
        });
    })
);

const responseErrorMsgs = module.exports.responseErrorMsgs = {
    username: {
        'username exists': {errMsgId: 'registration.errorUsernameExists'},
        'bad username': {errMsgId: 'registration.errorBadUsername'}
    },
    password: {
        'Ensure this value has at least 6 characters \\(it has \\d\\).': {
            errMsgId: 'registration.errorPasswordTooShort'
        }
    },
    recaptcha: {
        'Incorrect, please try again.': {errMsgId: 'registration.errorCaptcha'}
    }
};

module.exports.responseErrorMsg = (fieldName, serverRawErr) => {
    if (fieldName && responseErrorMsgs[fieldName]) {
        const serverErrPatterns = responseErrorMsgs[fieldName];
        // use regex compare to find matching error string in responseErrorMsgs
        const matchingKey = Object.keys(serverErrPatterns).find(errPattern => (
            RegExp(errPattern).test(serverRawErr)
        ));
        if (matchingKey) return responseErrorMsgs[fieldName][matchingKey].errMsgId;
    }
    return null;
};
<commit_msg>use api instead of scratchr2 to validate usernames<commit_after>module.exports = {};
const api = require('./api');
const emailValidator = require('email-validator');

module.exports.validateUsernameLocally = username => {
    if (!username || username === '') {
        return {valid: false, errMsgId: 'general.required'};
    } else if (username.length < 3) {
        return {valid: false, errMsgId: 'registration.validationUsernameMinLength'};
    } else if (username.length > 20) {
        return {valid: false, errMsgId: 'registration.validationUsernameMaxLength'};
    } else if (/\s/i.test(username)) {
        return {valid: false, errMsgId: 'registration.validationUsernameSpaces'};
    } else if (!/^[\w-]+$/i.test(username)) {
        return {valid: false, errMsgId: 'registration.validationUsernameRegexp'};
    }
    return {valid: true};
};

module.exports.validateUsernameRemotely = username => (
    new Promise(resolve => {
        api({
            uri: `/accounts/checkusername/${username}/`
        }, (err, body, res) => {
            if (err || res.statusCode !== 200) {
                resolve({requestSucceeded: false, valid: false, errMsgId: 'general.error'});
            }
            let msg = '';
            if (body && body.msg) msg = body.msg;
            else if (body && body[0]) msg = body[0].msg;
            switch (msg) {
            case 'valid username':
                resolve({requestSucceeded: true, valid: true});
                break;
            case 'username exists':
                resolve({requestSucceeded: true, valid: false, errMsgId: 'registration.validationUsernameExists'});
                break;
            case 'bad username': // i.e., vulgar
                resolve({requestSucceeded: true, valid: false, errMsgId: 'registration.validationUsernameNotAllowed'});
                break;
            case 'invalid username':
            default:
                resolve({requestSucceeded: true, valid: false, errMsgId: 'registration.validationUsernameNotAllowed'});
            }
        });
    })
);

/**
 * Validate password value, optionally also considering username value
 * @param  {string} password     password value to validate
 * @param  {string} username     username value to compare
 * @return {object}              {valid: boolean, errMsgId: string}
 */
module.exports.validatePassword = (password, username) => {
    if (!password) {
        return {valid: false, errMsgId: 'general.required'};
    // Using Array.from(string).length, instead of string.length, improves unicode
    // character counting for a subset of unicode characters, so that they are counted
    // as single characters by js.
    // However, this only helps with a subset of unicode. Characters combinations,
    // including diacritical marks or skintone/gender variations, will still appear
    // to be multiple characters. See discussions:
    // https://blog.jonnew.com/posts/poo-dot-length-equals-two
    // https://stackoverflow.com/a/54370584/2308190
    } else if (Array.from(password).length < 6) {
        return {valid: false, errMsgId: 'registration.validationPasswordLength'};
    } else if (password === 'password') {
        return {valid: false, errMsgId: 'registration.validationPasswordNotEquals'};
    } else if (username && password === username) {
        return {valid: false, errMsgId: 'registration.validationPasswordNotUsername'};
    }
    return {valid: true};
};

module.exports.validatePasswordConfirm = (password, passwordConfirm) => {
    if (!passwordConfirm) {
        return {valid: false, errMsgId: 'general.required'};
    } else if (password !== passwordConfirm) {
        return {valid: false, errMsgId: 'registration.validationPasswordConfirmNotEquals'};
    }
    return {valid: true};
};

module.exports.validateEmailLocally = email => {
    if (!email || email === '') {
        return {valid: false, errMsgId: 'general.required'};
    } else if (emailValidator.validate(email)) {
        return {valid: true};
    }
    return ({valid: false, errMsgId: 'registration.validationEmailInvalid'});
};

module.exports.validateEmailRemotely = email => (
    new Promise(resolve => {
        api({
            host: '', // not handled by API; use existing infrastructure
            params: {email: email},
            uri: '/accounts/check_email/'
        }, (err, body, res) => {
            if (err || res.statusCode !== 200 || !body || body.length < 1 || !body[0].msg) {
                resolve({requestSucceeded: false, valid: false, errMsgId: 'general.apiError'});
            }
            switch (body[0].msg) {
            case 'valid email':
                resolve({requestSucceeded: true, valid: true});
                break;
            case 'Scratch is not allowed to send email to this address.': // e.g., bad TLD or block-listed
            case 'Enter a valid email address.':
            default:
                resolve({requestSucceeded: true, valid: false, errMsgId: 'registration.validationEmailInvalid'});
                break;
            }
        });
    })
);

const responseErrorMsgs = module.exports.responseErrorMsgs = {
    username: {
        'username exists': {errMsgId: 'registration.errorUsernameExists'},
        'bad username': {errMsgId: 'registration.errorBadUsername'}
    },
    password: {
        'Ensure this value has at least 6 characters \\(it has \\d\\).': {
            errMsgId: 'registration.errorPasswordTooShort'
        }
    },
    recaptcha: {
        'Incorrect, please try again.': {errMsgId: 'registration.errorCaptcha'}
    }
};

module.exports.responseErrorMsg = (fieldName, serverRawErr) => {
    if (fieldName && responseErrorMsgs[fieldName]) {
        const serverErrPatterns = responseErrorMsgs[fieldName];
        // use regex compare to find matching error string in responseErrorMsgs
        const matchingKey = Object.keys(serverErrPatterns).find(errPattern => (
            RegExp(errPattern).test(serverRawErr)
        ));
        if (matchingKey) return responseErrorMsgs[fieldName][matchingKey].errMsgId;
    }
    return null;
};
<|endoftext|>


<commit_before>import React from 'react'
import {
  func,
  object,
  string
} from 'prop-types'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'

import {
  ___saveEndpoint,
  ___deleteEndpoint,
  ___saveOAuthServer,
  ___deleteOAuthServer,
  ___closeConfirmation
} from '../../../store/actions'

import Modal from '../../../components/Modal/Modal'
import Button from '../../../components/Button/Button'

const propTypes = {
  closeModal: func.isRequired,
  objectType: string,
  objectEntity: object
}

const defaultProps = {
  isOpen: false,
  objectEntity: {}
}

const ActionsMap = {
  save: {
    'endpoint': {
      title: 'Save Endpoint title',
      message: 'Habra-habra',
      onConfirm: ___saveEndpoint({
        isEditing: false
      })
    },
    'OAuthServer': {
      title: 'Save OAuthServer title',
      message: 'Habra-habra',
      onConfirm: ___saveOAuthServer({
        isEditing: false
      })
    }
  },
  update: {
    'endpoint': {
      title: 'Update Endpoint title',
      message: 'Habra-habra',
      onConfirm: ___saveEndpoint({
        isEditing: true
      })
    },
    'OAuthServer': {
      title: 'Update OAuthServer title',
      message: 'Habra-habra',
      onConfirm: ___saveOAuthServer({
        isEditing: true
      })
    }
  },
  delete: {
    'endpoint': {
      title: 'Delete endpoint title',
      message: 'Habra-habra',
      onConfirm: ___deleteEndpoint
    },
    'OAuthServer': {
      title: 'Delete endpoint title',
      message: 'Habra-habra',
      onConfirm: ___deleteOAuthServer
    }
  }
}

const ConfirmationModal = ({
  dispatch,
  isOpen,
  actionType,
  closeModal,
  objectEntity,
  objectType,
  configurationMetadata,
  backup
}) => {
  // eslint-disable-next-line no-mixed-operators
  const getValue = target => isOpen && ActionsMap[actionType][objectType][target] || ''
  const onConfirm = getValue('onConfirm')
  const handleCloseModal = () => {
    // if (actionType === 'conflict') {
    //   const metadata = configurationMetadata.next.metadata

    //   // dispatch(discardConfigurationToExactState(metadata, backup))
    // }

    closeModal()
  }

  return (
    <Modal
      show={isOpen}
      closeModal={handleCloseModal}
      message={
        React.isValidElement(getValue('message'))
          ? React.cloneElement(getValue('message'), { objectEntity })
          : getValue('message')
      }
      title={getValue('title')}
      buttons={[
        <Button key='cancel' mod='default' onClick={handleCloseModal}>Cancel</Button>,
        <Button
          key='ok'
          mod='primary'
          onClick={
            () => dispatch(onConfirm(objectEntity))
          }
        >
                    OK
        </Button>
      ]}
    />
  )
}

ConfirmationModal.propTypes = propTypes
ConfirmationModal.defaultProps = defaultProps

const mapStateToProps = state => {
  const {
    actionType,
    isOpen,
    objectEntity,
    objectType
  } = state.confirmationReducer

  return {
    actionType,
    objectEntity,
    objectType,
    isOpen
    // configurationMetadata: state.configurationReducer.configurationDifference,
    // backup: state.configurationReducer.backup
  }
}

const mapDispatchToProps = dispatch => {
  return {
    dispatch,
    closeModal: bindActionCreators(___closeConfirmation, dispatch)
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(ConfirmationModal)

/*
const ____ActionsMap = {
    save: {
      configuration: {
        title: 'Save configuration',
        message: 'Are you sure you want to save configuration?',
        onConfirm: saveConfiguration({
          isEditing: false
        })
      },
      project: {
        title: 'Save project',
        message: 'Are you sure you want to save project?',
        onConfirm: saveProject
      },
      locale: {
        title: 'Save locale',
        message: 'Are you sure you want to save locale?',
        onConfirm: saveLocale({
          isEditing: false
        })
      }
    },
    update: {
      configuration: {
        title: 'Save configuration',
        message: 'Are you sure you want to save configuration?',
        onConfirm: saveConfiguration({
          isEditing: true
        })
      },
      locale: {
        title: 'Save locale',
        message: 'Are you sure you want to save locale?',
        onConfirm: saveLocale({
          isEditing: true
        })
      }
    },
    delete: {
      configuration: {
        title: 'Delete configuration',
        message: <DeleteConfigurationModalContent />,
        onConfirm: deleteConfiguration
      },
      project: {
        title: 'Delete project',
        message: <DeleteProjectModalContent />,
        onConfirm: deleteProject
      },
      locale: {
        title: 'Delete country-locale',
        message: <DeleteLocaleModalContent />,
        onConfirm: deleteLocale
      }
    },
    preview: {
      auditlog: {
        title: 'Audit log details',
        message: <PreviewModalContent />,
        onConfirm: closeConfirmation
      }
    },
    rollback: {
      auditlog: {
        title: 'Rollback audit log',
        message: <RollbackModalContent />,
        onConfirm: rollbackAuditLog
      }
    },
    error: {
      error: {
        title: 'Error',
        message: <ErrorContent />,
        onConfirm: closeConfirmation
      }
    },
    conflict: {
      configuration: {
        title: 'Conflict detected',
        message: <ConflictsPreviewContainer />,
        onConfirm: closeConfirmation
      }
    }
  }
  */
<commit_msg>Adjust confirmation messages<commit_after>import React from 'react'
import {
  func,
  object,
  string
} from 'prop-types'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'

import {
  ___saveEndpoint,
  ___deleteEndpoint,
  ___saveOAuthServer,
  ___deleteOAuthServer,
  ___closeConfirmation
} from '../../../store/actions'

import Modal from '../../../components/Modal/Modal'
import Button from '../../../components/Button/Button'

const propTypes = {
  closeModal: func.isRequired,
  objectType: string,
  objectEntity: object
}

const defaultProps = {
  isOpen: false,
  objectEntity: {}
}

const ActionsMap = {
  save: {
    'endpoint': {
      title: 'Save endpoint',
      message: 'Are you sure you want to save endpoint?',
      onConfirm: ___saveEndpoint({
        isEditing: false
      })
    },
    'OAuthServer': {
      title: 'Save OAuthServer',
      message: 'Are you sure you want to save OAuth server?',
      onConfirm: ___saveOAuthServer({
        isEditing: false
      })
    }
  },
  update: {
    'endpoint': {
      title: 'Update endpoint',
      message: 'Are you sure you want to update endpoint?',
      onConfirm: ___saveEndpoint({
        isEditing: true
      })
    },
    'OAuthServer': {
      title: 'Update OAuthServer title',
      message: 'Are you sure you want to update OAuth server?',
      onConfirm: ___saveOAuthServer({
        isEditing: true
      })
    }
  },
  delete: {
    'endpoint': {
      title: 'Delete endpoint',
      message: 'Are you sure you want to delete the endpoint?',
      onConfirm: ___deleteEndpoint
    },
    'OAuthServer': {
      title: 'Delete endpoint title',
      message: 'Are you sure you want to delete the OAuth server?',
      onConfirm: ___deleteOAuthServer
    }
  }
}

const ConfirmationModal = ({
  dispatch,
  isOpen,
  actionType,
  closeModal,
  objectEntity,
  objectType,
  configurationMetadata,
  backup
}) => {
  // eslint-disable-next-line no-mixed-operators
  const getValue = target => isOpen && ActionsMap[actionType][objectType][target] || ''
  const onConfirm = getValue('onConfirm')
  const handleCloseModal = () => {
    // if (actionType === 'conflict') {
    //   const metadata = configurationMetadata.next.metadata

    //   // dispatch(discardConfigurationToExactState(metadata, backup))
    // }

    closeModal()
  }

  return (
    <Modal
      show={isOpen}
      closeModal={handleCloseModal}
      message={
        React.isValidElement(getValue('message'))
          ? React.cloneElement(getValue('message'), { objectEntity })
          : getValue('message')
      }
      title={getValue('title')}
      buttons={[
        <Button key='cancel' mod='default' onClick={handleCloseModal}>Cancel</Button>,
        <Button
          key='ok'
          mod='primary'
          onClick={
            () => dispatch(onConfirm(objectEntity))
          }
        >
                    OK
        </Button>
      ]}
    />
  )
}

ConfirmationModal.propTypes = propTypes
ConfirmationModal.defaultProps = defaultProps

const mapStateToProps = state => {
  const {
    actionType,
    isOpen,
    objectEntity,
    objectType
  } = state.confirmationReducer

  return {
    actionType,
    objectEntity,
    objectType,
    isOpen
    // configurationMetadata: state.configurationReducer.configurationDifference,
    // backup: state.configurationReducer.backup
  }
}

const mapDispatchToProps = dispatch => {
  return {
    dispatch,
    closeModal: bindActionCreators(___closeConfirmation, dispatch)
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(ConfirmationModal)

/*
const ____ActionsMap = {
    save: {
      configuration: {
        title: 'Save configuration',
        message: 'Are you sure you want to save configuration?',
        onConfirm: saveConfiguration({
          isEditing: false
        })
      },
      project: {
        title: 'Save project',
        message: 'Are you sure you want to save project?',
        onConfirm: saveProject
      },
      locale: {
        title: 'Save locale',
        message: 'Are you sure you want to save locale?',
        onConfirm: saveLocale({
          isEditing: false
        })
      }
    },
    update: {
      configuration: {
        title: 'Save configuration',
        message: 'Are you sure you want to save configuration?',
        onConfirm: saveConfiguration({
          isEditing: true
        })
      },
      locale: {
        title: 'Save locale',
        message: 'Are you sure you want to save locale?',
        onConfirm: saveLocale({
          isEditing: true
        })
      }
    },
    delete: {
      configuration: {
        title: 'Delete configuration',
        message: <DeleteConfigurationModalContent />,
        onConfirm: deleteConfiguration
      },
      project: {
        title: 'Delete project',
        message: <DeleteProjectModalContent />,
        onConfirm: deleteProject
      },
      locale: {
        title: 'Delete country-locale',
        message: <DeleteLocaleModalContent />,
        onConfirm: deleteLocale
      }
    },
    preview: {
      auditlog: {
        title: 'Audit log details',
        message: <PreviewModalContent />,
        onConfirm: closeConfirmation
      }
    },
    rollback: {
      auditlog: {
        title: 'Rollback audit log',
        message: <RollbackModalContent />,
        onConfirm: rollbackAuditLog
      }
    },
    error: {
      error: {
        title: 'Error',
        message: <ErrorContent />,
        onConfirm: closeConfirmation
      }
    },
    conflict: {
      configuration: {
        title: 'Conflict detected',
        message: <ConflictsPreviewContainer />,
        onConfirm: closeConfirmation
      }
    }
  }
  */
<|endoftext|>


<commit_before>var equal = require('assert').equal
var hash = require('..')

equal(hash('abc'),  'a9993e364706816aba3e25717850c26c9cd0d89d')
equal(hash('abce'), '0a431a7631cabf6b11b984a943127b5e0aa9d687')
equal(hash({}),     'bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f')
equal(hash([]),     '97d170e1550eee4afc0af065b78cda302a97674c')
equal(hash(new Buffer('abc')),
                    'a9993e364706816aba3e25717850c26c9cd0d89d')
equal(hash('ab\xff'),'ba5142a8207bd61baddf325088732e71cbfe8eb6')
equal(hash(new Buffer('ab\xff').toString()),
                    'ba5142a8207bd61baddf325088732e71cbfe8eb6')

<commit_msg>failing tests for non-deterministic stringify<commit_after>var equal = require('assert').equal
var hash = require('..')

equal(hash('abc'),  'a9993e364706816aba3e25717850c26c9cd0d89d')
equal(hash('abce'), '0a431a7631cabf6b11b984a943127b5e0aa9d687')
equal(hash({}),     'bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f')
equal(hash([]),     '97d170e1550eee4afc0af065b78cda302a97674c')
equal(hash(new Buffer('abc')),
                    'a9993e364706816aba3e25717850c26c9cd0d89d')
equal(hash('ab\xff'),'ba5142a8207bd61baddf325088732e71cbfe8eb6')
equal(hash(new Buffer('ab\xff').toString()),
                    'ba5142a8207bd61baddf325088732e71cbfe8eb6')
equal(hash({a:1,b:2,c:3}), hash({c:3,b:2,a:1}))
equal(hash({a:1,b:2,c:3}), hash({c:3,b:2,a:1}))
equal(hash({a:1,b:[2,3],c:4}), hash({c:4,b:[2,3],a:1}))
equal(hash({a:1,b:[2,{c:3,d:4}],e:5}), hash({e:5,b:[2,{d:4,c:3}],a:1}))

<|endoftext|>


<commit_before>var lat = null;
var lng = null;
var locationKey = 'location-Permission';

function searchInit() {
	$('#query').focus();
	$('#search').click(checkUserPermission);
	$(window).keyup(function(event) {
		if (event.keyCode == 13) {
			checkUserPermission();
		}
	});
}

function checkUserPermission () {
	retrieveUserPermission(locationKey) == null ? getLocationPermission(): getLocation()
}

function search() {
	var trimmed =  $.trim( $('#query').val());
	if (trimmed) {
		window.location.href = '/results?q=' + encodeURIComponent(trimmed) + (lat && lng ? '&lat=' + lat + '&lng=' + lng : '');
	}
}

function getLocationPermission() {
	$('.ui.modal').modal('show');
}

function getLocation() {
	if(retrieveUserPermission(locationKey)=='Yes'){
		if (navigator.geolocation) {
			navigator.geolocation.getCurrentPosition(locationSuccess,locationError);
		} else {
			console.log("Geolocation is not supported by this browser");
			saveUserResponse(locationKey, 'Not Supported');
			search();
		}
	}else {
		search();
	}
}

function locationSuccess(location) {
	lat = location.coords.latitude;
	lng = location.coords.longitude;
	search();
}

function locationError() {
	search();
}

function saveUserResponse(key, resp) {
	window.localStorage.setItem(key, resp);
}

function retrieveUserPermission(key) {
	return window.localStorage.getItem(key);
}<commit_msg>Added loading icon while the results page loads.<commit_after>var lat = null;
var lng = null;
var locationKey = 'location-Permission';

function searchInit() {
	$('#query').focus();
	$('#search').click(checkUserPermission);
	$(window).keyup(function(event) {
		if (event.keyCode == 13) {
			checkUserPermission();
		}
	});
}

function checkUserPermission () {
	retrieveUserPermission(locationKey) == null ? getLocationPermission(): getLocation()
}

function search() {
	var trimmed =  $.trim( $('#query').val());
	if (trimmed) {
		$('body').append('

Searching...

')
		window.location.href = '/results?q=' + encodeURIComponent(trimmed) + (lat && lng ? '&lat=' + lat + '&lng=' + lng : '');
	}
}

function getLocationPermission() {
	$('.ui.modal').modal('show');
}

function getLocation() {
	if(retrieveUserPermission(locationKey)=='Yes'){
		if (navigator.geolocation) {
			navigator.geolocation.getCurrentPosition(locationSuccess,locationError);
		} else {
			console.log("Geolocation is not supported by this browser");
			saveUserResponse(locationKey, 'Not Supported');
			search();
		}
	}else {
		search();
	}
}

function locationSuccess(location) {
	lat = location.coords.latitude;
	lng = location.coords.longitude;
	search();
}

function locationError() {
	search();
}

function saveUserResponse(key, resp) {
	window.localStorage.setItem(key, resp);
}

function retrieveUserPermission(key) {
	return window.localStorage.getItem(key);
}<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';

export default class Truncate extends Component {
    static propTypes = {
        children: PropTypes.node,
        ellipsis: PropTypes.node,
        lines: PropTypes.oneOfType([
            PropTypes.oneOf([false]),
            PropTypes.number
        ])
    };

    static defaultProps = {
        children: '',
        ellipsis: '…',
        lines: 1
    };

    componentDidMount() {
        let canvas = document.createElement('canvas');
        this.canvas = canvas.getContext('2d');

        window.addEventListener('resize', this.onResize);

        this.onResize();
    }

    componentWillUnmount() {
        window.removeEventListener('resize', this.onResize);
    }

    onResize = () => {
        this.setState({
            targetWidth: null
        }, this.calcTargetWidth);
    };

    calcTargetWidth = () => {
        // Calculation is no longer relevant, since node has been removed
        if (!this.refs.target) {
            return;
        }

        // Delay calculation until parent node is inserted to the document
        // Mounting order in React is ChildComponent, ParentComponent
        if (!this.refs.target.parentNode.getBoundingClientRect().width) {
            return requestAnimationFrame(this.calcTargetWidth);
        }

        this.refs.target.style.display = 'inline-block';
        this.refs.target.style.width = '100%';
        this.refs.target.style.whiteSpace = null;

        let style = window.getComputedStyle(this.refs.target);
        let targetWidth = this.refs.target.clientWidth;

        this.refs.target.style.display = null;
        this.refs.target.style.width = null;
        this.refs.target.style.whiteSpace = 'nowrap';

        let font = [
            style['font-weight'],
            style['font-style'],
            style['font-size'],
            style['font-family']
        ].join(' ');

        this.canvas.font = font;

        this.setState({
            targetWidth
        });
    };

    measureWidth = text => {
        return this.canvas.measureText(text).width;
    };

    resetTargetStyle = () => {
        this.refs.target.style.display = null;
        this.refs.target.style.width = null;
        this.refs.target.style.whiteSpace = null;
    };

    getLines() {
        let {
            refs: {
                text: {
                    textContent: text
                },
                ellipsis: {
                    textContent: ellipsisText
                }
            },
            props: {
                lines: numLines,
                ellipsis
            },
            state: {
                targetWidth
            },
            measureWidth
        } = this;

        let lines = [];
        let textWords = text.split(' ');

        for (let line = 1; line <= numLines; line++) {
            let resultLine = textWords.join(' ');

            if (measureWidth(resultLine) < targetWidth) {
                // Line is end of text and fits without truncating //
                lines.push(resultLine);
                break;
            }

            if (line === numLines) {
                // Binary search determining the longest possible line inluding truncate string //
                let textRest = textWords.join(' ');

                let lower = 0;
                let upper = textRest.length - 1;

                while (lower <= upper) {
                    let middle = Math.floor((lower + upper) / 2);

                    let testLine = textRest.slice(0, middle + 1);

                    if (measureWidth(testLine + ellipsisText) <= targetWidth) {
                        lower = middle + 1;
                    } else {
                        upper = middle - 1;
                    }
                }

                resultLine = {textRest.slice(0, lower)}{ellipsis};
            } else {
                // Binary search determining when the line breaks //
                let lower = 0;
                let upper = textWords.length - 1;

                while (lower <= upper) {
                    let middle = Math.floor((lower + upper) / 2);

                    let testLine = textWords.slice(0, middle + 1).join(' ');

                    if (measureWidth(testLine) <= targetWidth) {
                        lower = middle + 1;
                    } else {
                        upper = middle - 1;
                    }
                }

                resultLine = textWords.slice(0, lower).join(' ');
                textWords = textWords.slice(lower, textWords.length);
            }

            lines.push(resultLine);
        }

        return lines;
    }

    renderLine = (line, i, arr) => {
        if (i === arr.length - 1) {
            return {line};
        } else {
            return [
                {line},

            ];
        }
    };

    render() {
        let {
            refs: {
                target
            },
            props,
            props: {
                children,
                ellipsis,
                lines
            }
        } = this;

        let text = children;

        if (target) {
            if (lines > 0) {
                text = this.getLines().map(this.renderLine);
            } else {
                this.resetTargetStyle();
            }
        }

        return (

                {text}

                    {children}
                    {ellipsis}

        );
    }

    styles = {
        raw: {
            display: 'none'
        }
    };
};
<commit_msg>Prevent lines and ellipsis props passed to span (#7)<commit_after>import React, { Component, PropTypes } from 'react';

export default class Truncate extends Component {
    static propTypes = {
        children: PropTypes.node,
        ellipsis: PropTypes.node,
        lines: PropTypes.oneOfType([
            PropTypes.oneOf([false]),
            PropTypes.number
        ])
    };

    static defaultProps = {
        children: '',
        ellipsis: '…',
        lines: 1
    };

    componentDidMount() {
        let canvas = document.createElement('canvas');
        this.canvas = canvas.getContext('2d');

        window.addEventListener('resize', this.onResize);

        this.onResize();
    }

    componentWillUnmount() {
        window.removeEventListener('resize', this.onResize);
    }

    onResize = () => {
        this.setState({
            targetWidth: null
        }, this.calcTargetWidth);
    };

    calcTargetWidth = () => {
        // Calculation is no longer relevant, since node has been removed
        if (!this.refs.target) {
            return;
        }

        // Delay calculation until parent node is inserted to the document
        // Mounting order in React is ChildComponent, ParentComponent
        if (!this.refs.target.parentNode.getBoundingClientRect().width) {
            return requestAnimationFrame(this.calcTargetWidth);
        }

        this.refs.target.style.display = 'inline-block';
        this.refs.target.style.width = '100%';
        this.refs.target.style.whiteSpace = null;

        let style = window.getComputedStyle(this.refs.target);
        let targetWidth = this.refs.target.clientWidth;

        this.refs.target.style.display = null;
        this.refs.target.style.width = null;
        this.refs.target.style.whiteSpace = 'nowrap';

        let font = [
            style['font-weight'],
            style['font-style'],
            style['font-size'],
            style['font-family']
        ].join(' ');

        this.canvas.font = font;

        this.setState({
            targetWidth
        });
    };

    measureWidth = text => {
        return this.canvas.measureText(text).width;
    };

    resetTargetStyle = () => {
        this.refs.target.style.display = null;
        this.refs.target.style.width = null;
        this.refs.target.style.whiteSpace = null;
    };

    getLines() {
        let {
            refs: {
                text: {
                    textContent: text
                },
                ellipsis: {
                    textContent: ellipsisText
                }
            },
            props: {
                lines: numLines,
                ellipsis
            },
            state: {
                targetWidth
            },
            measureWidth
        } = this;

        let lines = [];
        let textWords = text.split(' ');

        for (let line = 1; line <= numLines; line++) {
            let resultLine = textWords.join(' ');

            if (measureWidth(resultLine) < targetWidth) {
                // Line is end of text and fits without truncating //
                lines.push(resultLine);
                break;
            }

            if (line === numLines) {
                // Binary search determining the longest possible line inluding truncate string //
                let textRest = textWords.join(' ');

                let lower = 0;
                let upper = textRest.length - 1;

                while (lower <= upper) {
                    let middle = Math.floor((lower + upper) / 2);

                    let testLine = textRest.slice(0, middle + 1);

                    if (measureWidth(testLine + ellipsisText) <= targetWidth) {
                        lower = middle + 1;
                    } else {
                        upper = middle - 1;
                    }
                }

                resultLine = {textRest.slice(0, lower)}{ellipsis};
            } else {
                // Binary search determining when the line breaks //
                let lower = 0;
                let upper = textWords.length - 1;

                while (lower <= upper) {
                    let middle = Math.floor((lower + upper) / 2);

                    let testLine = textWords.slice(0, middle + 1).join(' ');

                    if (measureWidth(testLine) <= targetWidth) {
                        lower = middle + 1;
                    } else {
                        upper = middle - 1;
                    }
                }

                resultLine = textWords.slice(0, lower).join(' ');
                textWords = textWords.slice(lower, textWords.length);
            }

            lines.push(resultLine);
        }

        return lines;
    }

    renderLine = (line, i, arr) => {
        if (i === arr.length - 1) {
            return {line};
        } else {
            return [
                {line},

            ];
        }
    };

    render() {
        let {
            refs: {
                target
            },
            props: {
                children,
                ellipsis,
                lines,
                ...spanProps
            }
        } = this;

        let text = children;

        if (target) {
            if (lines > 0) {
                text = this.getLines().map(this.renderLine);
            } else {
                this.resetTargetStyle();
            }
        }

        return (

                {text}

                    {children}
                    {ellipsis}

        );
    }

    styles = {
        raw: {
            display: 'none'
        }
    };
};
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

const mongoose = require('mongoose')
    , express = require('express')
    , assert = require('assert')
    , should = require('should')
    , Promise = mongoose.Promise
    , Schema = mongoose.Schema

require('../');

/**
 * Schema definition.
 */

var DrumsetSchema = new Schema({
    brand: String
  , color: String
  , type:  String
  , model: String
});

/**
 * Schema methods.
 */

DrumsetSchema.statics.useQuery = function () {
  // return a Query
  return this.find({ color: 'black' });
}

DrumsetSchema.statics.usePromise = function () {
  var promise = new Promise();
  this.find({ type: 'Acoustic' }, promise.resolve.bind(promise));
  return promise;
}

DrumsetSchema.statics.queryError = function () {
  // should produce an invalid query error
  return this.find({ color: { $fake: { $boom: [] }} });
}

DrumsetSchema.statics.promiseError = function () {
  var promise = new Promise;
  promise.error(new Error('splat!'));
  return promise;
}

mongoose.model('Drumset', DrumsetSchema);

/**
 * Mongoose connection helper.
 */

function connect () {
  return mongoose.createConnection('mongodb://localhost/express_goose_test');
}

/**
 * Dummy data.
 */

var db = connect();
var collection = 'drumsets_' + (Math.random() * 100000 | 0);
var Drumset = db.model('Drumset', collection);
var pending = 4;

Drumset.create({
    brand: 'Roland'
  , color: 'black'
  , type: 'electronic'
  , _id: '4da8b662057a83596c000001'
}, added);

Drumset.create({
    brand: 'GMS'
  , color: 'Silver Sparkle'
  , type: 'Acoustic'
  , _id: '4da8b662057a83596c000002'
}, added);

Drumset.create({
    brand: 'DW'
  , color: 'Broken Glass'
  , type: 'Acoustic'
  , _id: '4da8b662057a83596c000003'
}, added);

Drumset.create({
    brand: 'Meinl'
  , color: 'black'
  , type: 'Acoustic'
  , _id: '4da8b662057a83596c000004'
}, added);

function added (err) {
  if (err) return console.error(err);
  if (--pending) return;
  db.close();
  assignExports();
}

/**
 * Creates a test server.
 */

function makeapp () {
  var app = express.createServer();
  app.set('views', __dirname + '/fixtures');
  app.set('view engine', 'jade');
  return app;
}

/**
 * DB is ready, export our expresso tests.
 */

function assignExports () {

  /**
   * Clean up the test db when finished.
   */

  var testsrunning = 3;
  function finishTest () {
    if (--testsrunning) return;
    var db = connect();
    db.db.dropDatabase(function () {
      db.close();
    });
  }

  exports['test render'] = function () {
    var app = makeapp();
    var db = connect();
    var Drumset = db.model('Drumset', collection);

    app.get('/renderquery', function (req, res) {
      res.render('query', {
        query: Drumset.useQuery()
      });
    });

    app.get('/renderpromise', function (req, res) {
      res.render('promise', {
        promise: Drumset.usePromise()
      });
    });

    app.get('/renderboth', function (req, res) {
      res.render('both', {
          query: Drumset.useQuery()
        , promise: Drumset.usePromise()
      });
    });

    app.get('/renderqueryerror', function (req, res) {
      res.render('query', {
        query: Drumset.queryError()
      });
    });

    app.get('/renderpromiseerror', function (req, res) {
      res.render('promise', {
        promise: Drumset.promiseError()
      });
    });

    app.get('/renderbotherror', function (req, res) {
      res.render('both', {
          query: Drumset.queryError()
        , promise: Drumset.promiseError()
      });
    });

    app.get('/renderbothnest', function (req, res) {
      res.render('both', {
          locals: {
            promise: Drumset.usePromise()
          }
        , query: Drumset.useQuery()
      });
    });

    app.get('/renderlocalsonly', function (req, res) {
      res.render('promise', {
        locals: {
          promise: Drumset.usePromise()
        }
      });
    });

    app.get('/renderbothnesterror', function (req, res) {
      res.render('both', {
          locals: {
            query: Drumset.queryError()
          }
        , promise: Drumset.usePromise()
      });
    });

    // test

    var pending = 6;
    function done () {
      if (--pending) return;
      db.close();
      finishTest();
    }

    assert.response(app,
      { url: '/renderquery' }
    , { status: 200
      , body: '

Roland

'
      }
    , done
    );

    assert.response(app,
      { url: '/renderpromise' }
    , { status: 200
      , body: '

Silver Sparkle

'
      }
    , done
    );

    assert.response(app,
      { url: '/renderboth' }
    , { status: 200
      , body: '

Roland

Meinl

'
      }
    , done
    );

    assert.response(app,
      { url: '/renderbothnest' }
    , { status: 200
      , body: '

Roland

Meinl

'
      }
    , done
    );

    assert.response(app,
      { url: '/renderlocalsonly' }
    , { status: 200
      , body: '

Silver Sparkle

'
      }
    , done
    );

    assert.response(app,
      { url: '/renderqueryerror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: Can't use $fake with String."));
        done();
      }
    );

    assert.response(app,
      { url: '/renderpromiseerror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: splat!"));
        done();
      }
    );

    assert.response(app,
      { url: '/renderbotherror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: splat!"));
        done();
      }
    );

    assert.response(app,
      { url: '/renderbothnesterror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: Can't use $fake with String."));
        done();
      }
    );
  };

  exports['test partial'] = function () {
    var app = makeapp();
    var db = connect();
    var Drumset = db.model('Drumset', collection);

    app.get('/partialquery', function (req, res) {
      res.partial('query', {
        query: Drumset.useQuery()
      });
    });

    app.get('/partiallocalsonly', function (req, res) {
      res.partial('promise', {
        locals: {
          promise: Drumset.usePromise()
        }
      });
    });

    app.get('/partialpromise', function (req, res) {
      res.partial('promise', {
        promise: Drumset.usePromise()
      });
    });

    app.get('/partialboth', function (req, res) {
      res.partial('both', {
          query: Drumset.useQuery()
        , promise: Drumset.usePromise()
      });
    });

    app.get('/partialqueryerror', function (req, res) {
      res.partial('query', {
        query: Drumset.queryError()
      });
    });

    app.get('/partialpromiseerror', function (req, res) {
      res.partial('promise', {
        promise: Drumset.promiseError()
      });
    });

    app.get('/partialbotherror', function (req, res) {
      res.partial('both', {
          query: Drumset.queryError()
        , promise: Drumset.promiseError()
      });
    });

    // test

    var pending = 6;
    function done () {
      if (--pending) return;
      db.close();
      finishTest();
    }

    assert.response(app,
      { url: '/partialquery' }
    , { status: 200
      , body: '

Roland

'
      }
    , done
    );

    assert.response(app,
      { url: '/partialpromise' }
    , { status: 200
      , body: '

Silver Sparkle

'
      }
    , done
    );

    assert.response(app,
      { url: '/partialboth' }
    , { status: 200
      , body: '

Roland

Meinl

'
      }
    , done
    );

    assert.response(app,
      { url: '/partiallocalsonly' }
    , { status: 200
      , body: '

Silver Sparkle

'
      }
    , done
    );

    assert.response(app,
      { url: '/partialqueryerror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: Can't use $fake with String."));
        done();
      }
    );

    assert.response(app,
      { url: '/partialpromiseerror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: splat!"));
        done();
      }
    );

    assert.response(app,
      { url: '/partialbotherror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: splat!"));
        done();
      }
    );
  };

  exports['test send'] = function () {
    var app = makeapp();
    var db = connect();
    var Drumset = db.model('Drumset', collection);

    app.get('/sendquery', function (req, res) {
      res.send(Drumset.useQuery());
    });

    app.get('/sendpromise', function (req, res) {
      res.send(Drumset.usePromise());
    });

    app.get('/sendboth', function (req, res) {
      res.send({
          query: Drumset.useQuery()
        , promise: Drumset.usePromise()
      });
    });

    app.get('/sendqueryerror', function (req, res) {
      res.send(Drumset.queryError());
    });

    app.get('/sendpromiseerror', function (req, res) {
      res.send(Drumset.promiseError());
    });

    app.get('/sendbotherror', function (req, res) {
      res.send({
          query: Drumset.queryError()
        , promise: Drumset.promiseError()
      });
    });

    // test

    var pending = 6;
    function done () {
      if (--pending) return;
      db.close();
      finishTest();
    }

    assert.response(app,
      { url: '/sendquery' }
    , { status: 200
      , body: '[{"_id":"4da8b662057a83596c000001","type":"electronic","color":"black","brand":"Roland"},{"_id":"4da8b662057a83596c000004","type":"Acoustic","color":"black","brand":"Meinl"}]'
      , headers: { 'Content-Type': 'application/json' }
      }
    , done
    );

    assert.response(app,
      { url: '/sendpromise' }
    , { status: 200
      , body: '[{"_id":"4da8b662057a83596c000002","type":"Acoustic","color":"Silver Sparkle","brand":"GMS"},{"_id":"4da8b662057a83596c000003","type":"Acoustic","color":"Broken Glass","brand":"DW"},{"_id":"4da8b662057a83596c000004","type":"Acoustic","color":"black","brand":"Meinl"}]'
      , headers: { 'Content-Type': 'application/json' }
      }
    , done
    );

    assert.response(app,
      { url: '/sendboth' }
    , { status: 200
      , body: '{"query":[{"_id":"4da8b662057a83596c000001","type":"electronic","color":"black","brand":"Roland"},{"_id":"4da8b662057a83596c000004","type":"Acoustic","color":"black","brand":"Meinl"}],"promise":[{"_id":"4da8b662057a83596c000002","type":"Acoustic","color":"Silver Sparkle","brand":"GMS"},{"_id":"4da8b662057a83596c000003","type":"Acoustic","color":"Broken Glass","brand":"DW"},{"_id":"4da8b662057a83596c000004","type":"Acoustic","color":"black","brand":"Meinl"}]}'
      , headers: { 'Content-Type': 'application/json' }
      }
    , done
    );

    assert.response(app,
      { url: '/sendqueryerror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: Can't use $fake with String."));
        done();
      }
    );

    assert.response(app,
      { url: '/sendpromiseerror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: splat!"));
        done();
      }
    );

    assert.response(app,
      { url: '/sendbotherror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: splat!"));
        done();
      }
    );
  }

}
<commit_msg>allow tests to handle json properties in different orders<commit_after>
/**
 * Module dependencies.
 */

const mongoose = require('mongoose')
    , express = require('express')
    , assert = require('assert')
    , should = require('should')
    , Promise = mongoose.Promise
    , Schema = mongoose.Schema

require('../');

/**
 * Schema definition.
 */

var DrumsetSchema = new Schema({
    brand: String
  , color: String
  , type:  String
  , model: String
});

/**
 * Schema methods.
 */

DrumsetSchema.statics.useQuery = function () {
  // return a Query
  return this.find({ color: 'black' }).sort('_id', 1);
}

DrumsetSchema.statics.usePromise = function () {
  var promise = new Promise();
  this.find({ type: 'Acoustic' })
      .sort('_id', 1)
      .run(promise.resolve.bind(promise));
  return promise;
}

DrumsetSchema.statics.queryError = function () {
  // should produce an invalid query error
  return this.find({ color: { $fake: { $boom: [] }} }).sort('_id', 1);
}

DrumsetSchema.statics.promiseError = function () {
  var promise = new Promise;
  promise.error(new Error('splat!'));
  return promise;
}

mongoose.model('Drumset', DrumsetSchema);

/**
 * Mongoose connection helper.
 */

function connect () {
  return mongoose.createConnection('mongodb://localhost/express_goose_test');
}

/**
 * Dummy data.
 */

var db = connect();
var collection = 'drumsets_' + (Math.random() * 100000 | 0);
var Drumset = db.model('Drumset', collection);
var pending = 4;

Drumset.create({
    brand: 'Roland'
  , color: 'black'
  , type: 'electronic'
  , _id: '4da8b662057a83596c000001'
}, added);

Drumset.create({
    brand: 'GMS'
  , color: 'Silver Sparkle'
  , type: 'Acoustic'
  , _id: '4da8b662057a83596c000002'
}, added);

Drumset.create({
    brand: 'DW'
  , color: 'Broken Glass'
  , type: 'Acoustic'
  , _id: '4da8b662057a83596c000003'
}, added);

Drumset.create({
    brand: 'Meinl'
  , color: 'black'
  , type: 'Acoustic'
  , _id: '4da8b662057a83596c000004'
}, added);

function added (err) {
  if (err) return console.error(err);
  if (--pending) return;
  db.close();
  assignExports();
}

/**
 * Creates a test server.
 */

function makeapp () {
  var app = express.createServer();
  app.set('views', __dirname + '/fixtures');
  app.set('view engine', 'jade');
  return app;
}

/**
 * DB is ready, export our expresso tests.
 */

function assignExports () {

  /**
   * Clean up the test db when finished.
   */

  var testsrunning = 3;
  function finishTest () {
    if (--testsrunning) return;
    var db = connect();
    db.db.dropDatabase(function () {
      db.close();
    });
  }

  exports['test render'] = function () {
    var app = makeapp();
    var db = connect();
    var Drumset = db.model('Drumset', collection);

    app.get('/renderquery', function (req, res) {
      res.render('query', {
        query: Drumset.useQuery()
      });
    });

    app.get('/renderpromise', function (req, res) {
      res.render('promise', {
        promise: Drumset.usePromise()
      });
    });

    app.get('/renderboth', function (req, res) {
      res.render('both', {
          query: Drumset.useQuery()
        , promise: Drumset.usePromise()
      });
    });

    app.get('/renderqueryerror', function (req, res) {
      res.render('query', {
        query: Drumset.queryError()
      });
    });

    app.get('/renderpromiseerror', function (req, res) {
      res.render('promise', {
        promise: Drumset.promiseError()
      });
    });

    app.get('/renderbotherror', function (req, res) {
      res.render('both', {
          query: Drumset.queryError()
        , promise: Drumset.promiseError()
      });
    });

    app.get('/renderbothnest', function (req, res) {
      res.render('both', {
          locals: {
            promise: Drumset.usePromise()
          }
        , query: Drumset.useQuery()
      });
    });

    app.get('/renderlocalsonly', function (req, res) {
      res.render('promise', {
        locals: {
          promise: Drumset.usePromise()
        }
      });
    });

    app.get('/renderbothnesterror', function (req, res) {
      res.render('both', {
          locals: {
            query: Drumset.queryError()
          }
        , promise: Drumset.usePromise()
      });
    });

    // test

    var pending = 6;
    function done () {
      if (--pending) return;
      db.close();
      finishTest();
    }

    assert.response(app,
      { url: '/renderquery' }
    , { status: 200
      , body: '

Roland

'
      }
    , done
    );

    assert.response(app,
      { url: '/renderpromise' }
    , { status: 200
      , body: '

Silver Sparkle

'
      }
    , done
    );

    assert.response(app,
      { url: '/renderboth' }
    , { status: 200
      , body: '

Roland

Meinl

'
      }
    , done
    );

    assert.response(app,
      { url: '/renderbothnest' }
    , { status: 200
      , body: '

Roland

Meinl

'
      }
    , done
    );

    assert.response(app,
      { url: '/renderlocalsonly' }
    , { status: 200
      , body: '

Silver Sparkle

'
      }
    , done
    );

    assert.response(app,
      { url: '/renderqueryerror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: Can't use $fake with String."));
        done();
      }
    );

    assert.response(app,
      { url: '/renderpromiseerror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: splat!"));
        done();
      }
    );

    assert.response(app,
      { url: '/renderbotherror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: splat!"));
        done();
      }
    );

    assert.response(app,
      { url: '/renderbothnesterror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: Can't use $fake with String."));
        done();
      }
    );
  };

  exports['test partial'] = function () {
    var app = makeapp();
    var db = connect();
    var Drumset = db.model('Drumset', collection);

    app.get('/partialquery', function (req, res) {
      res.partial('query', {
        query: Drumset.useQuery()
      });
    });

    app.get('/partiallocalsonly', function (req, res) {
      res.partial('promise', {
        locals: {
          promise: Drumset.usePromise()
        }
      });
    });

    app.get('/partialpromise', function (req, res) {
      res.partial('promise', {
        promise: Drumset.usePromise()
      });
    });

    app.get('/partialboth', function (req, res) {
      res.partial('both', {
          query: Drumset.useQuery()
        , promise: Drumset.usePromise()
      });
    });

    app.get('/partialqueryerror', function (req, res) {
      res.partial('query', {
        query: Drumset.queryError()
      });
    });

    app.get('/partialpromiseerror', function (req, res) {
      res.partial('promise', {
        promise: Drumset.promiseError()
      });
    });

    app.get('/partialbotherror', function (req, res) {
      res.partial('both', {
          query: Drumset.queryError()
        , promise: Drumset.promiseError()
      });
    });

    // test

    var pending = 6;
    function done () {
      if (--pending) return;
      db.close();
      finishTest();
    }

    assert.response(app,
      { url: '/partialquery' }
    , { status: 200
      , body: '

Roland

'
      }
    , done
    );

    assert.response(app,
      { url: '/partialpromise' }
    , { status: 200
      , body: '

Silver Sparkle

'
      }
    , done
    );

    assert.response(app,
      { url: '/partialboth' }
    , { status: 200
      , body: '

Roland

Meinl

'
      }
    , done
    );

    assert.response(app,
      { url: '/partiallocalsonly' }
    , { status: 200
      , body: '

Silver Sparkle

'
      }
    , done
    );

    assert.response(app,
      { url: '/partialqueryerror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: Can't use $fake with String."));
        done();
      }
    );

    assert.response(app,
      { url: '/partialpromiseerror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: splat!"));
        done();
      }
    );

    assert.response(app,
      { url: '/partialbotherror' }
    , function (res) {
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: splat!"));
        done();
      }
    );
  };

  exports['test send'] = function () {
    var app = makeapp();
    var db = connect();
    var Drumset = db.model('Drumset', collection);

    app.get('/sendquery', function (req, res) {
      res.send(Drumset.useQuery());
    });

    app.get('/sendpromise', function (req, res) {
      res.send(Drumset.usePromise());
    });

    app.get('/sendboth', function (req, res) {
      res.send({
          query: Drumset.useQuery()
        , promise: Drumset.usePromise()
      });
    });

    app.get('/sendqueryerror', function (req, res) {
      res.send(Drumset.queryError());
    });

    app.get('/sendpromiseerror', function (req, res) {
      res.send(Drumset.promiseError());
    });

    app.get('/sendbotherror', function (req, res) {
      res.send({
          query: Drumset.queryError()
        , promise: Drumset.promiseError()
      });
    });

    // test

    var pending = 6;
    function done () {
      if (--pending) return;
      db.close();
      finishTest();
    }

    assert.response(app,
      { url: '/sendquery' }
    , function (res) {
        done();

        assert.equal(res.statusCode, 200);
        assert.ok(/^application\/json/.test(res.headers['content-type']));

        var body = JSON.parse(res.body);

        assert.ok(!!body);
        assert.ok(Array.isArray(body));
        assert.equal(body.length, 2);
        body.forEach(function (doc) {
          assert.equal(doc.color, 'black');
        });
      }
    );

    assert.response(app,
      { url: '/sendpromise' }
    , function (res) {
        done();

        assert.equal(res.statusCode, 200);
        assert.ok(/^application\/json/.test(res.headers['content-type']));

        var body = JSON.parse(res.body);

        assert.ok(!!body);
        assert.ok(Array.isArray(body));
        assert.equal(body.length, 3);
        body.forEach(function (doc) {
          assert.equal(doc.type, 'Acoustic');
        });
      }
    );

    assert.response(app,
      { url: '/sendboth' }
    , function (res) {
        done();

        assert.equal(res.statusCode, 200);
        assert.ok(/^application\/json/.test(res.headers['content-type']));

        var body = JSON.parse(res.body);

        assert.ok(!!body);
        assert.equal(body.query.length, 2);
        assert.equal(body.promise.length, 3);
      }
    );

    assert.response(app,
      { url: '/sendqueryerror' }
    , function (res) {
        done();
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: Can't use $fake with String."));
      }
    );

    assert.response(app,
      { url: '/sendpromiseerror' }
    , function (res) {
        done();
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: splat!"));
      }
    );

    assert.response(app,
      { url: '/sendbotherror' }
    , function (res) {
        done();
        assert.equal(res.statusCode, 500);
        assert.ok(~res.body.indexOf("Error: splat!"));
      }
    );
  }

}
<|endoftext|>


<commit_before>"use strict";

const fs = require("fs");
const path = require("path");
require("should");
const glob = require("glob");
const rootDir = path.resolve(__dirname, "..");

describe("Schemas", () => {
	const schemas = glob.sync("schemas/**/*.json", {
		cwd: rootDir
	});

	schemas.forEach(filename => {
		describe(filename, () => {
			let content;
			let fileContent;
			let errorWhileParsing;

			try {
				fileContent = fs.readFileSync(path.resolve(rootDir, filename), "utf-8");
				content = JSON.parse(fileContent);
			} catch (e) {
				errorWhileParsing = e;
			}

			it("should be parse-able", () => {
				if (errorWhileParsing) throw errorWhileParsing;
			});

			if (content) {
				it("should be formated correctly", () => {
					fileContent
						.replace(/\r\n?/g, "\n")
						.should.be.eql(JSON.stringify(content, 0, 2) + "\n");
				});

				const arrayProperties = ["oneOf", "anyOf", "allOf"];
				const allowedProperties = [
					"definitions",
					"$ref",
					"id",
					"items",
					"properties",
					"additionalProperties",
					"type",
					"oneOf",
					"anyOf",
					"allOf",
					"absolutePath",
					"description",
					"enum",
					"minLength",
					"minimum",
					"required",
					"uniqueItems",
					"minItems",
					"minProperties",
					"instanceof"
				];

				const validateProperty = property => {
					it("should have description set", () => {
						property.should.be.property("description").be.type("string");
						property.description.length.should.be.above(1);
					});
				};

				const walker = item => {
					it("should only use allowed schema properties", () => {
						const otherProperties = Object.keys(item).filter(
							p => allowedProperties.indexOf(p) < 0
						);
						if (otherProperties.length > 0) {
							throw new Error(
								`The properties ${otherProperties.join(
									", "
								)} are not allowed to use`
							);
							// When allowing more properties make sure to add nice error messages for them in WebpackOptionsValidationError
						}
					});

					if (Object.keys(item).indexOf("$ref") >= 0) {
						it("should not have other properties next to $ref", () => {
							const otherProperties = Object.keys(item).filter(
								p => p !== "$ref"
							);
							if (otherProperties.length > 0) {
								throw new Error(
									`When using $ref not other properties are possible (${otherProperties.join(
										", "
									)})`
								);
							}
						});
					}

					arrayProperties.forEach(prop => {
						if (prop in item) {
							describe(prop, () => {
								item[prop].forEach(walker);
							});
						}
					});
					if ("items" in item) {
						describe("items", () => {
							if (Object.keys(item).join() !== "$ref") {
								validateProperty(item.items);
							}
							walker(item.items);
						});
					}
					if ("definitions" in item) {
						Object.keys(item.definitions).forEach(name => {
							describe(`#${name}`, () => {
								walker(item.definitions[name]);
							});
						});
					}
					if ("properties" in item) {
						it("should have additionalProperties set to some value when descriping properties", () => {
							item.should.be.property("additionalProperties");
						});
						Object.keys(item.properties).forEach(name => {
							describe(`> '${name}'`, () => {
								const property = item.properties[name];
								validateProperty(property);
								walker(property);
							});
						});
					}
					if (typeof item.additionalProperties === "object") {
						describe("properties", () => {
							validateProperty(item.additionalProperties);
							walker(item.additionalProperties);
						});
					}
				};

				walker(content);
			}
		});
	});
});
<commit_msg>spelling: describing<commit_after>"use strict";

const fs = require("fs");
const path = require("path");
require("should");
const glob = require("glob");
const rootDir = path.resolve(__dirname, "..");

describe("Schemas", () => {
	const schemas = glob.sync("schemas/**/*.json", {
		cwd: rootDir
	});

	schemas.forEach(filename => {
		describe(filename, () => {
			let content;
			let fileContent;
			let errorWhileParsing;

			try {
				fileContent = fs.readFileSync(path.resolve(rootDir, filename), "utf-8");
				content = JSON.parse(fileContent);
			} catch (e) {
				errorWhileParsing = e;
			}

			it("should be parse-able", () => {
				if (errorWhileParsing) throw errorWhileParsing;
			});

			if (content) {
				it("should be formated correctly", () => {
					fileContent
						.replace(/\r\n?/g, "\n")
						.should.be.eql(JSON.stringify(content, 0, 2) + "\n");
				});

				const arrayProperties = ["oneOf", "anyOf", "allOf"];
				const allowedProperties = [
					"definitions",
					"$ref",
					"id",
					"items",
					"properties",
					"additionalProperties",
					"type",
					"oneOf",
					"anyOf",
					"allOf",
					"absolutePath",
					"description",
					"enum",
					"minLength",
					"minimum",
					"required",
					"uniqueItems",
					"minItems",
					"minProperties",
					"instanceof"
				];

				const validateProperty = property => {
					it("should have description set", () => {
						property.should.be.property("description").be.type("string");
						property.description.length.should.be.above(1);
					});
				};

				const walker = item => {
					it("should only use allowed schema properties", () => {
						const otherProperties = Object.keys(item).filter(
							p => allowedProperties.indexOf(p) < 0
						);
						if (otherProperties.length > 0) {
							throw new Error(
								`The properties ${otherProperties.join(
									", "
								)} are not allowed to use`
							);
							// When allowing more properties make sure to add nice error messages for them in WebpackOptionsValidationError
						}
					});

					if (Object.keys(item).indexOf("$ref") >= 0) {
						it("should not have other properties next to $ref", () => {
							const otherProperties = Object.keys(item).filter(
								p => p !== "$ref"
							);
							if (otherProperties.length > 0) {
								throw new Error(
									`When using $ref not other properties are possible (${otherProperties.join(
										", "
									)})`
								);
							}
						});
					}

					arrayProperties.forEach(prop => {
						if (prop in item) {
							describe(prop, () => {
								item[prop].forEach(walker);
							});
						}
					});
					if ("items" in item) {
						describe("items", () => {
							if (Object.keys(item).join() !== "$ref") {
								validateProperty(item.items);
							}
							walker(item.items);
						});
					}
					if ("definitions" in item) {
						Object.keys(item.definitions).forEach(name => {
							describe(`#${name}`, () => {
								walker(item.definitions[name]);
							});
						});
					}
					if ("properties" in item) {
						it("should have additionalProperties set to some value when describing properties", () => {
							item.should.be.property("additionalProperties");
						});
						Object.keys(item.properties).forEach(name => {
							describe(`> '${name}'`, () => {
								const property = item.properties[name];
								validateProperty(property);
								walker(property);
							});
						});
					}
					if (typeof item.additionalProperties === "object") {
						describe("properties", () => {
							validateProperty(item.additionalProperties);
							walker(item.additionalProperties);
						});
					}
				};

				walker(content);
			}
		});
	});
});
<|endoftext|>


<commit_before>import { isNil } from 'lodash/lang'
import { getCurrentCampaignHardcodedData } from './hardcodedCampaignData'

const createCampaignData = async (userContext, campaignConfig) => {
  const {
    campaignId,
    content,
    endContent,
    getCharityData,
    goal,
    incrementNewUserCount,
    incrementTabCount,
    isActive,
    isLive,
    showCountdownTimer,
    showHeartsDonationButton,
    showProgressBar,
    time,
  } = campaignConfig

  // Fetch the charity.
  const charity = await getCharityData(userContext)

  // If there is a goal, fetch the current goal number.
  let goalWithData
  if (!isNil(goal)) {
    const {
      getCurrentNumber,
      impactUnitSingular,
      impactUnitPlural,
      impactVerbPastTense,
      targetNumber,
    } = goal
    const currentNumber = await getCurrentNumber()
    goalWithData = {
      currentNumber,
      impactUnitSingular,
      impactUnitPlural,
      impactVerbPastTense,
      targetNumber,
    }
  }

  return {
    campaignId,
    ...(charity && { charity }),
    content,
    ...(endContent && { endContent }),
    ...(goalWithData && { goal: goalWithData }),
    incrementNewUserCount,
    incrementTabCount,
    isActive,
    isLive,
    showCountdownTimer,
    showHeartsDonationButton,
    showProgressBar,
    time,
  }
}

const getCampaign = async userContext => {
  return createCampaignData(userContext, getCurrentCampaignHardcodedData())
}

export default getCampaign
<commit_msg>Add TODO<commit_after>import { isNil } from 'lodash/lang'
import { getCurrentCampaignHardcodedData } from './hardcodedCampaignData'

const createCampaignData = async (userContext, campaignConfig) => {
  const {
    campaignId,
    content,
    endContent,
    getCharityData,
    goal,
    incrementNewUserCount,
    incrementTabCount,
    isActive,
    isLive,
    showCountdownTimer,
    showHeartsDonationButton,
    showProgressBar,
    time,
  } = campaignConfig

  // TODO: we may not want to fetch this every time when
  //   the requester may not need the data.
  // Fetch the charity.
  const charity = await getCharityData(userContext)

  // If there is a goal, fetch the current goal number.
  let goalWithData
  if (!isNil(goal)) {
    const {
      getCurrentNumber,
      impactUnitSingular,
      impactUnitPlural,
      impactVerbPastTense,
      targetNumber,
    } = goal
    const currentNumber = await getCurrentNumber()
    goalWithData = {
      currentNumber,
      impactUnitSingular,
      impactUnitPlural,
      impactVerbPastTense,
      targetNumber,
    }
  }

  return {
    campaignId,
    ...(charity && { charity }),
    content,
    ...(endContent && { endContent }),
    ...(goalWithData && { goal: goalWithData }),
    incrementNewUserCount,
    incrementTabCount,
    isActive,
    isLive,
    showCountdownTimer,
    showHeartsDonationButton,
    showProgressBar,
    time,
  }
}

const getCampaign = async userContext => {
  return createCampaignData(userContext, getCurrentCampaignHardcodedData())
}

export default getCampaign
<|endoftext|>