<commit_before>const path = require('path');
const fs = require('fs-extra');
const chalk = require('chalk');
const { spawn } = require('child_process');

const BUILD_DIRECTORY = `deploy`;

const NORMALIZED_ROOT = path.normalize(`${__dirname}/..`);
const NORMALIZED_DIRECTORY = path.normalize(`${NORMALIZED_ROOT}/${BUILD_DIRECTORY}`);

const BUILD_COMMAND = `parcel build src/index.js --no-cache --out-dir ${NORMALIZED_DIRECTORY}/build`;
const BUILD_BG_COMMAND = `parcel build src/background.js --no-cache --out-dir ${NORMALIZED_DIRECTORY}/build`;

const copyRoot = async fileName => {
  try {
    console.log(`\n${NORMALIZED_ROOT}/${fileName} -> ${NORMALIZED_DIRECTORY}/${fileName}`);
    await fs.copy(`${NORMALIZED_ROOT}/${fileName}`, `${NORMALIZED_DIRECTORY}/${fileName}`);
    console.log(chalk.green(`✅  Success!`));
  } catch (e) {
    console.log(chalk.red(`❌  ERROR - Failed to copy file '${fileName}'`));
  }
};

const exec = (cmd, name = '') => {
  const getParts = cmdStr => {
    const parts = cmdStr.split(' ');
    return {
      command: parts[0],
      args: parts.slice(1),
    };
  };

  const { command, args } = getParts(cmd);
  const BUILD_PROCESS = spawn(command, args, {
    cwd: NORMALIZED_ROOT,
    stdio: [process.stdin, process.stdout, 'pipe'],
  });

  BUILD_PROCESS.stderr.on('data', data => {
    console.log(chalk.red(`❌  ERROR - ${data}`));
  });

  BUILD_PROCESS.on('close', data => {
    console.log(chalk.green(`✅  ${name} Success!`));
  });
};

const build = async () => {
  try {
    console.log(chalk.cyan('\n🎉  Starting Bundle Task 🎉  '));

    console.log(chalk.blue(`\nRemoving Deploy Directory '${NORMALIZED_DIRECTORY}'`));
    await fs.remove(NORMALIZED_DIRECTORY);
    console.log(chalk.green(`✅  Success!`));

    console.log(chalk.blue(`\nCreating Deploy Directory '${NORMALIZED_DIRECTORY}'`));
    await fs.ensureDir(NORMALIZED_DIRECTORY);
    console.log(chalk.green(`✅  Success!`));

    console.log(chalk.blue(`\nCopying package.json`));
    await copyRoot('package.json');

    console.log(chalk.blue(`\nCopying manifest.json`));
    await copyRoot('manifest.json');

    console.log(chalk.blue(`\nCopying Icons`));
    await copyRoot('icon16.png');
    await copyRoot('icon48.png');
    await copyRoot('icon128.png');

    console.log(chalk.blue(`\nRunning Build`));
    console.log(chalk.blue(BUILD_COMMAND));
    exec(BUILD_COMMAND, 'Build');

    console.log(chalk.blue(`\nRunning Background Build`));
    console.log(chalk.blue(BUILD_BG_COMMAND));
    exec(BUILD_BG_COMMAND, 'Background Build');
  } catch (err) {
    console.log(chalk.red(`❌  Build Failed!`));
    console.log(chalk.red(`❌  ERROR - ${err}`));
  }
};

build();
<commit_msg>Renamed index.js -> play-midnight.js in production script<commit_after>const path = require('path');
const fs = require('fs-extra');
const chalk = require('chalk');
const { spawn } = require('child_process');

const BUILD_DIRECTORY = `deploy`;

const NORMALIZED_ROOT = path.normalize(`${__dirname}/..`);
const NORMALIZED_DIRECTORY = path.normalize(`${NORMALIZED_ROOT}/${BUILD_DIRECTORY}`);

const BUILD_COMMAND = `parcel build src/play-midnight.js --no-cache --out-dir ${NORMALIZED_DIRECTORY}/build`;
const BUILD_BG_COMMAND = `parcel build src/background.js --no-cache --out-dir ${NORMALIZED_DIRECTORY}/build`;

const copyRoot = async fileName => {
  try {
    console.log(`\n${NORMALIZED_ROOT}/${fileName} -> ${NORMALIZED_DIRECTORY}/${fileName}`);
    await fs.copy(`${NORMALIZED_ROOT}/${fileName}`, `${NORMALIZED_DIRECTORY}/${fileName}`);
    console.log(chalk.green(`✅  Success!`));
  } catch (e) {
    console.log(chalk.red(`❌  ERROR - Failed to copy file '${fileName}'`));
  }
};

const exec = (cmd, name = '') => {
  const getParts = cmdStr => {
    const parts = cmdStr.split(' ');
    return {
      command: parts[0],
      args: parts.slice(1),
    };
  };

  const { command, args } = getParts(cmd);
  const BUILD_PROCESS = spawn(command, args, {
    cwd: NORMALIZED_ROOT,
    stdio: [process.stdin, process.stdout, 'pipe'],
  });

  BUILD_PROCESS.stderr.on('data', data => {
    console.log(chalk.red(`❌  ERROR - ${data}`));
  });

  BUILD_PROCESS.on('close', data => {
    console.log(chalk.green(`✅  ${name} Success!`));
  });
};

const build = async () => {
  try {
    console.log(chalk.cyan('\n🎉  Starting Bundle Task 🎉  '));

    console.log(chalk.blue(`\nRemoving Deploy Directory '${NORMALIZED_DIRECTORY}'`));
    await fs.remove(NORMALIZED_DIRECTORY);
    console.log(chalk.green(`✅  Success!`));

    console.log(chalk.blue(`\nCreating Deploy Directory '${NORMALIZED_DIRECTORY}'`));
    await fs.ensureDir(NORMALIZED_DIRECTORY);
    console.log(chalk.green(`✅  Success!`));

    console.log(chalk.blue(`\nCopying package.json`));
    await copyRoot('package.json');

    console.log(chalk.blue(`\nCopying manifest.json`));
    await copyRoot('manifest.json');

    console.log(chalk.blue(`\nCopying Icons`));
    await copyRoot('icon16.png');
    await copyRoot('icon48.png');
    await copyRoot('icon128.png');

    console.log(chalk.blue(`\nRunning Build`));
    console.log(chalk.blue(BUILD_COMMAND));
    exec(BUILD_COMMAND, 'Build');

    console.log(chalk.blue(`\nRunning Background Build`));
    console.log(chalk.blue(BUILD_BG_COMMAND));
    exec(BUILD_BG_COMMAND, 'Background Build');
  } catch (err) {
    console.log(chalk.red(`❌  Build Failed!`));
    console.log(chalk.red(`❌  ERROR - ${err}`));
  }
};

build();
<|endoftext|>


<commit_before>(function(){
  'use strict';

  var utils = require('../e2e/_utils'),
      CONSTANTS = require('../protractor.constants.js'),
      modelsRecipe = require('./_modelsRecipe');

  ddescribe('Provider Case Control', function() {
    var case_to_accept;
    var accept_button = element(by.name('accept-case'));
    var close_button = element(by.name('provider-close-case'));

    function get_provider() {
      return element(by.css('.ContactBlock-heading')).getText();
    }

    function do_assign() {
      element(by.name('assign-provider')).click();
    }

    function manually_select_provider() {
      element(by.cssContainingText('.Button.Button--secondary', 'Assign other provider manually')).click();
      element(by.cssContainingText('input[name="provider"] + strong', 'Duncan Lewis')).click();
    }

    describe('As Operator', function () {
      beforeEach(utils.setUp);

      it('should create a case as operator and assign (manually) to a provider', function () {
        browser.get(CONSTANTS.callcentreBaseUrl);

        modelsRecipe.Case.createReadyToAssign().then(function (case_ref) {
          case_to_accept = case_ref;
          browser.get(CONSTANTS.callcentreBaseUrl + case_ref + '/assign/?as_of=2014-08-06T11:50');
          get_provider().then(function (provider) {
            if (provider !== 'Duncan Lewis') {
              manually_select_provider();
            }
            do_assign();
          });
        });
      });

      it('should logout', function () {
        this.after(function () {
          utils.logout();
        });
      });
    });

    describe('As Provider', function () {
      beforeEach(utils.setUpAsProvider);

      it('should be able to accept a case', function(){
        browser.get(CONSTANTS.providerBaseUrl + case_to_accept + '/');

        expect(close_button.isDisplayed()).toBe(false);
        expect(accept_button.isPresent()).toBe(true);

        // click but cancel accept
        accept_button.click();

        // check can't accept anymore
        expect(accept_button.isDisplayed()).toBe(false);
        expect(close_button.isDisplayed()).toBe(true);
        expect(element(by.css('.NoticeContainer--fixed')).getInnerHtml()).toContain('Case accepted successfully');
      });

      it('should be able to close a case', function(){
        expect(close_button.isPresent()).toBe(true);

        // click but cancel accept
        close_button.click();
        browser.switchTo().alert().dismiss();

        expect(close_button.isPresent()).toBe(true);

        // click and accept case
        close_button.click();
        browser.switchTo().alert().accept();

        // check can't accept anymore
        expect(element(by.css('.NoticeContainer--fixed')).getInnerHtml()).toContain('Case ' + case_to_accept + ' closed successfully');
      });

      it('should logout', function () {
        this.after(function () {
          utils.logout();
        });
      });
    });
  });
})();
<commit_msg>Run full test suite - remove ddescribe<commit_after>(function(){
  'use strict';

  var utils = require('../e2e/_utils'),
      CONSTANTS = require('../protractor.constants.js'),
      modelsRecipe = require('./_modelsRecipe');

  describe('Provider Case Control', function() {
    var case_to_accept;
    var accept_button = element(by.name('accept-case'));
    var close_button = element(by.name('provider-close-case'));

    function get_provider() {
      return element(by.css('.ContactBlock-heading')).getText();
    }

    function do_assign() {
      element(by.name('assign-provider')).click();
    }

    function manually_select_provider() {
      element(by.cssContainingText('.Button.Button--secondary', 'Assign other provider manually')).click();
      element(by.cssContainingText('input[name="provider"] + strong', 'Duncan Lewis')).click();
    }

    describe('As Operator', function () {
      beforeEach(utils.setUp);

      it('should create a case as operator and assign (manually) to a provider', function () {
        browser.get(CONSTANTS.callcentreBaseUrl);

        modelsRecipe.Case.createReadyToAssign().then(function (case_ref) {
          case_to_accept = case_ref;
          browser.get(CONSTANTS.callcentreBaseUrl + case_ref + '/assign/?as_of=2014-08-06T11:50');
          get_provider().then(function (provider) {
            if (provider !== 'Duncan Lewis') {
              manually_select_provider();
            }
            do_assign();
          });
        });
      });

      it('should logout', function () {
        this.after(function () {
          utils.logout();
        });
      });
    });

    describe('As Provider', function () {
      beforeEach(utils.setUpAsProvider);

      it('should be able to accept a case', function(){
        browser.get(CONSTANTS.providerBaseUrl + case_to_accept + '/');

        expect(close_button.isDisplayed()).toBe(false);
        expect(accept_button.isPresent()).toBe(true);

        // click but cancel accept
        accept_button.click();

        // check can't accept anymore
        expect(accept_button.isDisplayed()).toBe(false);
        expect(close_button.isDisplayed()).toBe(true);
        expect(element(by.css('.NoticeContainer--fixed')).getInnerHtml()).toContain('Case accepted successfully');
      });

      it('should be able to close a case', function(){
        expect(close_button.isPresent()).toBe(true);

        // click but cancel accept
        close_button.click();
        browser.switchTo().alert().dismiss();

        expect(close_button.isPresent()).toBe(true);

        // click and accept case
        close_button.click();
        browser.switchTo().alert().accept();

        // check can't accept anymore
        expect(element(by.css('.NoticeContainer--fixed')).getInnerHtml()).toContain('Case ' + case_to_accept + ' closed successfully');
      });

      it('should logout', function () {
        this.after(function () {
          utils.logout();
        });
      });
    });
  });
})();
<|endoftext|>


<commit_before>/*
 * Copyright 2016, Lorenzo Mangani (lorenzo.mangani@gmail.com)
 * Copyright 2015, Rao Chenlin (rao.chenlin@gmail.com)
 *
 * This file is part of Sentinl (http://github.com/sirensolutions/sentinl)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import getElasticsearchClient from './get_elasticsearch_client';

var dynamicTemplates = [ {
  string_fields : {
    mapping : {
      type : 'string',
      index : 'not_analyzed',
      doc_values: true,
      fields : {
        search : {
          index : 'analyzed',
          omit_norms : true,
          type : 'string',
        }
      }
    },
    match_mapping_type : 'string',
    match : '*'
  }
}];

function createSentinlIndex(server, config) {
  server.log(['status', 'info', 'Sentinl'], 'Core Index check...');
  if (!server.plugins.elasticsearch) {
    server.log(['status', 'error', 'Sentinl'], 'Elasticsearch client not available, retrying in 5s');
    tryCreate(server);
    return;
  }

  var client = getElasticsearchClient(server);
  client.indices.exists({
    index: config.es.default_index
  }, function (error, exists) {
    if (exists === true) {
      server.log(['status', 'debug', 'Sentinl'], 'Core Index exists!');
      return;
    }
    server.log(['status', 'info', 'Sentinl'], 'Creating Sentinl core Index...');
    client.indices.create({
      index: config.es.default_index,
      body: {
        settings: {
          number_of_shards: 1,
          number_of_replicas: 1
        },
        mappings: {
          watch: {
            properties: {
              input: {
                type: 'object',
                enabled: false
              },
              transform: {
                type: 'object',
                enabled: false
              },
              condition: {
                type: 'object',
                enabled: false
              }
            }
          }
        }
      }
    })
    .then(function (resp) {
      server.log(['status', 'debug', 'Sentinl'], 'Core Index response', resp);
    }, function (err) {
      server.log(['status', 'warning', 'Sentinl'], err.message);
    });
  });
}

var tryCount = 0;
function tryCreate(server) {
  if (tryCount > 5) {
    server.log(['status', 'warning', 'Sentinl'], 'Failed creating Indices mapping!');
    return;
  }
  setTimeout(createSentinlIndex, 5000);
  tryCount++;
}

function createSentinlAlarmIndex(server,config) {
  server.log(['status', 'info', 'Sentinl'], 'Alarm Index check...');
  if (!server.plugins.elasticsearch) {
    server.log(['status', 'error', 'Sentinl'], 'Elasticsearch client not available, retrying in 5s');
    tryAlarmCreate(server);
    return;
  }

  var client = getElasticsearchClient(server);
  client.indices.exists({
    index: config.es.alarm_index
  }, function (error, exists) {
    if (exists === true) {
      server.log(['status', 'debug', 'Sentinl'], 'Alarms Index exists!');
      return;
    }
    server.log(['status', 'info', 'Sentinl'], 'Creating Sentinl Alarms Template...');
    client.indices.putTemplate({
      name: config.es.alarm_index,
      body: {
        template: config.es.alarm_index + "-*",
        mappings: {
          _default_: {
            properties: {
              payload: {
                type: 'object',
                enabled:  false
              },
              attachment : {
                type : 'binary'
              }
            }
          }
        }
      }
    })
    .then(function (resp) {
      server.log(['status', 'debug', 'Sentinl'], 'Alarm Template response', resp);
    }, function (err) {
      server.log(['error', 'warning', 'Sentinl'], err.message);
    });

    server.log(['status', 'info', 'Sentinl'], 'Creating Sentinl Alarms Index...');
    client.indices.create({
      index: config.es.alarm_index,
      body: {
        settings: {
          number_of_shards: 1,
          number_of_replicas: 1
        }
      }
    })
    .then(function (resp) {
      server.log(['status', 'debug', 'Sentinl'], 'Alarm Index response', resp);
    }, function (err) {
      server.log(['error', 'warning', 'Sentinl'], err.message);
    });
  });
}

var tryAlarmCount = 0;
function tryAlarmCreate(server) {
  if (tryCount > 5) {
    server.log(['error', 'warning', 'Sentinl'], 'Failed creating Alarm Indices mapping!');
    return;
  }
  setTimeout(createSentinlAlarmIndex, 5000);
  tryAlarmCount++;
}

module.exports = {
  createSentinlAlarmIndex: createSentinlAlarmIndex,
  createSentinlIndex: createSentinlIndex,
  tryCreate: tryCreate,
  tryAlarmCreate: tryAlarmCreate
};
<commit_msg>add bools to mapping template<commit_after>/*
 * Copyright 2016, Lorenzo Mangani (lorenzo.mangani@gmail.com)
 * Copyright 2015, Rao Chenlin (rao.chenlin@gmail.com)
 *
 * This file is part of Sentinl (http://github.com/sirensolutions/sentinl)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import getElasticsearchClient from './get_elasticsearch_client';

var dynamicTemplates = [ {
  string_fields : {
    mapping : {
      type : 'string',
      index : 'not_analyzed',
      doc_values: true,
      fields : {
        search : {
          index : 'analyzed',
          omit_norms : true,
          type : 'string',
        }
      }
    },
    match_mapping_type : 'string',
    match : '*'
  }
}];

function createSentinlIndex(server, config) {
  server.log(['status', 'info', 'Sentinl'], 'Core Index check...');
  if (!server.plugins.elasticsearch) {
    server.log(['status', 'error', 'Sentinl'], 'Elasticsearch client not available, retrying in 5s');
    tryCreate(server);
    return;
  }

  var client = getElasticsearchClient(server);
  client.indices.exists({
    index: config.es.default_index
  }, function (error, exists) {
    if (exists === true) {
      server.log(['status', 'debug', 'Sentinl'], 'Core Index exists!');
      return;
    }
    server.log(['status', 'info', 'Sentinl'], 'Creating Sentinl core Index...');
    client.indices.create({
      index: config.es.default_index,
      body: {
        settings: {
          number_of_shards: 1,
          number_of_replicas: 1
        },
        mappings: {
          watch: {
            properties: {
              input: {
                type: 'object',
                enabled: false
              },
              transform: {
                type: 'object',
                enabled: false
              },
              condition: {
                type: 'object',
                enabled: false
              },
              uuid: {
                type:  'string',
                index: 'not_analyzed'
              },
              report: {
                type: 'boolean'
              },
              disable: {
                 type: 'boolean'
              }
            }
          }
        }
      }
    })
    .then(function (resp) {
      server.log(['status', 'debug', 'Sentinl'], 'Core Index response', resp);
    }, function (err) {
      server.log(['status', 'warning', 'Sentinl'], err.message);
    });
  });
}

var tryCount = 0;
function tryCreate(server) {
  if (tryCount > 5) {
    server.log(['status', 'warning', 'Sentinl'], 'Failed creating Indices mapping!');
    return;
  }
  setTimeout(createSentinlIndex, 5000);
  tryCount++;
}

function createSentinlAlarmIndex(server,config) {
  server.log(['status', 'info', 'Sentinl'], 'Alarm Index check...');
  if (!server.plugins.elasticsearch) {
    server.log(['status', 'error', 'Sentinl'], 'Elasticsearch client not available, retrying in 5s');
    tryAlarmCreate(server);
    return;
  }

  var client = getElasticsearchClient(server);
  client.indices.exists({
    index: config.es.alarm_index
  }, function (error, exists) {
    if (exists === true) {
      server.log(['status', 'debug', 'Sentinl'], 'Alarms Index exists!');
      return;
    }
    server.log(['status', 'info', 'Sentinl'], 'Creating Sentinl Alarms Template...');
    client.indices.putTemplate({
      name: config.es.alarm_index,
      body: {
        template: config.es.alarm_index + "-*",
        mappings: {
          _default_: {
            properties: {
              payload: {
                type: 'object',
                enabled:  false
              },
              attachment : {
                type : 'binary'
              },
              uuid: {
                type:  'string',
                index: 'not_analyzed'
              }
            }
          }
        }
      }
    })
    .then(function (resp) {
      server.log(['status', 'debug', 'Sentinl'], 'Alarm Template response', resp);
    }, function (err) {
      server.log(['error', 'warning', 'Sentinl'], err.message);
    });

    server.log(['status', 'info', 'Sentinl'], 'Creating Sentinl Alarms Index...');
    client.indices.create({
      index: config.es.alarm_index,
      body: {
        settings: {
          number_of_shards: 1,
          number_of_replicas: 1
        }
      }
    })
    .then(function (resp) {
      server.log(['status', 'debug', 'Sentinl'], 'Alarm Index response', resp);
    }, function (err) {
      server.log(['error', 'warning', 'Sentinl'], err.message);
    });
  });
}

var tryAlarmCount = 0;
function tryAlarmCreate(server) {
  if (tryCount > 5) {
    server.log(['error', 'warning', 'Sentinl'], 'Failed creating Alarm Indices mapping!');
    return;
  }
  setTimeout(createSentinlAlarmIndex, 5000);
  tryAlarmCount++;
}

module.exports = {
  createSentinlAlarmIndex: createSentinlAlarmIndex,
  createSentinlIndex: createSentinlIndex,
  tryCreate: tryCreate,
  tryAlarmCreate: tryAlarmCreate
};
<|endoftext|>


<commit_before>'use strict'

const { Schema } = require('mongoose')
const enums = require('../../specs/enums.json')
const { get, map, filter, identity } = require('lodash/fp')
const { getMeta } = require('./specs')
const memoize = require('memoizee')

// TODO use proper logger
const debug = require('debug')('isari:schema')
const chalk = require('chalk')
const util = require('util')

const RESERVED_FIELDS = [
	// Isari fields
	'accessType',
	'label',
	'requirement',
	'accessMonitoring',
	'suggestions',
	'description',
	'comment',
	'service',
	'softenum',
	'template',
	// Schema fields
	'type',
	'enum',
	'ref',
	'regex',
	'default',
	'min',
	'max',
	// Additional generated technical fields
	'multiple'
]

const FRONT_KEPT_FIELDS = [
	'label',
	'requirement',
	'suggestions',
	'type',
	'ref',
	'enum',
	'softenum',
	'default',
	'min',
	'max',
	'regex',
	'multiple'
]

module.exports = {
	getMongooseSchema: memoize(getMongooseSchema),
	getFrontSchema: memoize(getFrontSchema),
	RESERVED_FIELDS,
	FRONT_KEPT_FIELDS
}

const extractValue = map('value')
const removeEmpty = filter(identity)

// Get schema description from metadata
function getMongooseSchema (name) {
	const meta = getMeta(name)
	if (!meta) {
		throw Error(`${name}: Unknonwn schema`)
	}

	return getField(name, meta, meta)
}

// Get schema for a field or sub-field…
function getField (name, meta, parentDesc, rootDesc = null) {
	debug(`${name}: Normal field`)

	const isArray = Array.isArray(meta)
	const desc = isArray ? meta[0] : meta

	// All non-reserved fields are considered subfields
	const subFields = Object.keys(desc).filter(subField => {
		if (RESERVED_FIELDS.includes(subField)) {
			debug(`${name}: Reserved field ${subField}`)
			return false
		} else if (subField.substring(0, 2) === '//') {
			debug(`${name}: Ignored comment field ${subField}`)
			return false
		} else {
			return true
		}
	})
	const isDocument = subFields.length > 0

	// A pointer to root desc is required as we'll store some additional information like hooks
	if (!rootDesc) {
		rootDesc = desc
	}

	// Field description, we expect some fields and ignore others
	// Other unknown field names will be treated as sub-fields
	let schema = {}

	// If it's a document, do not set "type", "required", or any other field-related configuration
	// Just define sub-fields and finish
	if (isDocument) {
		subFields.forEach(subField => {
			schema[subField] = getField(`${name}.${subField}`, desc[subField], desc)
		})
		return isArray ? [schema] : schema
	}

	// Check 'type'
	const type = desc.type || (desc.ref ? 'ref' : 'string')
	if (desc.ref && type !== 'ref') {
		throw Error(`${name}: Invalid type "${type}" conflicting with ref field`)
	}

	// Required?
	schema.required = desc.requirement === 'mandatory'

	// Set Mongoose type
	if (type === 'string' || type === 'text') {
		schema.type = String
	} else if (type === 'bool' || type === 'boolean') {
		schema.type = Boolean
	} else if (type === 'number') {
		schema.type = Number
	} else if (type === 'date') {
		// Special type date, not translated into Date because we want support for partial dates
		schema.type = String
		schema.match = /^[12][0-9]{3}(?:-(?:0?[1-9]|1[0-2]))?(?:-(?:0?[1-9]|[12]\d|3[01]))?$/
	} else if (type === 'ref') {
		schema.type = Schema.Types.ObjectId
		if (!desc.ref) {
			throw Error(`${name}: No ref defined for a ref field`)
		}
		schema.ref = desc.ref
	} else if (type === 'email') {
		schema.type = String
		schema.match = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
	} else {
		throw Error(`${name}: Unknown type "${type}"`)
	}

	// Validation rule: enum
	// Note: softenum does not come with any validation, it's only about suggestions
	if (typeof desc.enum === 'string') {
		// As a string: enums key or complex rule "KEYS(name)" or "name.$field"
		const matchKeys = desc.enum.match(/^KEYS\((.*)\)$/)
		const matchDot = desc.enum.match(/^(.*?)\.\$(.*)$/)
		const getKeys = !!matchKeys
		const getSubKey = !!matchDot
		const subKey = getSubKey ? matchDot[2] : null
		const enumName = getKeys ? matchKeys[1] : getSubKey ? matchDot[1] : desc.enum

		const values = getEnumValues(enumName)
		if (!values) {
			throw Error(`${name}: Unknown enum "${enumName}" (in "${desc.enum}")`)
		}

		if (subKey && !parentDesc[subKey]) {
			throw Error(`${name}: Unknown field "${subKey}" (required by enum "${desc.enum}"`)
		}

		if (getSubKey) {
			// Context-dependent enum validation: use a custom validator
			if (typeof values !== 'object') {
				throw Error(`${name}: context-dependent enum must be an object (in "${desc.enum}")`)
			}
			const getRefValue = get(subKey)
			// 'function' is used on purpose, "this" will be defined as the validated document
			// in case of sub-documents, it's the sub-document (not the root document)
			// we can go up using "this.parent()" (behavior not implemented in current schema DSL)
			const validator = function (value) {
				// Allow empty value?
				if (!schema.required && !value) {
					return true
				}
				// Beware of 'runValidators' on update methods, as "this" will not be defined then
				// More info: http://mongoosejs.com/docs/api.html#schematype_SchemaType-validate
				if (!this) {
					process.stderr.write(chalk.yellow(`${name}: validator cannot be run in update context (enum "${desc.enum}")`))
					// Just pass
					return true
				}
				// Now the usual case
				const refValue = getRefValue(this)
				const allowedValues = values[refValue]
				if (!Array.isArray(allowedValues)) {
					process.stderr.write(chalk.yellow(`${name}: no values found for enum ${enumName}.${refValue}`))
					return false
				}
				return allowedValues.includes(value)
			}
			const message = `{PATH} does not allow "{VALUE}" as of enum "${desc.enum}"`
			schema.validate = { validator, message }
		} else {
			// Use basic enum validation
			schema.enum = getKeys ? Object.keys(values) : values
		}
	} else if (Array.isArray(desc.enum)) {
		// As an array: direct values not exported into enums module
		schema.enum = getEnumValues(desc.enum)
	} else if (desc.enum) {
		throw Error(`${name}: Invalid enum value "${desc.enum}"`)
	}

	// Basic enum validation set? Add support for empty values if field is not required
	if (schema.enum && !schema.required) {
		schema.enum = schema.enum.slice().concat([ '', null ])
	}

	// Validation rule: regex
	if (desc.regex) {
		schema.match = new RegExp(desc.regex)
	}

	// Simple fields that need no translation
	['default', 'min', 'max'].forEach(k => {
		if (desc[k]) {
			schema[k] = desc[k]
		}
	})

	return isArray ? [schema] : schema
}

function getEnumValues (zenum) {
	if (typeof zenum === 'string') {
		return getEnumValues(enums[zenum])
	}

	if (Array.isArray(zenum) && typeof zenum[0] === 'object') {
		// Array of object, grab 'value' field
		return removeEmpty(extractValue(zenum))
	} else {
		// Array of string or object, keep as-is
		return zenum
	}
}

// Formatting for frontend APIs
function getFrontSchema (name, options = {}) {
	const meta = getMeta(name)

	return meta && formatMeta(meta, options)
}

function formatMeta (meta, options = {}) {
	const { admin = false } = options

	const multiple = Array.isArray(meta)
	const desc = multiple ? meta[0] : meta
	let isObject = false

	const handleField = (result, name) => {
		if (name.substring(0, 2) === '//') {
			return false // Skipped comment field
		}

		if (!result) {
			return result // Skipped field
		}

		if (!RESERVED_FIELDS.includes(name)) {
			// Sub-field: just include it
			isObject = true
			const subres = formatMeta(desc[name], options)
			if (subres) {
				result[name] = subres
			}
		}

		// Access type defines if we can see this field
		else if (name === 'accessType' && !admin) {
			result = null // Skip the field
		}

		// Field kept as-is
		else if (FRONT_KEPT_FIELDS.includes(name)) {
			result[name] = desc[name]
		}

		// Other reserved fields are skipped

		return result
	}

	let result = Object.keys(desc).reduce(handleField, {})
	if (!result) {
		// Skip this field
		return result
	}

	if (isObject) {
		if (result.type) {
			process.stderr.write(chalk.yellow(`\n[WARN] type "${result.type}" defined on an object document?\n`))
			process.stderr.write(util.inspect(result, { colors: true }))
		}
		result.type = 'object'
	}

	if (!result.type) {
		result.type = 'string'
	}

	// Handle multi-valued fields
	if (multiple) {
		result.multiple = true
	}

	return result
}
<commit_msg>fix(schemas): adds 'description' to front schemas & layouts<commit_after>'use strict'

const { Schema } = require('mongoose')
const enums = require('../../specs/enums.json')
const { get, map, filter, identity } = require('lodash/fp')
const { getMeta } = require('./specs')
const memoize = require('memoizee')

// TODO use proper logger
const debug = require('debug')('isari:schema')
const chalk = require('chalk')
const util = require('util')

const RESERVED_FIELDS = [
	// Isari fields
	'accessType',
	'label',
	'requirement',
	'accessMonitoring',
	'suggestions',
	'description',
	'comment',
	'service',
	'softenum',
	'template',
	// Schema fields
	'type',
	'enum',
	'ref',
	'regex',
	'default',
	'min',
	'max',
	// Additional generated technical fields
	'multiple'
]

const FRONT_KEPT_FIELDS = [
	'label',
	'requirement',
	'suggestions',
	'description',
	'type',
	'ref',
	'enum',
	'softenum',
	'default',
	'min',
	'max',
	'regex',
	'multiple'
]

module.exports = {
	getMongooseSchema: memoize(getMongooseSchema),
	getFrontSchema: memoize(getFrontSchema),
	RESERVED_FIELDS,
	FRONT_KEPT_FIELDS
}

const extractValue = map('value')
const removeEmpty = filter(identity)

// Get schema description from metadata
function getMongooseSchema (name) {
	const meta = getMeta(name)
	if (!meta) {
		throw Error(`${name}: Unknonwn schema`)
	}

	return getField(name, meta, meta)
}

// Get schema for a field or sub-field…
function getField (name, meta, parentDesc, rootDesc = null) {
	debug(`${name}: Normal field`)

	const isArray = Array.isArray(meta)
	const desc = isArray ? meta[0] : meta

	// All non-reserved fields are considered subfields
	const subFields = Object.keys(desc).filter(subField => {
		if (RESERVED_FIELDS.includes(subField)) {
			debug(`${name}: Reserved field ${subField}`)
			return false
		} else if (subField.substring(0, 2) === '//') {
			debug(`${name}: Ignored comment field ${subField}`)
			return false
		} else {
			return true
		}
	})
	const isDocument = subFields.length > 0

	// A pointer to root desc is required as we'll store some additional information like hooks
	if (!rootDesc) {
		rootDesc = desc
	}

	// Field description, we expect some fields and ignore others
	// Other unknown field names will be treated as sub-fields
	let schema = {}

	// If it's a document, do not set "type", "required", or any other field-related configuration
	// Just define sub-fields and finish
	if (isDocument) {
		subFields.forEach(subField => {
			schema[subField] = getField(`${name}.${subField}`, desc[subField], desc)
		})
		return isArray ? [schema] : schema
	}

	// Check 'type'
	const type = desc.type || (desc.ref ? 'ref' : 'string')
	if (desc.ref && type !== 'ref') {
		throw Error(`${name}: Invalid type "${type}" conflicting with ref field`)
	}

	// Required?
	schema.required = desc.requirement === 'mandatory'

	// Set Mongoose type
	if (type === 'string' || type === 'text') {
		schema.type = String
	} else if (type === 'bool' || type === 'boolean') {
		schema.type = Boolean
	} else if (type === 'number') {
		schema.type = Number
	} else if (type === 'date') {
		// Special type date, not translated into Date because we want support for partial dates
		schema.type = String
		schema.match = /^[12][0-9]{3}(?:-(?:0?[1-9]|1[0-2]))?(?:-(?:0?[1-9]|[12]\d|3[01]))?$/
	} else if (type === 'ref') {
		schema.type = Schema.Types.ObjectId
		if (!desc.ref) {
			throw Error(`${name}: No ref defined for a ref field`)
		}
		schema.ref = desc.ref
	} else if (type === 'email') {
		schema.type = String
		schema.match = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
	} else {
		throw Error(`${name}: Unknown type "${type}"`)
	}

	// Validation rule: enum
	// Note: softenum does not come with any validation, it's only about suggestions
	if (typeof desc.enum === 'string') {
		// As a string: enums key or complex rule "KEYS(name)" or "name.$field"
		const matchKeys = desc.enum.match(/^KEYS\((.*)\)$/)
		const matchDot = desc.enum.match(/^(.*?)\.\$(.*)$/)
		const getKeys = !!matchKeys
		const getSubKey = !!matchDot
		const subKey = getSubKey ? matchDot[2] : null
		const enumName = getKeys ? matchKeys[1] : getSubKey ? matchDot[1] : desc.enum

		const values = getEnumValues(enumName)
		if (!values) {
			throw Error(`${name}: Unknown enum "${enumName}" (in "${desc.enum}")`)
		}

		if (subKey && !parentDesc[subKey]) {
			throw Error(`${name}: Unknown field "${subKey}" (required by enum "${desc.enum}"`)
		}

		if (getSubKey) {
			// Context-dependent enum validation: use a custom validator
			if (typeof values !== 'object') {
				throw Error(`${name}: context-dependent enum must be an object (in "${desc.enum}")`)
			}
			const getRefValue = get(subKey)
			// 'function' is used on purpose, "this" will be defined as the validated document
			// in case of sub-documents, it's the sub-document (not the root document)
			// we can go up using "this.parent()" (behavior not implemented in current schema DSL)
			const validator = function (value) {
				// Allow empty value?
				if (!schema.required && !value) {
					return true
				}
				// Beware of 'runValidators' on update methods, as "this" will not be defined then
				// More info: http://mongoosejs.com/docs/api.html#schematype_SchemaType-validate
				if (!this) {
					process.stderr.write(chalk.yellow(`${name}: validator cannot be run in update context (enum "${desc.enum}")`))
					// Just pass
					return true
				}
				// Now the usual case
				const refValue = getRefValue(this)
				const allowedValues = values[refValue]
				if (!Array.isArray(allowedValues)) {
					process.stderr.write(chalk.yellow(`${name}: no values found for enum ${enumName}.${refValue}`))
					return false
				}
				return allowedValues.includes(value)
			}
			const message = `{PATH} does not allow "{VALUE}" as of enum "${desc.enum}"`
			schema.validate = { validator, message }
		} else {
			// Use basic enum validation
			schema.enum = getKeys ? Object.keys(values) : values
		}
	} else if (Array.isArray(desc.enum)) {
		// As an array: direct values not exported into enums module
		schema.enum = getEnumValues(desc.enum)
	} else if (desc.enum) {
		throw Error(`${name}: Invalid enum value "${desc.enum}"`)
	}

	// Basic enum validation set? Add support for empty values if field is not required
	if (schema.enum && !schema.required) {
		schema.enum = schema.enum.slice().concat([ '', null ])
	}

	// Validation rule: regex
	if (desc.regex) {
		schema.match = new RegExp(desc.regex)
	}

	// Simple fields that need no translation
	['default', 'min', 'max'].forEach(k => {
		if (desc[k]) {
			schema[k] = desc[k]
		}
	})

	return isArray ? [schema] : schema
}

function getEnumValues (zenum) {
	if (typeof zenum === 'string') {
		return getEnumValues(enums[zenum])
	}

	if (Array.isArray(zenum) && typeof zenum[0] === 'object') {
		// Array of object, grab 'value' field
		return removeEmpty(extractValue(zenum))
	} else {
		// Array of string or object, keep as-is
		return zenum
	}
}

// Formatting for frontend APIs
function getFrontSchema (name, options = {}) {
	const meta = getMeta(name)

	return meta && formatMeta(meta, options)
}

function formatMeta (meta, options = {}) {
	const { admin = false } = options

	const multiple = Array.isArray(meta)
	const desc = multiple ? meta[0] : meta
	let isObject = false

	const handleField = (result, name) => {
		if (name.substring(0, 2) === '//') {
			return false // Skipped comment field
		}

		if (!result) {
			return result // Skipped field
		}

		if (!RESERVED_FIELDS.includes(name)) {
			// Sub-field: just include it
			isObject = true
			const subres = formatMeta(desc[name], options)
			if (subres) {
				result[name] = subres
			}
		}

		// Access type defines if we can see this field
		else if (name === 'accessType' && !admin) {
			result = null // Skip the field
		}

		// Field kept as-is
		else if (FRONT_KEPT_FIELDS.includes(name)) {
			result[name] = desc[name]
		}

		// Other reserved fields are skipped

		return result
	}

	let result = Object.keys(desc).reduce(handleField, {})
	if (!result) {
		// Skip this field
		return result
	}

	if (isObject) {
		if (result.type) {
			process.stderr.write(chalk.yellow(`\n[WARN] type "${result.type}" defined on an object document?\n`))
			process.stderr.write(util.inspect(result, { colors: true }))
		}
		result.type = 'object'
	}

	if (!result.type) {
		result.type = 'string'
	}

	// Handle multi-valued fields
	if (multiple) {
		result.multiple = true
	}

	return result
}
<|endoftext|>


<commit_before>import config from 'config';
import Promise from 'bluebird';
import Twitter from 'twitter';
import activityType from '../constants/activities';
import models from '../models';
import { get } from 'lodash';
import debugLib from 'debug';
const debug = debugLib('twitter');
import { formatCurrency } from '../lib/utils';
import IntlMessageFormat from 'intl-messageformat';

const tweetUpdate = async activity => {
  const tweet = twitterLib.compileTweet('updatePublished', {
    title: activity.data.update.title,
  });
  const twitterAccount = await models.ConnectedAccount.findOne({
    where: { CollectiveId: activity.CollectiveId, service: 'twitter' },
  });
  if (!twitterAccount) {
    debug('no twitter account associated to ', activity.CollectiveId);
    return;
  }
  twitterAccount.settings = twitterAccount.settings || {};
  const settings = twitterAccount.settings['updatePublished'] || {};
  if (!settings.active) {
    debug('updatePublished.active false', settings);
    return;
  }

  twitterLib.tweetStatus(twitterAccount, tweet, activity.data.url);
};

const tweetNewMember = async activity => {
  if (get(activity, 'data.member.role') !== 'BACKER') {
    debug('skipping', activity.type, get(activity, 'data.member.role'));
    return;
  }

  if (!get(activity, 'data.member.memberCollective.twitterHandle')) {
    debug('skipping', 'no twitter handle for ', get(activity, 'data.member.memberCollective.slug'));
    return;
  }

  const twitterAccount = await models.ConnectedAccount.findOne({
    where: { CollectiveId: activity.CollectiveId, service: 'twitter' },
  });
  if (!twitterAccount) {
    debug('no twitter account associated to ', activity.CollectiveId);
    return;
  }
  debug(twitterAccount.settings);
  twitterAccount.settings = twitterAccount.settings || {};
  const settings = twitterAccount.settings['newBacker'] || {};
  if (!settings.active) {
    debug('newBacker.active false', settings);
    return;
  }

  const template = settings.tweet;

  // todo: we should use the handlebar templating system to support {{#if}}{{/if}}
  const status = template
    .replace('{backerTwitterHandle}', `@${get(activity, 'data.member.memberCollective.twitterHandle')}`)
    .replace('{referralTwitterHandle}', `@${get(activity, 'data.order.referral.twitterHandle')}`)
    .replace('{amount}', formatCurrency(get(activity, 'data.order.totalAmount'), get(activity, 'data.order.currency')));

  return await twitterLib.tweetStatus(
    twitterAccount,
    status,
    `https://opencollective.com/${get(activity, 'data.collective.slug')}`,
  );
};

const tweetActivity = async activity => {
  debug('>>> tweetActivity', activity.type);
  debug('>>> tweetActivity.data', JSON.stringify(activity.data));
  switch (activity.type) {
    case activityType.COLLECTIVE_MEMBER_CREATED:
      return tweetNewMember(activity);

    case activityType.COLLECTIVE_UPDATE_PUBLISHED:
      return tweetUpdate(activity);
  }
};

const tweetStatus = (twitterAccount, status, url, options = {}) => {
  // collectives without twitter credentials are ignored
  if (!twitterAccount) {
    debug('>>> tweetStatus: no twitter account connected');
    return;
  }

  const client = new Twitter({
    consumer_key: config.twitter.consumerKey,
    consumer_secret: config.twitter.consumerSecret,
    access_token_key: twitterAccount.clientId,
    access_token_secret: twitterAccount.token,
  });

  if (url) {
    status += `\n${url}`;
  }

  debug('tweeting status: ', status, 'with options:', options);
  if (config.twitter.consumerSecret) {
    const tweet = Promise.promisify(client.post, { context: client });
    return tweet('statuses/update', { status, ...options });
  } else {
    console.log('Tweet not sent: no twitter key/secret provided in env');
    return Promise.resolve();
  }
};

const compileTweet = (template, data, message) => {
  const messages = {
    'en-US': {
      tenBackers: `🎉 {collective} just reached 10 backers! Thank you {topBackersTwitterHandles} 🙌
Support them too!`,
      oneHundred: `🎉 {collective} just reached 100 backers!! 🙌
Support them too!`,
      oneThousandBackers: `🎉 {collective} just reached 1,0000 backers!!! 🙌
Support them too!`,
      updatePublished: 'Latest update from the collective: {title}',
      monthlyStats: `In {month}, {totalNewBackers, select,
  0 {no new backer joined. 😑}
  1 {one new backer joined.}
  other {{totalNewBackers} {totalNewBackers, plural, one {backer} other {backers}} joined ({newBackersTwitterHandles}) - you are the best! 🙌 }
}

We received {totalAmountReceived} from {totalActiveBackers} {totalActiveBackers, plural, one {backer} other {backers}} and we spent {topExpenseCategories, select,
  none {{totalAmountSpent}}
  other {{totalAmountSpent} on {topExpenseCategories}}
}. Our current balance is {balance}.

Top backers: {topBackersTwitterHandles}`,
      monthlyStatsNoNewDonation: `In {month}, we haven't received any new donation. 😑

Our current balance is {balance}.

Become a backer! 😃`,
    },
  };

  if (message) {
    messages['en-US'][template] = message;
  }

  if (!messages['en-US'][template]) {
    console.error('Invalid tweet template', template);
    return;
  }

  const thankyou = '\n\nThank you! 🙏';

  const compiled = new IntlMessageFormat(messages['en-US'][template], 'en-US');
  let tweet = compiled.format(data);

  if (template === 'monthlyStats') {
    // A URL always takes 23 chars (+ space)
    if (tweet.length < 280 - 24 - thankyou.length) {
      tweet += thankyou;
    }
  }
  return tweet;
};

const twitterLib = {
  tweetActivity,
  tweetStatus,
  compileTweet,
};

export default twitterLib;
<commit_msg>fix: twitter config<commit_after>import config from 'config';
import Promise from 'bluebird';
import Twitter from 'twitter';
import IntlMessageFormat from 'intl-messageformat';
import debugLib from 'debug';
import { has, get } from 'lodash';

import models from '../models';
import { formatCurrency } from '../lib/utils';
import activityType from '../constants/activities';

const debug = debugLib('twitter');

const tweetUpdate = async activity => {
  const tweet = twitterLib.compileTweet('updatePublished', {
    title: activity.data.update.title,
  });
  const twitterAccount = await models.ConnectedAccount.findOne({
    where: { CollectiveId: activity.CollectiveId, service: 'twitter' },
  });
  if (!twitterAccount) {
    debug('no twitter account associated to ', activity.CollectiveId);
    return;
  }
  twitterAccount.settings = twitterAccount.settings || {};
  const settings = twitterAccount.settings['updatePublished'] || {};
  if (!settings.active) {
    debug('updatePublished.active false', settings);
    return;
  }

  twitterLib.tweetStatus(twitterAccount, tweet, activity.data.url);
};

const tweetNewMember = async activity => {
  if (get(activity, 'data.member.role') !== 'BACKER') {
    debug('skipping', activity.type, get(activity, 'data.member.role'));
    return;
  }

  if (!get(activity, 'data.member.memberCollective.twitterHandle')) {
    debug('skipping', 'no twitter handle for ', get(activity, 'data.member.memberCollective.slug'));
    return;
  }

  const twitterAccount = await models.ConnectedAccount.findOne({
    where: { CollectiveId: activity.CollectiveId, service: 'twitter' },
  });
  if (!twitterAccount) {
    debug('no twitter account associated to ', activity.CollectiveId);
    return;
  }
  debug(twitterAccount.settings);
  twitterAccount.settings = twitterAccount.settings || {};
  const settings = twitterAccount.settings['newBacker'] || {};
  if (!settings.active) {
    debug('newBacker.active false', settings);
    return;
  }

  const template = settings.tweet;

  // todo: we should use the handlebar templating system to support {{#if}}{{/if}}
  const status = template
    .replace('{backerTwitterHandle}', `@${get(activity, 'data.member.memberCollective.twitterHandle')}`)
    .replace('{referralTwitterHandle}', `@${get(activity, 'data.order.referral.twitterHandle')}`)
    .replace('{amount}', formatCurrency(get(activity, 'data.order.totalAmount'), get(activity, 'data.order.currency')));

  return await twitterLib.tweetStatus(
    twitterAccount,
    status,
    `https://opencollective.com/${get(activity, 'data.collective.slug')}`,
  );
};

const tweetActivity = async activity => {
  debug('>>> tweetActivity', activity.type);
  debug('>>> tweetActivity.data', JSON.stringify(activity.data));
  switch (activity.type) {
    case activityType.COLLECTIVE_MEMBER_CREATED:
      return tweetNewMember(activity);

    case activityType.COLLECTIVE_UPDATE_PUBLISHED:
      return tweetUpdate(activity);
  }
};

const tweetStatus = (twitterAccount, status, url, options = {}) => {
  // collectives without twitter credentials are ignored
  if (!twitterAccount) {
    debug('>>> tweetStatus: no twitter account connected');
    return;
  }

  if (url) {
    status += `\n${url}`;
  }

  debug('tweeting status: ', status, 'with options:', options);
  if (has(config, 'twitter.consumerKey') && has(config, 'twitter.consumerSecret')) {
    const client = new Twitter({
      consumer_key: get(config, 'twitter.consumerKey'),
      consumer_secret: get(config, 'twitter.consumerSecret'),
      access_token_key: twitterAccount.clientId,
      access_token_secret: twitterAccount.token,
    });

    const tweet = Promise.promisify(client.post, { context: client });
    return tweet('statuses/update', { status, ...options });
  } else {
    console.log('Tweet not sent: no twitter key/secret provided in env');
    return Promise.resolve();
  }
};

const compileTweet = (template, data, message) => {
  const messages = {
    'en-US': {
      tenBackers: `🎉 {collective} just reached 10 backers! Thank you {topBackersTwitterHandles} 🙌
Support them too!`,
      oneHundred: `🎉 {collective} just reached 100 backers!! 🙌
Support them too!`,
      oneThousandBackers: `🎉 {collective} just reached 1,0000 backers!!! 🙌
Support them too!`,
      updatePublished: 'Latest update from the collective: {title}',
      monthlyStats: `In {month}, {totalNewBackers, select,
  0 {no new backer joined. 😑}
  1 {one new backer joined.}
  other {{totalNewBackers} {totalNewBackers, plural, one {backer} other {backers}} joined ({newBackersTwitterHandles}) - you are the best! 🙌 }
}

We received {totalAmountReceived} from {totalActiveBackers} {totalActiveBackers, plural, one {backer} other {backers}} and we spent {topExpenseCategories, select,
  none {{totalAmountSpent}}
  other {{totalAmountSpent} on {topExpenseCategories}}
}. Our current balance is {balance}.

Top backers: {topBackersTwitterHandles}`,
      monthlyStatsNoNewDonation: `In {month}, we haven't received any new donation. 😑

Our current balance is {balance}.

Become a backer! 😃`,
    },
  };

  if (message) {
    messages['en-US'][template] = message;
  }

  if (!messages['en-US'][template]) {
    console.error('Invalid tweet template', template);
    return;
  }

  const thankyou = '\n\nThank you! 🙏';

  const compiled = new IntlMessageFormat(messages['en-US'][template], 'en-US');
  let tweet = compiled.format(data);

  if (template === 'monthlyStats') {
    // A URL always takes 23 chars (+ space)
    if (tweet.length < 280 - 24 - thankyou.length) {
      tweet += thankyou;
    }
  }
  return tweet;
};

const twitterLib = {
  tweetActivity,
  tweetStatus,
  compileTweet,
};

export default twitterLib;
<|endoftext|>


<commit_before>
UpdatesAvailable = {};
UpdatesApplied = new Meteor.Collection("UpdatesApplied");

applyUpdates = function () {
	for (name in UpdatesAvailable) {
		if (UpdatesApplied.find({ name: name }).count() == 0) {
			console.log("Applying update " + name);
			UpdatesAvailable[name]();
			UpdatesApplied.insert({ name: name, applied: new Date() });
		}
	}
};<commit_msg>count afected documents on updates<commit_after>
UpdatesAvailable = {};
UpdatesApplied = new Meteor.Collection("UpdatesApplied");

applyUpdates = function () {
	for (name in UpdatesAvailable) {
		if (UpdatesApplied.find({ name: name }).count() == 0) {
			console.log("Applying update " + name);
			var affected = UpdatesAvailable[name]();
			console.log(''+affected+" affected documents");
			UpdatesApplied.insert({ name: name, applied: new Date(), affected: affected });
		}
	}
};
<|endoftext|>


<commit_before>
UpdatesAvailable = {};
UpdatesApplied = new Meteor.Collection("UpdatesApplied");

applyUpdates = function () {
	var skipInitial = UpdatesApplied.find().count() === 0;

	for (var name in UpdatesAvailable) {
		if (UpdatesApplied.find({ name: name }).count() === 0) {
			var entry =
				{ name: name
				, affected: 0
				, run: new Date()
				};

			if (skipInitial) {
				console.log("Skipping update " + name);
			} else {
				console.log("Applying update " + name);
				entry.affected = UpdatesAvailable[name]();
				entry.applied = new Date();
				entry.affected = affected;
				console.log(name + ": " + affected + " affected documents");
			}
			UpdatesApplied.insert(entry);
		}
	}
};
<commit_msg>fix update error<commit_after>
UpdatesAvailable = {};
UpdatesApplied = new Meteor.Collection("UpdatesApplied");

applyUpdates = function () {
	var skipInitial = UpdatesApplied.find().count() === 0;

	for (var name in UpdatesAvailable) {
		if (UpdatesApplied.find({ name: name }).count() === 0) {
			var entry =
				{ name: name
				, affected: 0
				, run: new Date()
				};

			if (skipInitial) {
				console.log("Skipping update " + name);
			} else {
				console.log("Applying update " + name);
				entry.affected = UpdatesAvailable[name]();
				entry.applied = new Date();
				console.log(name + ": " + affected + " affected documents");
			}
			UpdatesApplied.insert(entry);
		}
	}
};
<|endoftext|>


<commit_before>//
//   Copyright 2009-2014 Ilkka Oksanen <iao@iki.fi>
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing,
//   software distributed under the License is distributed on an "AS
//   IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//   express or implied.  See the License for the specific language
//   governing permissions and limitations under the License.
//

'use strict';

var wrapper = require('co-redis'),
    redis = wrapper(require('redis').createClient()),
    log = require('../../lib/log');

exports.getWindowIdsForNetwork = function *(userId, network) {
    var ids = yield getWindowIds(userId, network, null, 'id');

    return ids;
};

exports.getWindowId = function *(userId, network, group) {
    var ids = yield getWindowIds(userId, network, group, 'id');

    if (ids.length === 1) {
        return ids[0];
    } else {
        log.warn(userId, 'Tried to find non-existing group: ' + group);
        return null;
    }
};

exports.getWindowNameAndNetwork = function *(userId, windowId) {
    var windows = yield redis.smembers('windowlist:' + userId);

    for (var i = 0; i < windows.length; i++) {
        var details = windows[i].split(':');
        if (details[0] === windowId) {
            return [ details[2],  details[1] ];
        }
    }

    return null;
}

exports.getWindowNamesForNetwork = function *(userId, network) {
    var ids = yield getWindowIds(userId, network, null, 'name');

    return ids;
};

exports.getNetworks = function *(userId) {
    var networks = {};

    var windows = yield redis.smembers('windowlist:' + userId);
    for (var i = 0; i < windows.length; i++) {
        var details = windows[i].split(':');
        var windowNetwork = details[1];

        networks[windowNetwork] = true;
    }

    return Object.keys(networks);
};

function *getWindowIds(userId, network, group, returnType) {
    var windows = yield redis.smembers('windowlist:' + userId);
    var ret = [];

    for (var i = 0; i < windows.length; i++) {
        var details = windows[i].split(':');
        var windowId = details[0];
        var windowNetwork = details[1];
        var windowName = details[2];

        if (windowNetwork === network && (!group || windowName === group)) {
            if (returnType === 'id') {
                ret.push(windowId);
            } else if (returnType === 'name') {
                ret.push(windowName);
            }
        }
    }

    return ret;
}
<commit_msg>JSHint fixes<commit_after>//
//   Copyright 2009-2014 Ilkka Oksanen <iao@iki.fi>
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing,
//   software distributed under the License is distributed on an "AS
//   IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//   express or implied.  See the License for the specific language
//   governing permissions and limitations under the License.
//

'use strict';

var wrapper = require('co-redis'),
    redis = wrapper(require('redis').createClient()),
    log = require('../../lib/log');

exports.getWindowIdsForNetwork = function *(userId, network) {
    var ids = yield getWindowIds(userId, network, null, 'id');

    return ids;
};

exports.getWindowId = function *(userId, network, group) {
    var ids = yield getWindowIds(userId, network, group, 'id');

    if (ids.length === 1) {
        return ids[0];
    } else {
        log.warn(userId, 'Tried to find non-existing group: ' + group);
        return null;
    }
};

exports.getWindowNameAndNetwork = function *(userId, windowId) {
    var windows = yield redis.smembers('windowlist:' + userId);

    for (var i = 0; i < windows.length; i++) {
        var details = windows[i].split(':');
        if (details[0] === windowId) {
            return [ details[2],  details[1] ];
        }
    }

    return null;
};

exports.getWindowNamesForNetwork = function *(userId, network) {
    var ids = yield getWindowIds(userId, network, null, 'name');

    return ids;
};

exports.getNetworks = function *(userId) {
    var networks = {};

    var windows = yield redis.smembers('windowlist:' + userId);
    for (var i = 0; i < windows.length; i++) {
        var details = windows[i].split(':');
        var windowNetwork = details[1];

        networks[windowNetwork] = true;
    }

    return Object.keys(networks);
};

function *getWindowIds(userId, network, group, returnType) {
    var windows = yield redis.smembers('windowlist:' + userId);
    var ret = [];

    for (var i = 0; i < windows.length; i++) {
        var details = windows[i].split(':');
        var windowId = details[0];
        var windowNetwork = details[1];
        var windowName = details[2];

        if (windowNetwork === network && (!group || windowName === group)) {
            if (returnType === 'id') {
                ret.push(windowId);
            } else if (returnType === 'name') {
                ret.push(windowName);
            }
        }
    }

    return ret;
}
<|endoftext|>


<commit_before>import Imap from 'imap';
import addressparser from 'addressparser';
import quotedPrintable from 'quoted-printable';
import sanitizeHtml from 'sanitize-html';
import { CronJob } from 'cron';
import { moment } from 'meteor/momentjs:moment';
import { FlowRouter } from 'meteor/kadira:flow-router';

Meteor.startup(function () {
  const job = new CronJob('0 9 15 * * *', Meteor.bindEnvironment(() => {
    if (!UltiSite.settings().mailingListConfigs) { return; }
    UltiSite.settings().mailingListConfigs.forEach(function (config) {
      const options = _.extend({ tls: true }, config);
      console.log(config.from + ':connecting to mail-server');
      const imap = new Imap(options);
      imap.once('ready', Meteor.bindEnvironment(function () {
        console.log(config.from + ':Connected to ' + options.host);
        imap.openBox('INBOX', false, Meteor.bindEnvironment(function (err, box) {
          if (err) {
            console.log(config.from + ': ' + err);
            imap.end();
            return;
          }
          console.log(config.from + ': ' + box.messages.total + ' messages on server');
          if (box.messages.total === 0) { return; }
          const f = imap.fetch('*', {
            bodies: ['HEADER.FIELDS (FROM TO SUBJECT DATE)'],
            struct: true,
          });
          f.on('message', Meteor.bindEnvironment(function (msg, seqno) {
            let header;
            let structure;
            let uid;
            msg.on('body', function (stream, info) {
              let buffer = '';
              stream.on('data', function (chunk) {
                buffer += chunk.toString('utf8');
              });
              stream.once('end', function () {
                header = Imap.parseHeader(buffer);
              });
            });
            msg.on('attributes', function (attrs) {
              structure = attrs.struct;
              uid = attrs.uid;
            });
            msg.on('end', Meteor.bindEnvironment(function (attrs) {
              const address = addressparser(header.from[0]);
              const user = Accounts.findUserByEmail(address[0].address);
              const date = moment(header.date[0], 'ddd, DD MMM YYYY HH:mm:ss ZZ');
              let blogId = Random.id();

              if (user) {
                console.log('got email from user:', user.username, address[0].address);
                const exists = UltiSite.Blogs.findOne({ author: user._id, date: date.toDate() });
                if (exists) { blogId = exists._id; }
                UltiSite.Blogs.upsert(blogId, {
                  $set: {
                    lastChange: new Date(),
                    _id: blogId,
                    title: header.subject[0],
                    public: false,
                    author: user._id,
                    byEmail: true,
                    date: date.toDate(),
                  },
                });
                if (!exists) {
                  Meteor.call('addEvent', {
                    type: 'blog',
                    _id: blogId,
                    userId: user._id,
                    text: 'Artikel per Mail gesendet',
                  });
                }

                const parts = [];
                structure.forEach((part) => {
                  if (part.partID) { parts.push(part); } else if (Array.isArray(part)) {
                    part.forEach((sub) => {
                      if (sub.partID) { parts.push(sub); } else if (Array.isArray(sub)) {
                        sub.forEach((subsub) => {
                          if (subsub.partID) { parts.push(subsub); }
                        });
                      }
                    });
                  }
                });
                parts.forEach((p) => {
                  Meteor.setTimeout(function () {
                    fetchPart(imap, blogId, uid, p);
                  }, 100);
                });
              } else {
                console.log('got email from unknown:', address[0].address);
              }
              Meteor.setTimeout(function () {
                imap.addFlags(uid, 'Deleted', Meteor.bindEnvironment(function (err) {
                  if (err) { console.log('deletion failed:', err); }
                  imap.closeBox(true, (err) => {
                    if (err) { console.log('Failed closing box: ', err); }
                  });
                  console.log('Removed mail from inbox');
                  imap.end();
                  const search = {};
                  search['settings.email.' + config.from.toCamelCase()] = 'immediate';
                  Meteor.users.find(search).forEach(function (user) {
                    sendMailinglistArticle(user, blogId);
                  });
                }));
              }, 30000);
            }));
          }));
          f.once('error', function (err) {
            console.log(config.from + ': ' + err);
          });
          f.once('end', function () {
            console.log(config.from + ':Done fetching all messages!');
          });
        }));
      }));
      try {
        imap.connect();
      } catch (err) {
        console.log(config.from + ':', err);
      }
    });
  }), null, false, 'Europe/Berlin');
  job.start();
});

const fetchPart = function (imap, blogId, uid, part) {
  console.log('fetching part from ' + uid + ':', part.partID, part.type + '/' + part.subtype);
  const partfetch = imap.fetch(uid, { bodies: [part.partID] });
  partfetch.on('message', Meteor.bindEnvironment(function (msgPart) {
    msgPart.on('body', Meteor.bindEnvironment(function (stream, info) {
      let buffer = '';
      stream.on('data', Meteor.bindEnvironment(function (chunk) {
        buffer += chunk.toString('utf8');
      }));
      stream.once('end', Meteor.bindEnvironment(function () {
        if (part.disposition && part.disposition.type === 'attachment') {
          /* TODO: image from mail
          if (file.isImage()) {
          let buf = new Buffer(buffer, part.encoding);
          let file = {}
          file.associated = [blogId];
          file.tags = [];
          file.comments = [];
          file.name = part.disposition.filename||part.params.name;
          file.attachData(buf,{type:part.type+'/'+part.subtype});
              let id = UltiSite.Images.insert(file);
              Meteor.setTimeout(()=>{
                  UltiSite.Blogs.update(blogId,{
                      $set:{
                          image:id
                      }
                  });
              },100);
          }
          else
              UltiSite.Documents.insert(file);
          console.log(uid + ': fetched doc/image '+part.disposition.filename);
          */
        } else if (part.type === 'text') {
          let text = quotedPrintable.decode(buffer);
          text = sanitizeHtml(text);
          UltiSite.Blogs.upsert(blogId, {
            $set: {
              _id: blogId,
              content: text,
              preview: sanitizeHtml(text, { allowedTags: [] }).substr(0, 300),
            },
          });
          console.log(uid + ': fetched text');
        }
      }));
    }));
  }));
  partfetch.once('error', function (err) {
    console.log(uid + ': part fetch error' + err);
  });
};

const sendMailinglistArticle = function (user, blogId, mailinglist) {
  const template = Assets.getText('mail-templates/article.html');
  const layout = Assets.getText('mail-templates/layout.html');
  const blog = UltiSite.blogs.findOne(blogId);
  const author = Meteor.users.findOne(blog.author);
  UltiSite.Mail.send([user._id], blog.title,
    UltiSite.renderMailTemplate(layout, template, {
      user,
      profilUrl: FlowRouter.url('user', { _id: user._id }),
      mailinglist,
      url: FlowRouter.url('blog', { _id: blog._id }),
      title: blog.title,
      content: sanitizeHtml(blog.content),
      email: author.emails[0].address,
      writer: author.username + ' (' + author.profile.name + ' ' + author.profile.surname + ')',
      date: moment(blog.date).format('DD.MM.YYYY - HH:mm'),
    }));
  return true;
};
<commit_msg>mailinglist fetch disabled on localhost<commit_after>import Imap from 'imap';
import addressparser from 'addressparser';
import quotedPrintable from 'quoted-printable';
import sanitizeHtml from 'sanitize-html';
import { CronJob } from 'cron';
import { moment } from 'meteor/momentjs:moment';
import { FlowRouter } from 'meteor/kadira:flow-router';

Meteor.startup(function () {
  const job = new CronJob('0 9 15 * * *', Meteor.bindEnvironment(() => {
    if (!UltiSite.settings().mailingListConfigs) { return; }
    UltiSite.settings().mailingListConfigs.forEach(function (config) {
      const options = _.extend({ tls: true }, config);
      console.log(config.from + ':connecting to mail-server');
      const imap = new Imap(options);
      imap.once('ready', Meteor.bindEnvironment(function () {
        console.log(config.from + ':Connected to ' + options.host);
        imap.openBox('INBOX', false, Meteor.bindEnvironment(function (err, box) {
          if (err) {
            console.log(config.from + ': ' + err);
            imap.end();
            return;
          }
          console.log(config.from + ': ' + box.messages.total + ' messages on server');
          if (box.messages.total === 0) { return; }
          const f = imap.fetch('*', {
            bodies: ['HEADER.FIELDS (FROM TO SUBJECT DATE)'],
            struct: true,
          });
          f.on('message', Meteor.bindEnvironment(function (msg, seqno) {
            let header;
            let structure;
            let uid;
            msg.on('body', function (stream, info) {
              let buffer = '';
              stream.on('data', function (chunk) {
                buffer += chunk.toString('utf8');
              });
              stream.once('end', function () {
                header = Imap.parseHeader(buffer);
              });
            });
            msg.on('attributes', function (attrs) {
              structure = attrs.struct;
              uid = attrs.uid;
            });
            msg.on('end', Meteor.bindEnvironment(function (attrs) {
              const address = addressparser(header.from[0]);
              const user = Accounts.findUserByEmail(address[0].address);
              const date = moment(header.date[0], 'ddd, DD MMM YYYY HH:mm:ss ZZ');
              let blogId = Random.id();

              if (user) {
                console.log('got email from user:', user.username, address[0].address);
                const exists = UltiSite.Blogs.findOne({ author: user._id, date: date.toDate() });
                if (exists) { blogId = exists._id; }
                UltiSite.Blogs.upsert(blogId, {
                  $set: {
                    lastChange: new Date(),
                    _id: blogId,
                    title: header.subject[0],
                    public: false,
                    author: user._id,
                    byEmail: true,
                    date: date.toDate(),
                  },
                });
                if (!exists) {
                  Meteor.call('addEvent', {
                    type: 'blog',
                    _id: blogId,
                    userId: user._id,
                    text: 'Artikel per Mail gesendet',
                  });
                }

                const parts = [];
                structure.forEach((part) => {
                  if (part.partID) { parts.push(part); } else if (Array.isArray(part)) {
                    part.forEach((sub) => {
                      if (sub.partID) { parts.push(sub); } else if (Array.isArray(sub)) {
                        sub.forEach((subsub) => {
                          if (subsub.partID) { parts.push(subsub); }
                        });
                      }
                    });
                  }
                });
                parts.forEach((p) => {
                  Meteor.setTimeout(function () {
                    fetchPart(imap, blogId, uid, p);
                  }, 100);
                });
              } else {
                console.log('got email from unknown:', address[0].address);
              }
              Meteor.setTimeout(function () {
                imap.addFlags(uid, 'Deleted', Meteor.bindEnvironment(function (err) {
                  if (err) { console.log('deletion failed:', err); }
                  imap.closeBox(true, (err) => {
                    if (err) { console.log('Failed closing box: ', err); }
                  });
                  console.log('Removed mail from inbox');
                  imap.end();
                  const search = {};
                  search['settings.email.' + config.from.toCamelCase()] = 'immediate';
                  Meteor.users.find(search).forEach(function (user) {
                    sendMailinglistArticle(user, blogId);
                  });
                }));
              }, 30000);
            }));
          }));
          f.once('error', function (err) {
            console.log(config.from + ': ' + err);
          });
          f.once('end', function () {
            console.log(config.from + ':Done fetching all messages!');
          });
        }));
      }));
      try {
        imap.connect();
      } catch (err) {
        console.log(config.from + ':', err);
      }
    });
  }), null, false, 'Europe/Berlin');
  if (!Meteor.absoluteUrl().match(/localhost/)) {
    job.start();
  } else {
    console.log('no mail watcher because of localhost');
  }
});

const fetchPart = function (imap, blogId, uid, part) {
  console.log('fetching part from ' + uid + ':', part.partID, part.type + '/' + part.subtype);
  const partfetch = imap.fetch(uid, { bodies: [part.partID] });
  partfetch.on('message', Meteor.bindEnvironment(function (msgPart) {
    msgPart.on('body', Meteor.bindEnvironment(function (stream, info) {
      let buffer = '';
      stream.on('data', Meteor.bindEnvironment(function (chunk) {
        buffer += chunk.toString('utf8');
      }));
      stream.once('end', Meteor.bindEnvironment(function () {
        if (part.disposition && part.disposition.type === 'attachment') {
          /* TODO: image from mail
          if (file.isImage()) {
          let buf = new Buffer(buffer, part.encoding);
          let file = {}
          file.associated = [blogId];
          file.tags = [];
          file.comments = [];
          file.name = part.disposition.filename||part.params.name;
          file.attachData(buf,{type:part.type+'/'+part.subtype});
              let id = UltiSite.Images.insert(file);
              Meteor.setTimeout(()=>{
                  UltiSite.Blogs.update(blogId,{
                      $set:{
                          image:id
                      }
                  });
              },100);
          }
          else
              UltiSite.Documents.insert(file);
          console.log(uid + ': fetched doc/image '+part.disposition.filename);
          */
        } else if (part.type === 'text') {
          let text = quotedPrintable.decode(buffer);
          text = sanitizeHtml(text);
          UltiSite.Blogs.upsert(blogId, {
            $set: {
              _id: blogId,
              content: text,
              preview: sanitizeHtml(text, { allowedTags: [] }).substr(0, 300),
            },
          });
          console.log(uid + ': fetched text');
        }
      }));
    }));
  }));
  partfetch.once('error', function (err) {
    console.log(uid + ': part fetch error' + err);
  });
};

const sendMailinglistArticle = function (user, blogId, mailinglist) {
  const template = Assets.getText('mail-templates/article.html');
  const layout = Assets.getText('mail-templates/layout.html');
  const blog = UltiSite.blogs.findOne(blogId);
  const author = Meteor.users.findOne(blog.author);
  UltiSite.Mail.send([user._id], blog.title,
    UltiSite.renderMailTemplate(layout, template, {
      user,
      profilUrl: FlowRouter.url('user', { _id: user._id }),
      mailinglist,
      url: FlowRouter.url('blog', { _id: blog._id }),
      title: blog.title,
      content: sanitizeHtml(blog.content),
      email: author.emails[0].address,
      writer: author.username + ' (' + author.profile.name + ' ' + author.profile.surname + ')',
      date: moment(blog.date).format('DD.MM.YYYY - HH:mm'),
    }));
  return true;
};
<|endoftext|>


<commit_before>import express from 'express';
import passport from 'passport';

// Load passport configuration
import '../auth/passport';

const router = express.Router();
router.use(passport.initialize());
router.use(passport.session());

// Middlware to handle server errors during authentication
/**
 * @returns null
 * @param {Object} err Error object sent from server
 * @param {*} req Request object
 * @param {*} res Response Object
 * @param {*} next Next Callback function
 */

router.post('/signin',
  passport.authenticate('local.signin', { failWithError: true }),
  (req, res, next) =>
    // Successful signin
    res.send({ user: req.user, message: 'Successful Signin' }), (err, req, res, next) =>
    // Failure during signin
    res.send({ error: err, message: 'Error During Signin' })
);

router.post('/signup',
  passport.authenticate('local.signup', { failWithError: true }),
  (req, res, next) => {
    // Successful signup
    return res.send( { user: req.user, message: 'Successfull Signup' });
  }, (err, req, res, next) => {
    // Failure during signup
    return res.send({ error: err, message: 'Error During Signup' });
  }
);

export default router;
<commit_msg>remove superfluous return statement<commit_after>import express from 'express';
import passport from 'passport';

// Load passport configuration
import '../auth/passport';

const router = express.Router();
router.use(passport.initialize());
router.use(passport.session());

// Middlware to handle server errors during authentication
/**
 * @returns null
 * @param {Object} err Error object sent from server
 * @param {*} req Request object
 * @param {*} res Response Object
 * @param {*} next Next Callback function
 */

router.post('/signin',
  passport.authenticate('local.signin', { failWithError: true }),
  (req, res, next) =>
    // Successful signin
    res.send({ user: req.user, message: 'Successful Signin' }), (err, req, res, next) =>
    // Failure during signin
    res.send({ error: err, message: 'Error During Signin' })
);

router.post('/signup',
  passport.authenticate('local.signup', { failWithError: true }),
  (req, res, next) =>
    // Successful signup
    res.send({ user: req.user, message: 'Successfull Signup' }), (err, req, res, next) =>
    // Failure during signup
    res.send({ error: err, message: 'Error During Signup' })
);

export default router;
<|endoftext|>


<commit_before>/**
 * REST API to store/retrieve/edit device logs.
 *
 *
 * (c) 2013 Edouard Lafargue, edouard@lafargue.name
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

var PouchDB = require('pouchdb');
var dbs = require('../pouch-config');
var recorder = require('../recorder.js');

// Get all log sessions for a given instrument
exports.findByInstrumentId = function(req, res) {
    var id = req.params.id;
    console.log('Retrieving Logs for Instrument ID: ' + id);

    // TODO
    //  - Move to persistent queries (http://pouchdb.com/guides/queries.html)
    //  - Update entries count in the log (views ??)
    dbs.logs.query(function(doc) {
        emit(doc.instrumentid);
    }, {key: id, include_docs:true}, function(err,items) {
        if (err && err.status == 404) {
            res.send([]);
            return;
        }
        console.log(items);
        var resp = [];
        for (item in items.rows) {
            resp.push(items.rows[item].doc) ;
        }
        res.send(resp);
    });
};

// Get a log session
exports.findById = function(req, res) {
    var id = req.params.id;
    console.log('Retrieving Log session ID: ' + id);
    dbs.logs.get(id, function(err,item) {
        if (err && err.status == 404) {
            res.send([]);
            return;
        }
        console.log(item);
        res.send(item);
    });
};

exports.findAll = function(req, res) {
    dbs.logs.allDocs({include_docs: true}, function(err, items) {
        var resp = [];
        for (item in items.rows) {
            console.log(item);
            resp.push(items.rows[item].doc) ;
        }
        console.log(resp);
        res.send(resp);
    });
};

// Get all entries for a log session. These can be very large,
// so we have to stream the results back so that we don't get out
// of memory or let the client app hanging waiting for data
exports.getLogEntries = function(req, res) {
    // Empty for now...
    var id = req.params.id;
    var count = 0;
    console.log("Retrieving entries of log ID: " + id);
    // TODO: Now, create a new database that will contain the data points for
    // this log with the format "datapoints/[log ID]"
    var db = new PouchDB('./ldb/datapoints/' + id);
    res.writeHead(200, {"Content-Type": "application/json"});
    res.write("[");
    var ok = false;
    db.allDocs({include_docs:true}, function(err,entries) {
        for (row in entries.rows) {
            var item = entries.rows[row];
            if (ok) res.write(",");
            ok = true;
            // Clean up the log data: we don't need a lot of stuff that takes
            // lots of space:
            // delete item._id;  // Don't delete the _id, otherwise our front-end loses sync with backend!
            console.log(item);
            delete item.doc._rev;
            res.write(JSON.stringify(item.doc));
        }
        res.write(']');
        res.end();
    });

}

// Get log entries for the current live recording: we only get data for the last
// XX minutes
exports.getLive = function(req,res) {
    console.log("Request to get extract of live recording for the last " + req.params.period + " minutes");
    var rid = recorder.getRecordingID();
    if (rid == null) {
        res.send('{"error": "Not recording" }');
        return;
    }
    var minstamp = new Date( new Date().getTime() - req.params.period* 60000);
    var stream = DeviceLogEntry.find({logsessionid: rid, timestamp: {"$gte": minstamp} }).lean().batchSize(500).stream();
    res.writeHead(200, {"Content-Type": "application/json"});
    res.write("[");
    var ok = false;
    stream.on('data', function(item) {
                         if (ok) res.write(",");
                         ok = true;
                         console.log(item);
                         delete item._id;
                         delete item.__v;
                         delete item.logsessionid;
                         console.log("---------- AFTER --------");
                         console.log(item);
                         res.write(JSON.stringify(item));
                        }
             ).on('error', function(err) {
    }).on('close', function() {
        res.write("]");
        res.end();
    });
}

////////////////
//  This call is deprecated (log entries are created through the recorder now
////////////////
// Add a new log entry for a log:
// TODO : create a library to store logs in a more sophisticated
// manner - 1 hour granularity w/ 59 minute objects containing all measurements inside ?
exports.addLogEntry = function(req, res) {
    var logID = req.params.id;
    var entry = req.body;
    delete entry._id;
    entry.logsessionid = logID;
    console.log(entry);
    new DeviceLogEntry(entry).save(function(err,entry) {
        if (err) {
            console.log("Error saving entry: " + err);
            res.send({'error': 'Error saving entry - ' + err});
        } else {
        res.send(entry);
        }
    });
}

// Add a new log session entry
exports.addLog = function(req, res) {
    var entry = req.body;
    var instrumentid = req.params.id;
    entry.instrumentid = instrumentid;
    console.log('Adding log entry for Instrument ID: ' + instrumentid + ' - ' + JSON.stringify(entry));
    dbs.logs.post(entry, function(err, result) {
            if (err) {
                res.send({'error':'An error has occurred'});
            } else {
                console.log('Success - result: ' + JSON.stringify(result));
                res.send({ _id: result.id, _rev: result.rev} );
            }
    });

};

// Update the contents of a log session
exports.updateEntry = function(req, res) {
    var id = req.params.id;
    var iid = req.params.iid;
    var entry = req.body;
    console.log('Updating log session entry: ' + id + ' for instrument ' + iid);
    console.log(JSON.stringify(entry));

    // TODO: error checking on structure !!!
    //  -> CouchDB validation
    dbs.logs.put(entry, function(err, result) {
                    if (err) {
                        console.log('Error updating log session entry: ' + err);
                        res.send({'error':'An error has occurred'});
                    } else {
                        res.send({ _id: result.id, _rev: result.rev} );
                    }
                }
                                );
};

// This deletes a LOG Entry
exports.deleteLogEntry = function(req, res) {
    var id = req.params.id;
    console.log('Deleting log entry: ' + id);
    DeviceLogEntry.findByIdAndRemove(id, {safe:true}, function(err,result) {
            if (err) {
                res.send({'error':'An error has occurred - ' + err});
            } else {
                console.log('' + result + ' document(s) deleted');
                res.send(req.body);
            }
    });
}

// This deletes a log
exports.deleteLog= function(req, res) {
    var id = req.params.id;
    console.log('Deleting log: ' + id);
    dbs.logs.get(id, function(err,log) {
        if (err) {
            console.log('Error - ' + err);
            res.send({'error':'An error has occurred - ' + err});
        } else {
            dbs.logs.remove(log, function(err,result) {
                if (err) {
                    console.log('Error - ' + err);
                    res.send({'error':'An error has occurred - ' + err});
                } else {
                    console.log('' + result + ' document(s) deleted');
                    // Now delete the database of log points
                    PouchDB.destroy('./ldb/datapoints/' + id, function(err,res2) {
                        console.log("Destroyed datapoints");
                        res.send(req.body);
                    });
                }
            });
        }
    });
}
<commit_msg>Implement live view on CouchDB (temp view, needs to be migrated to persistent)<commit_after>/**
 * REST API to store/retrieve/edit device logs.
 *
 *
 * (c) 2013 Edouard Lafargue, edouard@lafargue.name
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

var PouchDB = require('pouchdb');
var dbs = require('../pouch-config');
var recorder = require('../recorder.js');

// Get all log sessions for a given instrument
exports.findByInstrumentId = function(req, res) {
    var id = req.params.id;
    console.log('Retrieving Logs for Instrument ID: ' + id);

    // TODO
    //  - Move to persistent queries (http://pouchdb.com/guides/queries.html)
    //  - Update entries count in the log (views ??)
    dbs.logs.query(function(doc) {
        emit(doc.instrumentid);
    }, {key: id, include_docs:true}, function(err,items) {
        if (err && err.status == 404) {
            res.send([]);
            return;
        }
        console.log(items);
        var resp = [];
        for (item in items.rows) {
            resp.push(items.rows[item].doc) ;
        }
        res.send(resp);
    });
};

// Get a log session
exports.findById = function(req, res) {
    var id = req.params.id;
    console.log('Retrieving Log session ID: ' + id);
    dbs.logs.get(id, function(err,item) {
        if (err && err.status == 404) {
            res.send([]);
            return;
        }
        console.log(item);
        res.send(item);
    });
};

exports.findAll = function(req, res) {
    dbs.logs.allDocs({include_docs: true}, function(err, items) {
        var resp = [];
        for (item in items.rows) {
            console.log(item);
            resp.push(items.rows[item].doc) ;
        }
        console.log(resp);
        res.send(resp);
    });
};

// Get all entries for a log session. These can be very large,
// so we have to stream the results back so that we don't get out
// of memory or let the client app hanging waiting for data
exports.getLogEntries = function(req, res) {
    // Empty for now...
    var id = req.params.id;
    var count = 0;
    console.log("Retrieving entries of log ID: " + id);
    // TODO: Now, create a new database that will contain the data points for
    // this log with the format "datapoints/[log ID]"
    var db = new PouchDB('./ldb/datapoints/' + id);
    res.writeHead(200, {"Content-Type": "application/json"});
    res.write("[");
    var ok = false;
    db.allDocs({include_docs:true}, function(err,entries) {
        for (row in entries.rows) {
            var item = entries.rows[row];
            if (ok) res.write(",");
            ok = true;
            // Clean up the log data: we don't need a lot of stuff that takes
            // lots of space:
            // delete item._id;  // Don't delete the _id, otherwise our front-end loses sync with backend!
            console.log(item);
            delete item.doc._rev;
            res.write(JSON.stringify(item.doc));
        }
        res.write(']');
        res.end();
    });

}

// Get log entries for the current live recording: we only get data for the last
// XX minutes
exports.getLive = function(req,res) {
    console.log("Request to get extract of live recording for the last " + req.params.period + " minutes");
    var rid = recorder.logID();
    if (rid == null) {
        res.send('{"error": "Not recording" }');
        return;
    }

    // TODO : use a persistent view here!
    var map = function(doc) {
        emit (doc.timestamp);
    }

    var minstamp = new Date().getTime() - req.params.period* 60000;
    console.log(" Min Stamp: " + minstamp);
    var db = new PouchDB('./ldb/datapoints/' + rid);
    res.writeHead(200, {"Content-Type": "application/json"});
    res.write("[");
    var ok = false;
    db.query(map, {startkey: minstamp, include_docs:true}, function(err,entries) {
        console.log(entries);
        for (row in entries.rows) {
            var item = entries.rows[row];
            if (ok) res.write(",");
            ok = true;
            // Clean up the log data: we don't need a lot of stuff that takes
            // lots of space:
            delete item.doc._rev;
            delete item.doc._id;
            console.log(item);
            res.write(JSON.stringify(item.doc));
        }
        res.write(']');
        res.end();
    });
}

////////////////
//  This call is deprecated (log entries are created through the recorder now
////////////////
// Add a new log entry for a log:
// TODO : create a library to store logs in a more sophisticated
// manner - 1 hour granularity w/ 59 minute objects containing all measurements inside ?
exports.addLogEntry = function(req, res) {
    var logID = req.params.id;
    var entry = req.body;
    delete entry._id;
    entry.logsessionid = logID;
    console.log(entry);
    new DeviceLogEntry(entry).save(function(err,entry) {
        if (err) {
            console.log("Error saving entry: " + err);
            res.send({'error': 'Error saving entry - ' + err});
        } else {
        res.send(entry);
        }
    });
}

// Add a new log session entry
exports.addLog = function(req, res) {
    var entry = req.body;
    var instrumentid = req.params.id;
    entry.instrumentid = instrumentid;
    console.log('Adding log entry for Instrument ID: ' + instrumentid + ' - ' + JSON.stringify(entry));
    dbs.logs.post(entry, function(err, result) {
            if (err) {
                res.send({'error':'An error has occurred'});
            } else {
                console.log('Success - result: ' + JSON.stringify(result));
                res.send({ _id: result.id, _rev: result.rev} );
            }
    });

};

// Update the contents of a log session
exports.updateEntry = function(req, res) {
    var id = req.params.id;
    var iid = req.params.iid;
    var entry = req.body;
    console.log('Updating log session entry: ' + id + ' for instrument ' + iid);
    console.log(JSON.stringify(entry));

    // TODO: error checking on structure !!!
    //  -> CouchDB validation
    dbs.logs.put(entry, function(err, result) {
                    if (err) {
                        console.log('Error updating log session entry: ' + err);
                        res.send({'error':'An error has occurred'});
                    } else {
                        res.send({ _id: result.id, _rev: result.rev} );
                    }
                }
                                );
};

// This deletes a LOG Entry
exports.deleteLogEntry = function(req, res) {
    var id = req.params.id;
    console.log('Deleting log entry: ' + id);
    DeviceLogEntry.findByIdAndRemove(id, {safe:true}, function(err,result) {
            if (err) {
                res.send({'error':'An error has occurred - ' + err});
            } else {
                console.log('' + result + ' document(s) deleted');
                res.send(req.body);
            }
    });
}

// This deletes a log
exports.deleteLog= function(req, res) {
    var id = req.params.id;
    console.log('Deleting log: ' + id);
    dbs.logs.get(id, function(err,log) {
        if (err) {
            console.log('Error - ' + err);
            res.send({'error':'An error has occurred - ' + err});
        } else {
            dbs.logs.remove(log, function(err,result) {
                if (err) {
                    console.log('Error - ' + err);
                    res.send({'error':'An error has occurred - ' + err});
                } else {
                    console.log('' + result + ' document(s) deleted');
                    // Now delete the database of log points
                    PouchDB.destroy('./ldb/datapoints/' + id, function(err,res2) {
                        console.log("Destroyed datapoints");
                        res.send(req.body);
                    });
                }
            });
        }
    });
}
<|endoftext|>


<commit_before>var Contract = require("../lib/contract"),
    ethCmd = require("../lib/eth_cmd"),
    settings = require("./test_settings.json"),
    fs = require("fs");

describe("Contract", function(){
  describe.skip("if built from source", function(){
    //nice to have, but turns out to be a bit of a pain in the rear
    var contract;

    before(function(){
      contract = new Contract();

      contract.build(fs.readFileSync("contracts/simplecoin.sol").toString());
    });

    it("has compiled bytecode", function(){
      contract.code.should.be.a.String;
      contract.code.length.should.be.above(0);
    });

    it("has an abi", function(){
      contract.abi.should.be.an.Array;
      var fnNames = contract.abi.map(function(fn){ return fn.name; });

      fnNames.should.containEql("issue");
      fnNames.should.containEql("balanceOf");
      fnNames.should.containEql("transferTo");
    });

    it("can be deployed", function(done){
      (contract.address === undefined).should.be.true;

      contract.deploy(function(err, addr){
        (!!err).should.be.false;

        addr.should.be.a.String;
        addr.should.be.equal(contract.address);
        addr.length.should.be.equal(24);

        console.log(2,ethCmd.web3.eth.getCode(addr));

        done();
      });
    });
  });

  describe("when linked with an address and abi of an existing contract", function(){
    var contract;

    before(function(){
      contract = new Contract({
        address: settings.contract.address,
        abi: settings.contract.abi
      });
    });

    it("exposes web3 contract interface", function(){
      contract.balanceOf.should.be.a.Function;
      contract.issue.should.be.a.Function;
    });
  });

  it("cannot be linked with an address of a contract that doesn't exist", function(){
    (function(){
      var contract = new Contract({
        address: settings.coinContract.address.substr(
          0,
          settings.coinContract.address.length - 1
        ) + "2",
        abi: settings.coinContract.abi
      });
    }).should.throw();
  });
});
<commit_msg>all tests pass<commit_after>var Contract = require("../lib/contract"),
    ethCmd = require("../lib/eth_cmd"),
    settings = require("./test_settings.json"),
    fs = require("fs");

describe("Contract", function(){
  describe("when linked with an address and abi of an existing contract", function(){
    var contract;

    before(function(){
      contract = new Contract({
        address: settings.contract.address,
        abi: settings.contract.abi
      });
    });

    it("exposes web3 contract interface", function(){
      contract.balanceOf.sendTransaction.should.be.a.Function;
      contract.issue.sendTransaction.should.be.a.Function;
      contract.balanceOf.call.should.be.a.Function;
      contract.issue.call.should.be.a.Function;
    });
  });

  it("cannot be linked with an address of a contract that doesn't exist", function(){
    (function(){
      var contract = new Contract({
        address: settings.coinContract.address.substr(
          0,
          settings.coinContract.address.length - 1
        ) + "2",
        abi: settings.coinContract.abi
      });
    }).should.throw();
  });
});
<|endoftext|>