<commit_before>/*!
 * Copyright (C) 2017 Glayzzle (BSD3 License)
 * @authors https://github.com/glayzzle/php-parser/graphs/contributors
 * @url http://glayzzle.com
 */
"use strict";

module.exports = {
  /**
   * Reads a namespace declaration block
   * ```ebnf
   * namespace ::= T_NAMESPACE namespace_name? '{'
   *    top_statements
   * '}'
   * | T_NAMESPACE namespace_name ';' top_statements
   * ```
   * @see http://php.net/manual/en/language.namespaces.php
   * @return {Namespace}
   */
  read_namespace: function() {
    var result = this.node('namespace');
    this.expect(this.tok.T_NAMESPACE) && this.next();
    if (this.token == '{') {
      this.currentNamespace = [''];
      return result([''], this.read_code_block(true), true);
    } else {
      var name = this.read_namespace_name();
      if (this.token == ';') {
        this.currentNamespace = name;
        var body = this.nextWithComments().read_top_statements();
        this.expect(this.EOF);
        return result(name, body);
      } else if (this.token == '{') {
        this.currentNamespace = name;
        return result(name, this.read_code_block(true), true);
      } else if (this.token === '(') {
        // resolve ambuiguity between namespace & function call
        name.resolution = this.ast.identifier.RELATIVE_NAME;
        name.name = name.name.substring(1);
        return this.node('call')(
          name, this.read_function_argument_list()
        );
      } else {
        this.error(['{', ';']);
        // graceful mode :
        this.currentNamespace = name;
        var body = this.read_top_statements();
        this.expect(this.EOF);
        return result(name, body);
      }
    }
  }
  /**
   * Reads a namespace name
   * ```ebnf
   *  namespace_name ::= T_NS_SEPARATOR? (T_STRING T_NS_SEPARATOR)* T_STRING
   * ```
   * @see http://php.net/manual/en/language.namespaces.rules.php
   * @return {Identifier}
   */
  ,read_namespace_name: function() {
    var result = this.node('identifier'), relative = false;
    if (this.token === this.tok.T_NAMESPACE) {
      this.next().expect(this.tok.T_NS_SEPARATOR) && this.next();
      relative = true
    }
    return result(
      this.read_list(this.tok.T_STRING, this.tok.T_NS_SEPARATOR, true),
      relative
    );
  }
  /**
   * Reads a use statement
   * ```ebnf
   * use_statement ::= T_USE
   *   use_type? use_declarations |
   *   use_type use_statement '{' use_declarations '}' |
   *   use_statement '{' use_declarations(=>typed) '}'
   * ';'
   * ```
   * @see http://php.net/manual/en/language.namespaces.importing.php
   * @return {UseGroup}
   */
  ,read_use_statement: function() {
    var result = this.node('usegroup'),
      type = null,
      items = [],
      name = null
    ;
    this.expect(this.tok.T_USE) && this.next();
    type = this.read_use_type();
    items.push(this.read_use_declaration(false));
    if (this.token === ',') {
      items = items.concat(this.next().read_use_declarations(false));
    } else if (this.token === '{') {
      name = items[0].name;
      items = this.next().read_use_declarations(type === null);
      this.expect('}') && this.next();
    }
    this.expect(';') && this.nextWithComments();
    return result(name, type, items);
  }
  /**
   * Reads a use declaration
   * ```ebnf
   * use_declaration ::= use_type? namespace_name use_alias
   * ```
   * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L380
   * @return {UseItem}
   */
  ,read_use_declaration: function(typed) {
    var result = this.node('useitem'), type = null;
    if (typed) type = this.read_use_type();
    var name = this.read_namespace_name();
    var alias = this.read_use_alias();
    return result(name, alias, type);
  }
  /**
  * Reads a list of use declarations
  * ```ebnf
  * use_declarations ::= use_declaration (',' use_declaration)*
  * ```
  * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L380
  * @return {UseItem[]}
   */
  ,read_use_declarations: function(typed) {
    var result = [this.read_use_declaration(typed)];
    while(this.token === ',') {
      result.push(this.next().read_use_declaration(typed));
    }
    return result;
  }
  /**
   * Reads a use statement
   * ```ebnf
   * use_alias ::= (T_AS T_STRING)?
   * ```
   * @return {String|null}
   */
  ,read_use_alias: function() {
    var result = null;
    if (this.token === this.tok.T_AS) {
      if (this.next().expect(this.tok.T_STRING)) {
        result = this.text();
        this.next();
      }
    }
    return result;
  }
  /**
   * Reads the namespace type declaration
   * ```ebnf
   * use_type ::= (T_FUNCTION | T_CONST)?
   * ```
   * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L335
   * @return {String|null} Possible values : function, const
   */
  ,read_use_type: function() {
    if (this.token === this.tok.T_FUNCTION) {
      this.next();
      return this.ast.useitem.TYPE_FUNCTION;
    } else if (this.token === this.tok.T_CONST) {
      this.next();
      return this.ast.useitem.TYPE_CONST;
    }
    return null;
  }
};
<commit_msg>avoid nested namespace blocks<commit_after>/*!
 * Copyright (C) 2017 Glayzzle (BSD3 License)
 * @authors https://github.com/glayzzle/php-parser/graphs/contributors
 * @url http://glayzzle.com
 */
"use strict";

module.exports = {
  /**
   * Reads a namespace declaration block
   * ```ebnf
   * namespace ::= T_NAMESPACE namespace_name? '{'
   *    top_statements
   * '}'
   * | T_NAMESPACE namespace_name ';' top_statements
   * ```
   * @see http://php.net/manual/en/language.namespaces.php
   * @return {Namespace}
   */
  read_namespace: function() {
    var result = this.node('namespace');
    this.expect(this.tok.T_NAMESPACE) && this.next();
    if (this.token == '{') {
      this.currentNamespace = [''];
      var body =  this.nextWithComments().read_top_statements();
      this.expect('}') && this.nextWithComments();
      return result([''], body, true);
    } else {
      var name = this.read_namespace_name();
      if (this.token == ';') {
        this.currentNamespace = name;
        var body = this.nextWithComments().read_top_statements();
        this.expect(this.EOF);
        return result(name, body, false);
      } else if (this.token == '{') {
        this.currentNamespace = name;
        var body =  this.nextWithComments().read_top_statements();
        this.expect('}') && this.nextWithComments();
        return result(name, body, true);
      } else if (this.token === '(') {
        // resolve ambuiguity between namespace & function call
        name.resolution = this.ast.identifier.RELATIVE_NAME;
        name.name = name.name.substring(1);
        return this.node('call')(
          name, this.read_function_argument_list()
        );
      } else {
        this.error(['{', ';']);
        // graceful mode :
        this.currentNamespace = name;
        var body = this.read_top_statements();
        this.expect(this.EOF);
        return result(name, body, false);
      }
    }
  }
  /**
   * Reads a namespace name
   * ```ebnf
   *  namespace_name ::= T_NS_SEPARATOR? (T_STRING T_NS_SEPARATOR)* T_STRING
   * ```
   * @see http://php.net/manual/en/language.namespaces.rules.php
   * @return {Identifier}
   */
  ,read_namespace_name: function() {
    var result = this.node('identifier'), relative = false;
    if (this.token === this.tok.T_NAMESPACE) {
      this.next().expect(this.tok.T_NS_SEPARATOR) && this.next();
      relative = true
    }
    return result(
      this.read_list(this.tok.T_STRING, this.tok.T_NS_SEPARATOR, true),
      relative
    );
  }
  /**
   * Reads a use statement
   * ```ebnf
   * use_statement ::= T_USE
   *   use_type? use_declarations |
   *   use_type use_statement '{' use_declarations '}' |
   *   use_statement '{' use_declarations(=>typed) '}'
   * ';'
   * ```
   * @see http://php.net/manual/en/language.namespaces.importing.php
   * @return {UseGroup}
   */
  ,read_use_statement: function() {
    var result = this.node('usegroup'),
      type = null,
      items = [],
      name = null
    ;
    this.expect(this.tok.T_USE) && this.next();
    type = this.read_use_type();
    items.push(this.read_use_declaration(false));
    if (this.token === ',') {
      items = items.concat(this.next().read_use_declarations(false));
    } else if (this.token === '{') {
      name = items[0].name;
      items = this.next().read_use_declarations(type === null);
      this.expect('}') && this.next();
    }
    this.expect(';') && this.nextWithComments();
    return result(name, type, items);
  }
  /**
   * Reads a use declaration
   * ```ebnf
   * use_declaration ::= use_type? namespace_name use_alias
   * ```
   * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L380
   * @return {UseItem}
   */
  ,read_use_declaration: function(typed) {
    var result = this.node('useitem'), type = null;
    if (typed) type = this.read_use_type();
    var name = this.read_namespace_name();
    var alias = this.read_use_alias();
    return result(name, alias, type);
  }
  /**
  * Reads a list of use declarations
  * ```ebnf
  * use_declarations ::= use_declaration (',' use_declaration)*
  * ```
  * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L380
  * @return {UseItem[]}
   */
  ,read_use_declarations: function(typed) {
    var result = [this.read_use_declaration(typed)];
    while(this.token === ',') {
      result.push(this.next().read_use_declaration(typed));
    }
    return result;
  }
  /**
   * Reads a use statement
   * ```ebnf
   * use_alias ::= (T_AS T_STRING)?
   * ```
   * @return {String|null}
   */
  ,read_use_alias: function() {
    var result = null;
    if (this.token === this.tok.T_AS) {
      if (this.next().expect(this.tok.T_STRING)) {
        result = this.text();
        this.next();
      }
    }
    return result;
  }
  /**
   * Reads the namespace type declaration
   * ```ebnf
   * use_type ::= (T_FUNCTION | T_CONST)?
   * ```
   * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L335
   * @return {String|null} Possible values : function, const
   */
  ,read_use_type: function() {
    if (this.token === this.tok.T_FUNCTION) {
      this.next();
      return this.ast.useitem.TYPE_FUNCTION;
    } else if (this.token === this.tok.T_CONST) {
      this.next();
      return this.ast.useitem.TYPE_CONST;
    }
    return null;
  }
};
<|endoftext|>


<commit_before>export const VUEXFIRE_OBJECT_VALUE = 'vuexfire/OBJECT_VALUE'
export const VUEXFIRE_ARRAY_INITIALIZE = 'VUEXFIRE_ARRAY_INITIALIZE'
export const VUEXFIRE_ARRAY_ADD = 'vuexfire/ARRAY_ADD'
export const VUEXFIRE_ARRAY_CHANGE = 'vuexfire/ARRAY_CHANGE'
export const VUEXFIRE_ARRAY_MOVE = 'vuexfire/ARRAY_MOVE'
export const VUEXFIRE_ARRAY_REMOVE = 'vuexfire/ARRAY_REMOVE'
export const VUEXFIRE_MUTATION = 'vuexfire/MUTATION'
<commit_msg>🔥 Remove old mutation type<commit_after>export const VUEXFIRE_OBJECT_VALUE = 'vuexfire/OBJECT_VALUE'
export const VUEXFIRE_ARRAY_INITIALIZE = 'VUEXFIRE_ARRAY_INITIALIZE'
export const VUEXFIRE_ARRAY_ADD = 'vuexfire/ARRAY_ADD'
export const VUEXFIRE_ARRAY_CHANGE = 'vuexfire/ARRAY_CHANGE'
export const VUEXFIRE_ARRAY_MOVE = 'vuexfire/ARRAY_MOVE'
export const VUEXFIRE_ARRAY_REMOVE = 'vuexfire/ARRAY_REMOVE'
<|endoftext|>


<commit_before>module.exports = {
  cookies       : require("cookies-js"),
  eventemitter2 : require("eventemitter2"),
  assign        : require("../polyfills/assign"),
  _             : require("./lodash"),
  isMobile      : require("./is-mobile"),
  uuid          : require("./uuid"),
  domready      : require("./domready"),
  Promise       : require("bluebird"),
  superagent    : require("superagent")
};
<commit_msg>bluebird core only<commit_after>module.exports = {
  cookies       : require("cookies-js"),
  eventemitter2 : require("eventemitter2"),
  assign        : require("../polyfills/assign"),
  _             : require("./lodash"),
  isMobile      : require("./is-mobile"),
  uuid          : require("./uuid"),
  domready      : require("./domready"),
  Promise       : require("./promises"),
  superagent    : require("superagent")
};
<|endoftext|>


<commit_before><commit_msg>add check for window existing in unescapeHTML utils function<commit_after><|endoftext|>


<commit_before>import net from 'net';
import url from 'url';
import crypto from 'crypto';
import ip from 'ip';

export const ATYP_V4 = 1;
export const ATYP_DOMAIN = 3;
export const ATYP_V6 = 4;

export const BYTE_ORDER_BE = 0;
export const BYTE_ORDER_LE = 1;

export class Utils {

  /**
   * convert a number to a buffer with specified length in big-endian
   * @param num
   * @param len
   * @param byteOrder
   * @returns {Buffer}
   */
  static numberToUInt(num, len = 2, byteOrder = BYTE_ORDER_BE) {
    if (len < 1) {
      throw Error('len must be greater than 0');
    }

    const isOutOfRange = num > parseInt(`0x${'ff'.repeat(len)}`);
    if (isOutOfRange) {
      throw Error(`Number ${num} is too long to store in a '${len}' length buffer`);
    }

    const buf = Buffer.alloc(len);
    if (byteOrder === BYTE_ORDER_BE) {
      buf.writeUIntBE(num, 0, len);
    } else {
      buf.writeUIntLE(num, 0, len);
    }
    return buf;
  }

  /**
   * convert an uri to Address
   * @param uri
   * @returns {{type: Number, host: Buffer, port: Buffer}}
   */
  static parseURI(uri) {
    let _uri = uri;
    if (_uri.indexOf('http') !== 0 && _uri.indexOf('https') !== 0) {
      if (_uri.indexOf(':443') !== -1) {
        // e.g, bing.com:443
        _uri = `https://${_uri}`;
      } else {
        // e.g, bing.com
        _uri = `http://${_uri}`;
      }
    }
    const {protocol, hostname} = url.parse(_uri);
    const addrType = net.isIP(hostname) ? (net.isIPv4(hostname) ? ATYP_V4 : ATYP_V6) : ATYP_DOMAIN;
    const port = {'http:': 80, 'https:': 443}[protocol];
    return {
      type: addrType,
      host: net.isIP(hostname) ? ip.toBuffer(hostname) : Buffer.from(hostname),
      port: this.numberToUInt(port)
    };
  }

  /**
   * returns a random integer in [min, max).
   * @param min
   * @param max
   * @returns {Number}
   */
  static getRandomInt(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min)) + min;
  }

  /**
   * split buffer into chunks, each chunk size is picked randomly from [min, max)
   * @param buffer
   * @param min
   * @param max
   * @returns {Array<Buffer>}
   */
  static getRandomChunks(buffer, min, max) {
    const totalLen = buffer.length;
    const bufs = [];
    let ptr = 0;
    while (ptr < totalLen - 1) {
      const offset = this.getRandomInt(min, max);
      bufs.push(buffer.slice(ptr, ptr + offset));
      ptr += offset;
    }
    if (ptr < totalLen) {
      bufs.push(buffer.slice(ptr));
    }
    return bufs;
  }

  /**
   * split buffer into chunks, the max chunk size is maxSize
   * @param buffer
   * @param maxSize
   * @returns {Array<Buffer>}
   */
  static getChunks(buffer, maxSize) {
    const totalLen = buffer.length;
    const bufs = [];
    let ptr = 0;
    while (ptr < totalLen - 1) {
      bufs.push(buffer.slice(ptr, ptr + maxSize));
      ptr += maxSize;
    }
    if (ptr < totalLen) {
      bufs.push(buffer.slice(ptr));
    }
    return bufs;
  }

  /**
   * return UTC timestamp as buffer
   * @returns {Buffer}
   */
  static getUTC() {
    const ts = Math.floor((new Date()).getTime() / 1e3);
    return this.numberToUInt(ts, 4, BYTE_ORDER_BE);
  }

  /**
   * convert string to buffer
   * @param str
   * @returns {Buffer}
   */
  static stringToBuffer(str) {
    return Buffer.from(str, 'hex');
  }

  /**
   * verify hostname
   *
   * @param hostname
   * @returns {boolean}
   *
   * @reference
   *   http://stackoverflow.com/questions/1755144/how-to-validate-domain-name-in-php
   */
  static isValidHostname(hostname) {
    // overall length check
    if (hostname.length < 1 || hostname.length > 253) {
      return false;
    }
    // valid chars check
    if (/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i.test(hostname) === false) {
      return false;
    }
    // length of each label
    if (/^[^.]{1,63}(\.[^.]{1,63})*$/.test(hostname) === false) {
      return false;
    }
    return true;
  }

  /**
   * whether a port is valid or not
   * @param port
   * @returns {boolean}
   */
  static isValidPort(port) {
    if (typeof port !== 'number') {
      return false;
    }
    if (port < 0 || port > 65535) {
      return false;
    }
    return true;
  }

  /**
   * md5 message digest
   * @param buffer
   * @returns {*}
   */
  static md5(buffer) {
    const md5 = crypto.createHash('md5');
    md5.update(buffer);
    return md5.digest();
  }

  /**
   * calculate the HMAC from key and message
   * @param algorithm
   * @param key
   * @param buffer
   * @returns {Buffer}
   */
  static hmac(algorithm, key, buffer) {
    const hmac = crypto.createHmac(algorithm, key);
    return hmac.update(buffer).digest();
  }

  /**
   * EVP_BytesToKey with the digest algorithm set to MD5, one iteration, and no salt
   *
   * @algorithm
   *   D_i = HASH^count(D_(i-1) || data || salt)
   */
  static EVP_BytesToKey(password, keyLen, ivLen) {
    let _data = Buffer.from(password);
    let i = 0;
    const bufs = [];
    while (Buffer.concat(bufs).length < (keyLen + ivLen)) {
      if (i > 0) {
        _data = Buffer.concat([bufs[i - 1], Buffer.from(password)]);
      }
      bufs.push(this.md5(_data));
      i += 1;
    }
    return Buffer.concat(bufs).slice(0, keyLen);
  }

  /**
   * HMAC-based Extract-and-Expand Key Derivation Function
   * @param hash, the message digest algorithm
   * @param salt, a non-secret random value
   * @param ikm, input keying material
   * @param info, optional context and application specific information
   * @param length, length of output keying material in octets
   * @returns {Buffer}
   */
  static HKDF(hash, salt, ikm, info, length) {
    // Step 1: "extract" to fixed length pseudo-random key(prk)
    const prk = this.hmac(hash, salt, ikm);
    // Step 2: "expand" prk to several pseudo-random keys(okm)
    let t = Buffer.alloc(0);
    let okm = Buffer.alloc(0);
    for (let i = 0; i < Math.ceil(length / prk.length); ++i) {
      t = this.hmac(hash, prk, Buffer.concat([t, info, Buffer.alloc(1, i + 1)]));
      okm = Buffer.concat([okm, t]);
    }
    // Step 3: crop okm to desired length
    return okm.slice(0, length);
  }

}
<commit_msg>refactor(utils): getRandomInt now generates cryptographically strong pseudo-random integer<commit_after>import net from 'net';
import url from 'url';
import crypto from 'crypto';
import ip from 'ip';

export const ATYP_V4 = 1;
export const ATYP_DOMAIN = 3;
export const ATYP_V6 = 4;

export const BYTE_ORDER_BE = 0;
export const BYTE_ORDER_LE = 1;

export class Utils {

  /**
   * convert a number to a buffer with specified length in big-endian
   * @param num
   * @param len
   * @param byteOrder
   * @returns {Buffer}
   */
  static numberToUInt(num, len = 2, byteOrder = BYTE_ORDER_BE) {
    if (len < 1) {
      throw Error('len must be greater than 0');
    }

    const isOutOfRange = num > parseInt(`0x${'ff'.repeat(len)}`);
    if (isOutOfRange) {
      throw Error(`Number ${num} is too long to store in a '${len}' length buffer`);
    }

    const buf = Buffer.alloc(len);
    if (byteOrder === BYTE_ORDER_BE) {
      buf.writeUIntBE(num, 0, len);
    } else {
      buf.writeUIntLE(num, 0, len);
    }
    return buf;
  }

  /**
   * convert an uri to Address
   * @param uri
   * @returns {{type: Number, host: Buffer, port: Buffer}}
   */
  static parseURI(uri) {
    let _uri = uri;
    if (_uri.indexOf('http') !== 0 && _uri.indexOf('https') !== 0) {
      if (_uri.indexOf(':443') !== -1) {
        // e.g, bing.com:443
        _uri = `https://${_uri}`;
      } else {
        // e.g, bing.com
        _uri = `http://${_uri}`;
      }
    }
    const {protocol, hostname} = url.parse(_uri);
    const addrType = net.isIP(hostname) ? (net.isIPv4(hostname) ? ATYP_V4 : ATYP_V6) : ATYP_DOMAIN;
    const port = {'http:': 80, 'https:': 443}[protocol];
    return {
      type: addrType,
      host: net.isIP(hostname) ? ip.toBuffer(hostname) : Buffer.from(hostname),
      port: this.numberToUInt(port)
    };
  }

  /**
   * returns a random integer in [min, max].
   * @param min
   * @param max
   * @returns {Number}
   */
  static getRandomInt(min, max) {
    min = Math.ceil(min);
    max = Math.ceil(max);
    return Math.floor(crypto.randomBytes(1)[0] / 0xff * (max - min + 1)) + min;
  }

  /**
   * split buffer into chunks, each chunk size is picked randomly from [min, max]
   * @param buffer
   * @param min
   * @param max
   * @returns {Array<Buffer>}
   */
  static getRandomChunks(buffer, min, max) {
    const totalLen = buffer.length;
    const bufs = [];
    let ptr = 0;
    while (ptr < totalLen - 1) {
      const offset = this.getRandomInt(min, max);
      bufs.push(buffer.slice(ptr, ptr + offset));
      ptr += offset;
    }
    if (ptr < totalLen) {
      bufs.push(buffer.slice(ptr));
    }
    return bufs;
  }

  /**
   * split buffer into chunks, the max chunk size is maxSize
   * @param buffer
   * @param maxSize
   * @returns {Array<Buffer>}
   */
  static getChunks(buffer, maxSize) {
    const totalLen = buffer.length;
    const bufs = [];
    let ptr = 0;
    while (ptr < totalLen - 1) {
      bufs.push(buffer.slice(ptr, ptr + maxSize));
      ptr += maxSize;
    }
    if (ptr < totalLen) {
      bufs.push(buffer.slice(ptr));
    }
    return bufs;
  }

  /**
   * return UTC timestamp as buffer
   * @returns {Buffer}
   */
  static getUTC() {
    const ts = Math.floor((new Date()).getTime() / 1e3);
    return this.numberToUInt(ts, 4, BYTE_ORDER_BE);
  }

  /**
   * convert string to buffer
   * @param str
   * @returns {Buffer}
   */
  static stringToBuffer(str) {
    return Buffer.from(str, 'hex');
  }

  /**
   * verify hostname
   *
   * @param hostname
   * @returns {boolean}
   *
   * @reference
   *   http://stackoverflow.com/questions/1755144/how-to-validate-domain-name-in-php
   */
  static isValidHostname(hostname) {
    // overall length check
    if (hostname.length < 1 || hostname.length > 253) {
      return false;
    }
    // valid chars check
    if (/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i.test(hostname) === false) {
      return false;
    }
    // length of each label
    if (/^[^.]{1,63}(\.[^.]{1,63})*$/.test(hostname) === false) {
      return false;
    }
    return true;
  }

  /**
   * whether a port is valid or not
   * @param port
   * @returns {boolean}
   */
  static isValidPort(port) {
    if (typeof port !== 'number') {
      return false;
    }
    if (port < 0 || port > 65535) {
      return false;
    }
    return true;
  }

  /**
   * md5 message digest
   * @param buffer
   * @returns {*}
   */
  static md5(buffer) {
    const md5 = crypto.createHash('md5');
    md5.update(buffer);
    return md5.digest();
  }

  /**
   * calculate the HMAC from key and message
   * @param algorithm
   * @param key
   * @param buffer
   * @returns {Buffer}
   */
  static hmac(algorithm, key, buffer) {
    const hmac = crypto.createHmac(algorithm, key);
    return hmac.update(buffer).digest();
  }

  /**
   * EVP_BytesToKey with the digest algorithm set to MD5, one iteration, and no salt
   *
   * @algorithm
   *   D_i = HASH^count(D_(i-1) || data || salt)
   */
  static EVP_BytesToKey(password, keyLen, ivLen) {
    let _data = Buffer.from(password);
    let i = 0;
    const bufs = [];
    while (Buffer.concat(bufs).length < (keyLen + ivLen)) {
      if (i > 0) {
        _data = Buffer.concat([bufs[i - 1], Buffer.from(password)]);
      }
      bufs.push(this.md5(_data));
      i += 1;
    }
    return Buffer.concat(bufs).slice(0, keyLen);
  }

  /**
   * HMAC-based Extract-and-Expand Key Derivation Function
   * @param hash, the message digest algorithm
   * @param salt, a non-secret random value
   * @param ikm, input keying material
   * @param info, optional context and application specific information
   * @param length, length of output keying material in octets
   * @returns {Buffer}
   */
  static HKDF(hash, salt, ikm, info, length) {
    // Step 1: "extract" to fixed length pseudo-random key(prk)
    const prk = this.hmac(hash, salt, ikm);
    // Step 2: "expand" prk to several pseudo-random keys(okm)
    let t = Buffer.alloc(0);
    let okm = Buffer.alloc(0);
    for (let i = 0; i < Math.ceil(length / prk.length); ++i) {
      t = this.hmac(hash, prk, Buffer.concat([t, info, Buffer.alloc(1, i + 1)]));
      okm = Buffer.concat([okm, t]);
    }
    // Step 3: crop okm to desired length
    return okm.slice(0, length);
  }

}
<|endoftext|>


<commit_before>/*global CallPower, Backbone */

(function () {
  CallPower.Views.StatisticsView = Backbone.View.extend({
    el: $('#statistics'),

    events: {
      'change select[name="campaigns"]': 'renderChart',
      'change select[name="timespan"]': 'renderChart',
    },

    initialize: function() {
      this.$el.find('.input-daterange input').each(function (){
        $(this).datepicker({
          'format': "yyyy/mm/dd"
        });
      });

      this.renderChart();
      this.chartOpts = {
        "library":{"canvasDimensions":{"height":250}},
      };

      this.$el.on('changeDate', this.renderChart);
      this.$el.on('changeDate', this.renderChart);
    },

    renderChart: function(event) {
      var campaign = $('select[name="campaigns"]').val();
      if (campaign === "") {
        campaign = 1;
      }
      var timespan = $('select[name="timespan"]').val();
      var start = new Date($('input[name="start"]').datepicker('getDate')).toISOString();
      var end = new Date($('input[name="end"]').datepicker('getDate')).toISOString();
      console.log(start, end);

      var dataUrl = '/api/campaign/'+campaign+'/stats.json?timespan='+timespan;
      if (start) {
        dataUrl += ('&start='+start);
      }
      if (end) {
        dataUrl += ('&end='+end);
      }

      this.chart = new Chartkick.LineChart(
          'calls_for_campaign', dataUrl, this.chartOpts
      );
    }

  });

})();<commit_msg>remove console.log<commit_after>/*global CallPower, Backbone */

(function () {
  CallPower.Views.StatisticsView = Backbone.View.extend({
    el: $('#statistics'),

    events: {
      'change select[name="campaigns"]': 'renderChart',
      'change select[name="timespan"]': 'renderChart',
    },

    initialize: function() {
      this.$el.find('.input-daterange input').each(function (){
        $(this).datepicker({
          'format': "yyyy/mm/dd"
        });
      });

      this.renderChart();
      this.chartOpts = {
        "library":{"canvasDimensions":{"height":250}},
      };

      this.$el.on('changeDate', this.renderChart);
      this.$el.on('changeDate', this.renderChart);
    },

    renderChart: function(event) {
      var campaign = $('select[name="campaigns"]').val();
      if (campaign === "") {
        campaign = 1;
      }
      var timespan = $('select[name="timespan"]').val();
      var start = new Date($('input[name="start"]').datepicker('getDate')).toISOString();
      var end = new Date($('input[name="end"]').datepicker('getDate')).toISOString();

      var dataUrl = '/api/campaign/'+campaign+'/stats.json?timespan='+timespan;
      if (start) {
        dataUrl += ('&start='+start);
      }
      if (end) {
        dataUrl += ('&end='+end);
      }

      this.chart = new Chartkick.LineChart('calls_for_campaign', dataUrl, this.chartOpts);
    }

  });

})();<|endoftext|>


<commit_before>var _ = require('lodash');
var Boom = require('boom');
var Bluebird = require('bluebird');

var mongo;

var valid_actions = {
    put_job: 'PUT',
    get_job: 'GET',
    destroy_job: 'DELETE',
    list_jobs: 'GET',
    job_history: 'GET',
    reserve_jobs: 'POST',
    update_job: 'PUT',
};

return function (context, req, res) {
    var action = validate_method(valid_actions);
    var maxJobsPerContainer = parseInt(context.data.max_jobs_per_container, 10) || 100;
    var now = new Date();

    if (!validate_params(['JOB_COLLECTION', 'LOG_COLLECTION', 'MONGO_URL', 'cluster_url']))
        return;

    if (action === 'put_job') {
        // Create or update a scheduled webtask

        if (!validate_params(['container', 'name']))
            return;

        if (!validate_body(['schedule', 'token']))
            return;

        var update = {
            $set: {
                state: 'active',
                schedule: context.body.schedule,
                token: context.body.token,
                cluster_url: context.data.cluster_url,
                container: context.data.container,
                name: context.data.name,
                last_scheduled_at: now,
                next_available_at: now,
            },
            $setOnInsert: {
                last_result: null,
                run_count: 0,
                error_count: 0,
            }
        };

        return withMongoCollection(context.data.JOB_COLLECTION)
            .then(function (coll) {
                var countExistingCursor = coll.find({
                    cluster_url: context.data.cluster_url,
                    container: context.data.container,
                });
                var countExisting = Bluebird.promisify(countExistingCursor.count, countExistingCursor)();

                var alreadyExistsCursor = coll.find({
                    cluster_url: context.data.cluster_url,
                    container: context.data.container,
                    name: context.data.name,
                });
                var alreadyExists = Bluebird.promisify(alreadyExistsCursor.count, alreadyExistsCursor)();

                return Bluebird.all([countExisting, alreadyExists])
                    .then(function (counts) {
                        var sameContainerCount = counts[0];
                        var exists = !!counts[1];

                        if (!exists && sameContainerCount >= maxJobsPerContainer) {
                            throw Boom.badRequest('Unable to schedule more than '
                                + maxJobsPerContainer
                                + ' jobs per container.');
                        }

                        return coll.findOneAndUpdateAsync({
                            cluster_url: context.data.cluster_url,
                            container: context.data.container,
                            name: context.data.name,
                        }, update, {
                            returnOriginal: false,
                            upsert: true,
                        });
                    });
            })
            .get('value')
            .then(stripMongoId)
            .then(function (data) {
                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify(data));
            })
            .catch(respondWithError);
    } else if (action === 'reserve_jobs') {
        // Reserve a set of jobs for execution

        if (!validate_params(['count', 'ttl']))
            return;

        var next_available_at = new Date(now.valueOf() + (parseInt(context.data.ttl, 10) * 1000));

        return withMongoCollection(context.data.JOB_COLLECTION)
            .then(function (coll) {
                return Bluebird.resolve(_.range(context.data.count))
                    // TODO (ggoodman): Refactor this to use reduce to avoid calling N queries if reservation fails before N
                    .map(function (n) {
                        var filter = {
                            cluster_url: context.data.cluster_url,
                            next_available_at: {
                                $lte: now
                            },
                            state: 'active',
                        };
                        var update = {
                            $set: {
                                next_available_at: next_available_at
                            }
                        };

                        return coll.findOneAndUpdateAsync(filter, update, {
                            returnOriginal: false, // Return modified
                        })
                            .get('value'); // Only pull out the value

                    })
                    // Don't prevent one failure from blocking the entire
                    // reservation (that would mean extra waiting on those that
                    // didn't fail)
                    .settle()
                    // Log errors and continue with safe fallback
                    .map(function (result) {
                        if (result.isRejected()) {
                            return null;
                        } else {
                            return result.value();
                        }
                    })
                    // Elminate error queries and queries that did not match
                    // (this is the case when there are fewer than N jobs
                    // available for running now)
                    .filter(Boolean);

            })
            .map(stripMongoId)
            .then(function (data) {
                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify(data));
            })
            .catch(respondWithError);
    } else if (action === 'list_jobs') {
        // List all scheduled webtasks or only those in a container passed via query

        return withMongoCollection(context.data.JOB_COLLECTION)
            .then(function (coll) {
                var query = {
                    cluster_url: context.data.cluster_url,
                };

                // Both admin and user will hit this endpoint. When admin, container can be unset
                if (context.data.container) query.container = context.data.container;

                // TODO (ggoodman): Pagination logic
                return coll.findAsync(query)
                    .then(function (cursor) {
                        return Bluebird.promisify(cursor.toArray, cursor)();
                    });
            })
            .map(stripMongoId)
            .then(function (data) {
                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify(data));
            })
            .catch(respondWithError);

    } else if (action === 'get_job') {
        // Get a single scheduled webtask

        if (!validate_params(['container', 'name']))
            return;

        return withMongoCollection(context.data.JOB_COLLECTION)
            .then(function (coll) {
                var query = {
                    cluster_url: context.data.cluster_url,
                    container: context.data.container,
                    name: context.data.name,
                };

                return coll.findOneAsync(query, {});
            })
            .then(stripMongoId)
            .then(function (data) {
                if (!data) throw Boom.notFound();

                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify(data));
            })
            .catch(respondWithError);

    } else if (action === 'destroy_job') {
        // Get a single scheduled webtask

        if (!validate_params(['container', 'name']))
            return;

        var filter = {
            cluster_url: context.data.cluster_url,
            container: context.data.container,
            name: context.data.name,
        };

        var deleteJob = withMongoCollection(context.data.JOB_COLLECTION)
            .then(function (coll) {
                return coll.deleteOne(filter, {w: 1});
            });

        var deleteLogs = withMongoCollection(context.data.LOG_COLLECTION)
            .then(function (coll) {
                return coll.deleteMany(filter, {w: 1});
            });

        Bluebird.all([deleteJob, deleteLogs])
            .then(function (data) {
                res.writeHead(204);
                res.end();
            })
            .catch(respondWithError);

    } else if (action === 'update_job') {
        // Update a single scheduled webtask based on criteria

        if (!validate_params(['container', 'name']))
            return;

        if (!validate_body(['criteria', 'updates']))
            return;

        if (!_.isObject(context.body.criteria)) return respondWithError(Boom.badRequest('Expecting criteria to be an object'));
        if (!_.isObject(context.body.updates)) return respondWithError(Boom.badRequest('Expecting updates to be an object'));

        var criteria = context.body.criteria;
        var updates = context.body.updates;
        var result = updates.last_result;

        var makeMongoDates = function (obj, fields) {
            _.forEach(fields, function (field) {
                if (obj[field]) obj[field] = new Date(obj[field]);
            });
        };

        makeMongoDates(criteria, ['last_scheduled_at', 'next_available_at']);
        makeMongoDates(updates, ['last_scheduled_at', 'next_available_at']);

        // This update includes a last_result, so we will add it to our log
        if (result) {
            // Fire and forget this as considered non-critical
            withMongoCollection(context.data.LOG_COLLECTION)
                .then(function (coll) {
                    var logEntry = _.extend(result, {
                        cluster_url: context.data.cluster_url,
                        container: context.data.container,
                        name: context.data.name,
                        created_at: new Date(),
                    });

                    return coll.insertOneAsync(logEntry, {w: 1});
                });
        }

        return withMongoCollection(context.data.JOB_COLLECTION)
            .then(function (coll) {
                var filter = _.defaults({
                    cluster_url: context.data.cluster_url,
                    container: context.data.container,
                    name: context.data.name,
                }, context.body.critera);

                var update = {
                    $set: context.body.updates,
                };

                // Track stats based on result
                if (result) {
                    if (result.type == 'success') update.$inc = { run_count: 1};
                    if (result.type == 'error') update.$inc = { error_count: 1};
                }

                return coll.findOneAndUpdateAsync(filter, update)
                    .then(stripMongoId);
            })
            .then(function (data) {
                // There may be no jobs that match criteria, resulting in
                // data being null

                if (!data) throw Boom.notFound('No job matched the supplied criteria');

                res.writeHead(200);
                res.end(JSON.stringify(data));
            })
            .catch(respondWithError);
    } else if (action === 'job_history') {
        // List all scheduled webtask's result history

        if (!validate_params(['container', 'name']))
            return;

        return withMongoCollection(context.data.LOG_COLLECTION)
            .then(function (coll) {
                var query = {
                    cluster_url: context.data.cluster_url,
                    container: context.data.container,
                    name: context.data.name,
                };

                var cursor = coll.find(query)
                    .sort({created_at: -1});

                if (context.data.limit) cursor.limit(parseInt(context.data.limit, 10));
                if (context.data.skip) cursor.skip(parseInt(context.data.skip, 0));

                var fetchResults = Bluebird.promisify(cursor.toArray, cursor);

                // TODO (ggoodman): Pagination logic
                return fetchResults();
            })
            .map(stripMongoId)
            .then(function (data) {
                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify(data));
            })
            .catch(respondWithError);

    } else {
        // TODO (ggoodman): Consider: https://github.com/auth0/auth0-sandbox-ext/pull/1#discussion_r32170649

        return respondWithError(Boom.methodNotAllowed('Invalid method'));
    }

    // Helper methods

    function stripMongoId (doc) {
        if (doc) {
            if (doc._id) delete doc._id;
            if (doc.last_result && doc.last_result._id) delete doc.last_result._id;
        }

        return doc;
    }

    function withMongoDb () {
        if (mongo) return mongo;

        var MongoClient = require('mongodb').MongoClient;
        var connect = Bluebird.promisify(MongoClient.connect, MongoClient);

        return connect(context.data.MONGO_URL)
            .then(function (db) {
                // Store the settled promise resolving to the db object
                mongo = Bluebird.resolve(db);

                return db;
            })
            .catch(function (err) {
                console.log("Database error:", err);
                console.trace(err);

                throw Boom.wrap(err, 'Database unreachable');
            });
    }

    function withMongoCollection (collName) {
        return withMongoDb()
            .call('collection', collName) // Get a Collection handle
            .then(Bluebird.promisifyAll) // Promisify all methods of Connection
            .catch(function (err) {
                console.log("Database error:", err);
                console.trace(err);

                throw Boom.wrap(err, 'Database collection unreachable');
            })
    }

    function validate_method (valid_actions) {
        var action = context.data.action;
        var expected_method = valid_actions[action];

        if (!action) return respondWithError(Boom.badRequest('Missing action.'));
        if (!expected_method) return respondWithError(Boom.badRequest('Invalid action.'));
        if (expected_method !== req.method) return respondWithError(Boom.methodNotAllowed('Invalid method.'));

        return action;
    }

    function validate_params(required_params) {
        for (var i in required_params) {
            if (typeof context.data[required_params[i]] !== 'string') {
                return respondWithError(Boom.badRequest('Missing query parameter ' + required_params[i] + '.'));
            }
        }
        return true;
    }

    function validate_body(required_fields) {
        for (var i in required_fields) {
            if (!context.body[required_fields[i]]) {
                return respondWithError(Boom.badRequest('Missing payload parameter ' + required_fields[i] + '.'));
            }
        }
        return true;
    }

    function respondWithError (err) {
        if (!err.isBoom) err = Boom.wrap(err);

        res.writeHead(err.output.statusCode, err.output.headers);
        res.end(JSON.stringify(err.output.payload));

        return false;
    }
}<commit_msg>Add additional error information<commit_after>var _ = require('lodash');
var Boom = require('boom');
var Bluebird = require('bluebird');

var mongo;

var valid_actions = {
    put_job: 'PUT',
    get_job: 'GET',
    destroy_job: 'DELETE',
    list_jobs: 'GET',
    job_history: 'GET',
    reserve_jobs: 'POST',
    update_job: 'PUT',
};

return function (context, req, res) {
    var action = validate_method(valid_actions);
    var maxJobsPerContainer = parseInt(context.data.max_jobs_per_container, 10) || 100;
    var now = new Date();

    if (!validate_params(['JOB_COLLECTION', 'LOG_COLLECTION', 'MONGO_URL', 'cluster_url']))
        return;

    if (action === 'put_job') {
        // Create or update a scheduled webtask

        if (!validate_params(['container', 'name']))
            return;

        if (!validate_body(['schedule', 'token']))
            return;

        var update = {
            $set: {
                state: 'active',
                schedule: context.body.schedule,
                token: context.body.token,
                cluster_url: context.data.cluster_url,
                container: context.data.container,
                name: context.data.name,
                last_scheduled_at: now,
                next_available_at: now,
            },
            $setOnInsert: {
                last_result: null,
                run_count: 0,
                error_count: 0,
            }
        };

        return withMongoCollection(context.data.JOB_COLLECTION)
            .then(function (coll) {
                var countExistingCursor = coll.find({
                    cluster_url: context.data.cluster_url,
                    container: context.data.container,
                });
                var countExisting = Bluebird.promisify(countExistingCursor.count, countExistingCursor)();

                var alreadyExistsCursor = coll.find({
                    cluster_url: context.data.cluster_url,
                    container: context.data.container,
                    name: context.data.name,
                });
                var alreadyExists = Bluebird.promisify(alreadyExistsCursor.count, alreadyExistsCursor)();

                return Bluebird.all([countExisting, alreadyExists])
                    .then(function (counts) {
                        var sameContainerCount = counts[0];
                        var exists = !!counts[1];

                        if (!exists && sameContainerCount >= maxJobsPerContainer) {
                            throw Boom.badRequest('Unable to schedule more than '
                                + maxJobsPerContainer
                                + ' jobs per container.');
                        }

                        return coll.findOneAndUpdateAsync({
                            cluster_url: context.data.cluster_url,
                            container: context.data.container,
                            name: context.data.name,
                        }, update, {
                            returnOriginal: false,
                            upsert: true,
                        });
                    });
            })
            .get('value')
            .then(stripMongoId)
            .then(function (data) {
                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify(data));
            })
            .catch(respondWithError);
    } else if (action === 'reserve_jobs') {
        // Reserve a set of jobs for execution

        if (!validate_params(['count', 'ttl']))
            return;

        var next_available_at = new Date(now.valueOf() + (parseInt(context.data.ttl, 10) * 1000));

        return withMongoCollection(context.data.JOB_COLLECTION)
            .then(function (coll) {
                return Bluebird.resolve(_.range(context.data.count))
                    // TODO (ggoodman): Refactor this to use reduce to avoid calling N queries if reservation fails before N
                    .map(function (n) {
                        var filter = {
                            cluster_url: context.data.cluster_url,
                            next_available_at: {
                                $lte: now
                            },
                            state: 'active',
                        };
                        var update = {
                            $set: {
                                next_available_at: next_available_at
                            }
                        };

                        return coll.findOneAndUpdateAsync(filter, update, {
                            returnOriginal: false, // Return modified
                        })
                            .get('value'); // Only pull out the value

                    })
                    // Don't prevent one failure from blocking the entire
                    // reservation (that would mean extra waiting on those that
                    // didn't fail)
                    .settle()
                    // Log errors and continue with safe fallback
                    .map(function (result) {
                        if (result.isRejected()) {
                            return null;
                        } else {
                            return result.value();
                        }
                    })
                    // Elminate error queries and queries that did not match
                    // (this is the case when there are fewer than N jobs
                    // available for running now)
                    .filter(Boolean);

            })
            .map(stripMongoId)
            .then(function (data) {
                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify(data));
            })
            .catch(respondWithError);
    } else if (action === 'list_jobs') {
        // List all scheduled webtasks or only those in a container passed via query

        return withMongoCollection(context.data.JOB_COLLECTION)
            .then(function (coll) {
                var query = {
                    cluster_url: context.data.cluster_url,
                };

                // Both admin and user will hit this endpoint. When admin, container can be unset
                if (context.data.container) query.container = context.data.container;

                // TODO (ggoodman): Pagination logic
                return coll.findAsync(query)
                    .then(function (cursor) {
                        return Bluebird.promisify(cursor.toArray, cursor)();
                    });
            })
            .map(stripMongoId)
            .then(function (data) {
                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify(data));
            })
            .catch(respondWithError);

    } else if (action === 'get_job') {
        // Get a single scheduled webtask

        if (!validate_params(['container', 'name']))
            return;

        return withMongoCollection(context.data.JOB_COLLECTION)
            .then(function (coll) {
                var query = {
                    cluster_url: context.data.cluster_url,
                    container: context.data.container,
                    name: context.data.name,
                };

                return coll.findOneAsync(query, {});
            })
            .then(stripMongoId)
            .then(function (data) {
                if (!data) throw Boom.notFound();

                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify(data));
            })
            .catch(respondWithError);

    } else if (action === 'destroy_job') {
        // Get a single scheduled webtask

        if (!validate_params(['container', 'name']))
            return;

        var filter = {
            cluster_url: context.data.cluster_url,
            container: context.data.container,
            name: context.data.name,
        };

        var deleteJob = withMongoCollection(context.data.JOB_COLLECTION)
            .then(function (coll) {
                return coll.deleteOne(filter, {w: 1});
            });

        var deleteLogs = withMongoCollection(context.data.LOG_COLLECTION)
            .then(function (coll) {
                return coll.deleteMany(filter, {w: 1});
            });

        Bluebird.all([deleteJob, deleteLogs])
            .then(function (data) {
                res.writeHead(204);
                res.end();
            })
            .catch(respondWithError);

    } else if (action === 'update_job') {
        // Update a single scheduled webtask based on criteria

        if (!validate_params(['container', 'name']))
            return;

        if (!validate_body(['criteria', 'updates']))
            return;

        if (!_.isObject(context.body.criteria)) return respondWithError(Boom.badRequest('Expecting criteria to be an object'));
        if (!_.isObject(context.body.updates)) return respondWithError(Boom.badRequest('Expecting updates to be an object'));

        var criteria = context.body.criteria;
        var updates = context.body.updates;
        var result = updates.last_result;

        var makeMongoDates = function (obj, fields) {
            _.forEach(fields, function (field) {
                if (obj[field]) obj[field] = new Date(obj[field]);
            });
        };

        makeMongoDates(criteria, ['last_scheduled_at', 'next_available_at']);
        makeMongoDates(updates, ['last_scheduled_at', 'next_available_at']);

        // This update includes a last_result, so we will add it to our log
        if (result) {
            // Fire and forget this as considered non-critical
            withMongoCollection(context.data.LOG_COLLECTION)
                .then(function (coll) {
                    var logEntry = _.extend(result, {
                        cluster_url: context.data.cluster_url,
                        container: context.data.container,
                        name: context.data.name,
                        created_at: new Date(),
                    });

                    return coll.insertOneAsync(logEntry, {w: 1});
                });
        }

        return withMongoCollection(context.data.JOB_COLLECTION)
            .then(function (coll) {
                var filter = _.defaults({
                    cluster_url: context.data.cluster_url,
                    container: context.data.container,
                    name: context.data.name,
                }, context.body.critera);

                var update = {
                    $set: context.body.updates,
                };

                // Track stats based on result
                if (result) {
                    if (result.type == 'success') update.$inc = { run_count: 1};
                    if (result.type == 'error') update.$inc = { error_count: 1};
                }

                return coll.findOneAndUpdateAsync(filter, update)
                    .then(stripMongoId);
            })
            .then(function (data) {
                // There may be no jobs that match criteria, resulting in
                // data being null

                if (!data) throw Boom.notFound('No job matched the supplied criteria');

                res.writeHead(200);
                res.end(JSON.stringify(data));
            })
            .catch(respondWithError);
    } else if (action === 'job_history') {
        // List all scheduled webtask's result history

        if (!validate_params(['container', 'name']))
            return;

        return withMongoCollection(context.data.LOG_COLLECTION)
            .then(function (coll) {
                var query = {
                    cluster_url: context.data.cluster_url,
                    container: context.data.container,
                    name: context.data.name,
                };

                var cursor = coll.find(query)
                    .sort({created_at: -1});

                if (context.data.limit) cursor.limit(parseInt(context.data.limit, 10));
                if (context.data.skip) cursor.skip(parseInt(context.data.skip, 0));

                var fetchResults = Bluebird.promisify(cursor.toArray, cursor);

                // TODO (ggoodman): Pagination logic
                return fetchResults();
            })
            .map(stripMongoId)
            .then(function (data) {
                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify(data));
            })
            .catch(respondWithError);

    } else {
        // TODO (ggoodman): Consider: https://github.com/auth0/auth0-sandbox-ext/pull/1#discussion_r32170649

        return respondWithError(Boom.methodNotAllowed('Invalid method'));
    }

    // Helper methods

    function stripMongoId (doc) {
        if (doc) {
            if (doc._id) delete doc._id;
            if (doc.last_result && doc.last_result._id) delete doc.last_result._id;
        }

        return doc;
    }

    function withMongoDb () {
        if (mongo) return mongo;

        var MongoClient = require('mongodb').MongoClient;
        var connect = Bluebird.promisify(MongoClient.connect, MongoClient);

        return connect(context.data.MONGO_URL)
            .then(function (db) {
                // Store the settled promise resolving to the db object
                mongo = Bluebird.resolve(db);

                return db;
            })
            .catch(function (err) {
                console.log("Database error:", err);
                console.trace(err);

                throw Boom.wrap(err, 'Database unreachable');
            });
    }

    function withMongoCollection (collName) {
        return withMongoDb()
            .call('collection', collName) // Get a Collection handle
            .then(Bluebird.promisifyAll) // Promisify all methods of Connection
            .catch(function (err) {
                console.log("Database error:", err);
                console.trace(err);

                throw Boom.wrap(err, 'Database collection unreachable');
            })
    }

    function validate_method (valid_actions) {
        var action = context.data.action;
        var expected_method = valid_actions[action];

        if (!action) return respondWithError(Boom.badRequest('Missing action.'));
        if (!expected_method) return respondWithError(Boom.badRequest('Invalid action.'));
        if (expected_method !== req.method) return respondWithError(Boom.methodNotAllowed('Invalid method.'));

        return action;
    }

    function validate_params(required_params) {
        for (var i in required_params) {
            if (typeof context.data[required_params[i]] !== 'string') {
                return respondWithError(Boom.badRequest('Missing query parameter ' + required_params[i] + '.'));
            }
        }
        return true;
    }

    function validate_body(required_fields) {
        for (var i in required_fields) {
            if (!context.body[required_fields[i]]) {
                return respondWithError(Boom.badRequest('Missing payload parameter ' + required_fields[i] + '.'));
            }
        }
        return true;
    }

    function respondWithError (err) {
        if (!err.isBoom) err = Boom.wrap(err, 502);

        err.output.payload.stack = err.stack;

        res.writeHead(err.output.statusCode, err.output.headers);
        res.end(JSON.stringify(err.output.payload));

        return false;
    }
}<|endoftext|>


<commit_before>/*
    ___ usage ___ en_US ___
    usage: prolific <protocol> <protocol args>

            --help                      display this message

    ___ $ ___ en_US ___

        udp is required:
            the `--udp` address and port is a required argument

        port is not an integer:
            the `--udp` port must be an integer

    ___ . ___
*/
require('arguable')(module, require('cadence')(function (async, program) {
    program.helpIf(program.param.help)

    var pkg = 'prolific.' + program.argv.shift()

    var io = {
        stdout: program.stdout,
        stdin: program.stdin,
        stderr: program.stderr,
        events: program.events
    }

    require(pkg)(process.env, program.argv, io, async())
}))
<commit_msg>Add shebang and set execute bit.<commit_after>#!/bin/bash

/*
    ___ usage ___ en_US ___
    usage: prolific <protocol> <protocol args>

            --help                      display this message

    ___ $ ___ en_US ___

        udp is required:
            the `--udp` address and port is a required argument

        port is not an integer:
            the `--udp` port must be an integer

    ___ . ___
*/
require('arguable')(module, require('cadence')(function (async, program) {
    program.helpIf(program.param.help)

    var pkg = 'prolific.' + program.argv.shift()

    var io = {
        stdout: program.stdout,
        stdin: program.stdin,
        stderr: program.stderr,
        events: program.events
    }

    require(pkg)(process.env, program.argv, io, async())
}))
<|endoftext|>


<commit_before>import Component from '@glimmer/component';
import {action} from '@ember/object';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';

/**
 * @typedef {import('../../services/dashboard-stats').SourceAttributionCount} SourceAttributionCount
*/

const DISPLAY_OPTIONS = [{
    name: 'Free signups',
    value: 'signups'
}, {
    name: 'Paid conversions',
    value: 'paid'
}];

export default class Analytics extends Component {
    @service ajax;
    @service ghostPaths;
    @service settings;
    @service membersUtils;
    @service utils;
    @service feature;

    @tracked sources = null;
    @tracked links = null;
    @tracked sortColumn = 'signups';
    @tracked showSuccess;
    @tracked updateLinkId;
    displayOptions = DISPLAY_OPTIONS;

    get post() {
        return this.args.post;
    }

    get allowedDisplayOptions() {
        if (!this.hasPaidConversionData) {
            return this.displayOptions.filter(d => d.value === 'signups');
        }

        if (!this.hasFreeSignups) {
            return this.displayOptions.filter(d => d.value === 'paid');
        }

        return this.displayOptions;
    }

    get isDropdownDisabled() {
        if (!this.hasPaidConversionData || !this.hasFreeSignups) {
            return true;
        }

        return false;
    }

    get selectedDisplayOption() {
        if (!this.hasPaidConversionData) {
            return this.displayOptions.find(d => d.value === 'signups');
        }

        if (!this.hasFreeSignups) {
            return this.displayOptions.find(d => d.value === 'paid');
        }

        return this.displayOptions.find(d => d.value === this.sortColumn) ?? this.displayOptions[0];
    }

    get selectedSortColumn() {
        if (!this.hasPaidConversionData) {
            return 'signups';
        }

        if (!this.hasFreeSignups) {
            return 'paid';
        }
        return this.sortColumn;
    }

    get hasPaidConversionData() {
        return this.sources.some(sourceData => sourceData.paidConversions > 0);
    }

    get hasFreeSignups() {
        return this.sources.some(sourceData => sourceData.signups > 0);
    }

    get totalFeedback() {
        return this.post.count.positive_feedback + this.post.count.negative_feedback;
    }

    get feedbackChartData() {
        const values = [this.post.count.positive_feedback, this.post.count.negative_feedback];
        const labels = ['More like this', 'Less like this'];
        const links = [
            {filterParam: '(feedback.post_id:6368b75db2faed03adaee6b7+feedback.score:1)'},
            {filterParam: '(feedback.post_id:6368b75db2faed03adaee6b7+feedback.score:0)'}
        ];
        const colors = ['#F080B2', '#8452f633'];
        return {values, labels, links, colors};
    }

    @action
    onDisplayChange(selected) {
        this.sortColumn = selected.value;
    }

    @action
    setSortColumn(column) {
        this.sortColumn = column;
    }

    @action
    updateLink(linkId, linkTo) {
        if (this._updateLinks.isRunning) {
            return this._updateLinks.last;
        }
        return this._updateLinks.perform(linkId, linkTo);
    }

    @action
    loadData() {
        if (this.showSources) {
            this.fetchReferrersStats();
        } else {
            this.sources = [];
        }

        if (this.showLinks) {
            this.fetchLinks();
        } else {
            this.links = [];
        }
    }

    updateLinkData(linksData) {
        let updatedLinks;
        if (this.links?.length) {
            updatedLinks = this.links.map((link) => {
                let linkData = linksData.find(l => l.link.link_id === link.link.link_id);
                if (linkData) {
                    return {
                        ...linkData,
                        link: {
                            ...linkData.link,
                            originalTo: linkData.link.to,
                            to: this.utils.cleanTrackedUrl(linkData.link.to, false),
                            title: this.utils.cleanTrackedUrl(linkData.link.to, true)
                        }
                    };
                }
                return link;
            });
        } else {
            updatedLinks = linksData.map((link) => {
                return {
                    ...link,
                    link: {
                        ...link.link,
                        originalTo: link.link.to,
                        to: this.utils.cleanTrackedUrl(link.link.to, false),
                        title: this.utils.cleanTrackedUrl(link.link.to, true)
                    }
                };
            });
        }

        // Remove duplicates by title ad merge
        const linksByTitle = updatedLinks.reduce((acc, link) => {
            if (!acc[link.link.title]) {
                acc[link.link.title] = link;
            } else {
                acc[link.link.title].clicks += link.clicks;
            }
            return acc;
        }, {});

        this.links = Object.values(linksByTitle);
    }

    async fetchReferrersStats() {
        if (this._fetchReferrersStats.isRunning) {
            return this._fetchReferrersStats.last;
        }
        return this._fetchReferrersStats.perform();
    }

    async fetchLinks() {
        if (this._fetchLinks.isRunning) {
            return this._fetchLinks.last;
        }
        return this._fetchLinks.perform();
    }

    @task
    *_updateLinks(linkId, newLink) {
        this.updateLinkId = linkId;
        let currentLink;
        this.links = this.links?.map((link) => {
            if (link.link.link_id === linkId) {
                currentLink = new URL(link.link.originalTo);
                return {
                    ...link,
                    link: {
                        ...link.link,
                        to: this.utils.cleanTrackedUrl(newLink, false),
                        title: this.utils.cleanTrackedUrl(newLink, true)
                    }
                };
            }
            return link;
        });

        const filter = `post_id:${this.post.id}+to:'${currentLink}'`;
        let bulkUpdateUrl = this.ghostPaths.url.api(`links/bulk`) + `?filter=${encodeURIComponent(filter)}`;
        yield this.ajax.put(bulkUpdateUrl, {
            data: {
                bulk: {
                    action: 'updateLink',
                    meta: {link: {to: newLink}}
                }
            }
        });

        // Refresh links data
        const linksFilter = `post_id:${this.post.id}`;
        let statsUrl = this.ghostPaths.url.api(`links/`) + `?filter=${encodeURIComponent(linksFilter)}`;
        let result = yield this.ajax.request(statsUrl);
        this.updateLinkData(result.links);
        this.showSuccess = this.updateLinkId;
        setTimeout(() => {
            this.showSuccess = null;
        }, 2000);
    }

    @task
    *_fetchReferrersStats() {
        let statsUrl = this.ghostPaths.url.api(`stats/referrers/posts/${this.post.id}`);
        let result = yield this.ajax.request(statsUrl);
        this.sources = result.stats.map((stat) => {
            return {
                source: stat.source ?? 'Direct',
                signups: stat.signups,
                paidConversions: stat.paid_conversions
            };
        });
    }

    @task
    *_fetchLinks() {
        const filter = `post_id:${this.post.id}`;
        let statsUrl = this.ghostPaths.url.api(`links/`) + `?filter=${encodeURIComponent(filter)}`;
        let result = yield this.ajax.request(statsUrl);
        this.updateLinkData(result.links);
    }

    get showLinks() {
        return this.post.showEmailClickAnalytics;
    }

    get showSources() {
        return this.feature.get('sourceAttribution') && this.post.showAttributionAnalytics;
    }

    get isLoaded() {
        return this.links !== null && this.souces !== null;
    }
}
<commit_msg>Fixed analytics page feedback chart filter button links<commit_after>import Component from '@glimmer/component';
import {action} from '@ember/object';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';

/**
 * @typedef {import('../../services/dashboard-stats').SourceAttributionCount} SourceAttributionCount
*/

const DISPLAY_OPTIONS = [{
    name: 'Free signups',
    value: 'signups'
}, {
    name: 'Paid conversions',
    value: 'paid'
}];

export default class Analytics extends Component {
    @service ajax;
    @service ghostPaths;
    @service settings;
    @service membersUtils;
    @service utils;
    @service feature;

    @tracked sources = null;
    @tracked links = null;
    @tracked sortColumn = 'signups';
    @tracked showSuccess;
    @tracked updateLinkId;
    displayOptions = DISPLAY_OPTIONS;

    get post() {
        return this.args.post;
    }

    get allowedDisplayOptions() {
        if (!this.hasPaidConversionData) {
            return this.displayOptions.filter(d => d.value === 'signups');
        }

        if (!this.hasFreeSignups) {
            return this.displayOptions.filter(d => d.value === 'paid');
        }

        return this.displayOptions;
    }

    get isDropdownDisabled() {
        if (!this.hasPaidConversionData || !this.hasFreeSignups) {
            return true;
        }

        return false;
    }

    get selectedDisplayOption() {
        if (!this.hasPaidConversionData) {
            return this.displayOptions.find(d => d.value === 'signups');
        }

        if (!this.hasFreeSignups) {
            return this.displayOptions.find(d => d.value === 'paid');
        }

        return this.displayOptions.find(d => d.value === this.sortColumn) ?? this.displayOptions[0];
    }

    get selectedSortColumn() {
        if (!this.hasPaidConversionData) {
            return 'signups';
        }

        if (!this.hasFreeSignups) {
            return 'paid';
        }
        return this.sortColumn;
    }

    get hasPaidConversionData() {
        return this.sources.some(sourceData => sourceData.paidConversions > 0);
    }

    get hasFreeSignups() {
        return this.sources.some(sourceData => sourceData.signups > 0);
    }

    get totalFeedback() {
        return this.post.count.positive_feedback + this.post.count.negative_feedback;
    }

    get feedbackChartData() {
        const values = [this.post.count.positive_feedback, this.post.count.negative_feedback];
        const labels = ['More like this', 'Less like this'];
        const links = [
            {filterParam: '(feedback.post_id:' + this.post.id + '+feedback.score:1)'},
            {filterParam: '(feedback.post_id:' + this.post.id + '+feedback.score:0)'}
        ];
        const colors = ['#F080B2', '#8452f633'];
        return {values, labels, links, colors};
    }

    @action
    onDisplayChange(selected) {
        this.sortColumn = selected.value;
    }

    @action
    setSortColumn(column) {
        this.sortColumn = column;
    }

    @action
    updateLink(linkId, linkTo) {
        if (this._updateLinks.isRunning) {
            return this._updateLinks.last;
        }
        return this._updateLinks.perform(linkId, linkTo);
    }

    @action
    loadData() {
        if (this.showSources) {
            this.fetchReferrersStats();
        } else {
            this.sources = [];
        }

        if (this.showLinks) {
            this.fetchLinks();
        } else {
            this.links = [];
        }
    }

    updateLinkData(linksData) {
        let updatedLinks;
        if (this.links?.length) {
            updatedLinks = this.links.map((link) => {
                let linkData = linksData.find(l => l.link.link_id === link.link.link_id);
                if (linkData) {
                    return {
                        ...linkData,
                        link: {
                            ...linkData.link,
                            originalTo: linkData.link.to,
                            to: this.utils.cleanTrackedUrl(linkData.link.to, false),
                            title: this.utils.cleanTrackedUrl(linkData.link.to, true)
                        }
                    };
                }
                return link;
            });
        } else {
            updatedLinks = linksData.map((link) => {
                return {
                    ...link,
                    link: {
                        ...link.link,
                        originalTo: link.link.to,
                        to: this.utils.cleanTrackedUrl(link.link.to, false),
                        title: this.utils.cleanTrackedUrl(link.link.to, true)
                    }
                };
            });
        }

        // Remove duplicates by title ad merge
        const linksByTitle = updatedLinks.reduce((acc, link) => {
            if (!acc[link.link.title]) {
                acc[link.link.title] = link;
            } else {
                acc[link.link.title].clicks += link.clicks;
            }
            return acc;
        }, {});

        this.links = Object.values(linksByTitle);
    }

    async fetchReferrersStats() {
        if (this._fetchReferrersStats.isRunning) {
            return this._fetchReferrersStats.last;
        }
        return this._fetchReferrersStats.perform();
    }

    async fetchLinks() {
        if (this._fetchLinks.isRunning) {
            return this._fetchLinks.last;
        }
        return this._fetchLinks.perform();
    }

    @task
    *_updateLinks(linkId, newLink) {
        this.updateLinkId = linkId;
        let currentLink;
        this.links = this.links?.map((link) => {
            if (link.link.link_id === linkId) {
                currentLink = new URL(link.link.originalTo);
                return {
                    ...link,
                    link: {
                        ...link.link,
                        to: this.utils.cleanTrackedUrl(newLink, false),
                        title: this.utils.cleanTrackedUrl(newLink, true)
                    }
                };
            }
            return link;
        });

        const filter = `post_id:${this.post.id}+to:'${currentLink}'`;
        let bulkUpdateUrl = this.ghostPaths.url.api(`links/bulk`) + `?filter=${encodeURIComponent(filter)}`;
        yield this.ajax.put(bulkUpdateUrl, {
            data: {
                bulk: {
                    action: 'updateLink',
                    meta: {link: {to: newLink}}
                }
            }
        });

        // Refresh links data
        const linksFilter = `post_id:${this.post.id}`;
        let statsUrl = this.ghostPaths.url.api(`links/`) + `?filter=${encodeURIComponent(linksFilter)}`;
        let result = yield this.ajax.request(statsUrl);
        this.updateLinkData(result.links);
        this.showSuccess = this.updateLinkId;
        setTimeout(() => {
            this.showSuccess = null;
        }, 2000);
    }

    @task
    *_fetchReferrersStats() {
        let statsUrl = this.ghostPaths.url.api(`stats/referrers/posts/${this.post.id}`);
        let result = yield this.ajax.request(statsUrl);
        this.sources = result.stats.map((stat) => {
            return {
                source: stat.source ?? 'Direct',
                signups: stat.signups,
                paidConversions: stat.paid_conversions
            };
        });
    }

    @task
    *_fetchLinks() {
        const filter = `post_id:${this.post.id}`;
        let statsUrl = this.ghostPaths.url.api(`links/`) + `?filter=${encodeURIComponent(filter)}`;
        let result = yield this.ajax.request(statsUrl);
        this.updateLinkData(result.links);
    }

    get showLinks() {
        return this.post.showEmailClickAnalytics;
    }

    get showSources() {
        return this.feature.get('sourceAttribution') && this.post.showAttributionAnalytics;
    }

    get isLoaded() {
        return this.links !== null && this.souces !== null;
    }
}
<|endoftext|>


<commit_before>/**
 *
 *    Copyright (c) 2020 Silicon Labs
 *
 *    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.
 */

/**
 * This module provides mappings between database columns and JS keys.
 *
 * @module DB API: DB mappings between columns and JS object keys.
 */

exports.map = {
  package: (x) => {
    if (x == null) return undefined
    return {
      id: x.PACKAGE_ID,
      path: x.PATH,
      crc: x.CRC,
      type: x.TYPE,
      version: x.VERSION,
      parentId: x.PARENT_PACKAGE_REF,
    }
  },
  options: (x) => {
    if (x == null) return undefined
    return {
      id: x.OPTION_ID,
      packageRef: x.PACKAGE_REF,
      optionCategory: x.OPTION_CATEGORY,
      optionCode: x.OPTION_CODE,
      optionLabel: x.OPTION_LABEL,
    }
  },
  optionDefaults: (x) => {
    if (x == null) return undefined
    return {
      id: x.OPTION_DEFAULT_ID,
      packageRef: x.PACKAGE_REF,
      optionCategory: x.OPTION_CATEGORY,
      optionRef: x.OPTION_REF,
    }
  },
  trackedFile: (x) => {
    if (x == null) return undefined
    return {
      path: x.PATH,
      crc: x.CRC,
    }
  },
  cluster: (x) => {
    if (x == null) return undefined
    return {
      id: x.CLUSTER_ID,
      code: x.CODE,
      manufacturerCode: x.MANUFACTURER_CODE,
      label: x.NAME,
      caption: x.DESCRIPTION,
      define: x.DEFINE,
      domainName: x.DOMAIN_NAME,
    }
  },

  attribute: (x) => {
    if (x == null) return undefined
    return {
      id: x.ATTRIBUTE_ID,
      clusterRef: x.CLUSTER_REF,
      code: x.CODE,
      clusterCode: x.CLUSTER_CODE,
      manufacturerCode: x.MANUFACTURER_CODE,
      label: x.NAME,
      type: x.TYPE,
      side: x.SIDE,
      define: x.DEFINE,
      min: x.MIN,
      max: x.MAX,
      minLength: x.MIN_LENGTH,
      maxLength: x.MAX_LENGTH,
      isWritable: x.IS_WRITABLE,
      defaultValue: x.DEFAULT_VALUE,
      isOptional: x.IS_OPTIONAL,
      isReportable: x.IS_REPORTABLE,
    }
  },

  command: (x) => {
    if (x == null) return undefined
    return {
      id: x.COMMAND_ID,
      clusterRef: x.CLUSTER_REF,
      code: x.CODE,
      manufacturerCode: x.MANUFACTURER_CODE,
      label: x.NAME,
      name: x.NAME,
      description: x.DESCRIPTION,
      source: x.SOURCE,
      isOptional: x.IS_OPTIONAL,
      clusterCode: x.CLUSTER_CODE,
      clusterName: x.CLUSTER_NAME,
      argName: x.ARG_NAME,
      argType: x.ARG_TYPE,
      argIsArray: x.ARG_IS_ARRAY,
    }
  },

  commandArgument: (x) => {
    if (x == null) return undefined
    return {
      commandRef: x.COMMAND_REF,
      label: x.NAME,
      name: x.NAME,
      type: x.TYPE,
      code: x.CODE,
      isArray: x.IS_ARRAY,
      caption: `Command argument of type ${x.TYPE}`,
    }
  },

  domain: (x) => {
    if (x == null) return undefined
    return {
      id: x.DOMAIN_ID,
      label: x.NAME,
      caption: `Domain, named ${x.NAME}`,
    }
  },

  enum: (x) => {
    if (x == null) return undefined
    return {
      id: x.ENUM_ID,
      label: x.NAME,
      type: x.TYPE,
      caption: `Enum of type ${x.TYPE}`,
    }
  },

  enumItem: (x) => {
    if (x == null) return undefined
    return {
      label: x.NAME,
      value: x.VALUE,
      enumRef: x.ENUM_REF,
      caption: `EnumItem, named ${x.NAME}`,
    }
  },

  struct: (x) => {
    if (x == null) return undefined
    return {
      id: x.STRUCT_ID,
      label: x.NAME,
      itemCnt: x.ITEM_COUNT,
      caption: `Struct, named ${x.NAME}`,
    }
  },

  structItem: (x) => {
    if (x == null) return undefined
    return {
      label: x.NAME,
      structRef: x.STRUCT_REF,
      type: x.TYPE,
      caption: `Struct Item of type ${x.TYPE}`,
    }
  },

  atomic: (x) => {
    if (x == null) return undefined
    return {
      atomicId: x.ATOMIC_IDENTIFIER,
      name: x.NAME,
      description: x.DESCRIPTION,
      size: x.ATOMIC_SIZE,
      discrete: x.DISCETE,
    }
  },

  bitmap: (x) => {
    if (x == null) return undefined
    return {
      id: x.BITMAP_ID,
      label: x.NAME,
      type: x.TYPE,
    }
  },

  bitmapField: (x) => {
    if (x == null) return undefined
    return {
      label: x.NAME,
      mask: x.MASK,
      bitmapRef: x.BITMAP_REF,
      caption: `BitmapField, named ${x.NAME}`,
    }
  },

  deviceType: (x) => {
    if (x == null) return undefined
    return {
      id: x.DEVICE_TYPE_ID,
      code: x.CODE,
      profileId: x.PROFILE_ID,
      domain: x.DOMAIN,
      label: x.NAME,
      caption: x.DESCRIPTION,
    }
  },

  deviceTypeCluster: (x) => {
    if (x == null) return undefined
    return {
      id: x.DEVICE_TYPE_CLUSTER_ID,
      deviceTypeRef: x.DEVICE_TYPE_REF,
      clusterRef: x.CLUSTER_REF,
      clusterName: x.CLUSTER_NAME,
      includeClient: x.INCLUDE_CLIENT,
      includeServer: x.INCLUDE_SERVER,
      lockClient: x.LOCK_CLIENT,
      lockServer: x.LOCK_SERVER,
    }
  },

  deviceTypeAttribute: (x) => {
    if (x == null) return undefined
    return {
      deviceTypeClusterRef: x.DEVICE_TYPE_CLUSTER_REF,
      attributeRef: x.ATTRIBUTE_REF,
      attributeName: x.ATTRIBUTE_NAME,
    }
  },

  deviceTypeCommand: (x) => {
    if (x == null) return undefined
    return {
      deviceTypeClusterRef: x.DEVICE_TYPE_CLUSTER_REF,
      commandRef: x.COMMAND_REF,
      commandName: x.COMMAND_NAME,
    }
  },

  endpoint: (x) => {
    if (x == null) return undefined
    return {
      id: x.ENDPOINT_ID,
      endpointRef: x.ENDPOINT_ID,
      sessionRef: x.SESSION_REF,
      endpointId: x.ENDPOINT_IDENTIFIER,
      endpointTypeRef: x.ENDPOINT_TYPE_REF,
      profileId: x.PROFILE,
      networkId: x.NETWORK_IDENTIFIER,
    }
  },
  endpointType: (x) => {
    if (x == null) return undefined
    return {
      id: x.ENDPOINT_TYPE_ID,
      endpointTypeId: x.ENDPOINT_TYPE_ID,
      sessionRef: x.SESSION_REF,
      name: x.NAME,
      deviceTypeRef: x.DEVICE_TYPE_REF,
    }
  },
  endpointTypeCluster: (x) => {
    if (x == null) return undefined
    return {
      endpointTypeClusterId: x.ENDPOINT_TYPE_CLUSTER_ID,
      endpointTypeRef: x.ENDPOINT_TYPE_REF,
      clusterRef: x.CLUSTER_REF,
      side: x.SIDE,
      enabled: x.ENABLED,
    }
  },

  endpointTypeAttribute: (x) => {
    if (x == null) return undefined
    return {
      endpointTypeRef: x.ENDPOINT_TYPE_REF,
      clusterRef: x.CLUSTER_REF,
      attributeRef: x.ATTRIBUTE_REF,
      included: x.INCLUDED,
      storageOption: x.STORAGE_OPTION,
      singleton: x.SINGLETON,
      bounded: x.BOUNDED,
      defaultValue: x.DEFAULT_VALUE,
      includedReportable: x.INCLUDED_REPORTABLE,
      minInterval: x.MIN_INTERVAL,
      maxInterval: x.MAX_INTERVAL,
      reportableChange: x.REPORTABLE_CHANGE,
    }
  },

  endpointTypeCommand: (x) => {
    if (x == null) return undefined
    return {
      endpointTypeRef: x.ENDPOINT_TYPE_REF,
      clusterRef: x.CLUSTER_REF,
      commandRef: x.COMMAND_REF,
      incoming: x.INCOMING,
      outgoing: x.OUTGOING,
    }
  },

  packageExtension: (x) => {
    if (x == null) return undefined
    return {
      entity: x.ENTITY,
      property: x.PROPERTY,
      type: x.TYPE,
      configurability: x.CONFIGURABILITY,
      label: x.LABEL,
      globalDefault: x.GLOBAL_DEFAULT,
    }
  },
  packageExtensionDefault: (x) => {
    if (x == null) return undefined
    return {
      entityCode: x.ENTITY_CODE,
      parentCode: x.PARENT_CODE,
      value: x.VALUE,
    }
  },
}

exports.reverseMap = {
  endpoint: {
    endpointRef: 'ENDPOINT_ID',
    sessionRef: 'SESSION_REF',
    endpointId: 'ENDPOINT_IDENTIFIER',
    endpointTypeRef: 'ENDPOINT_TYPE_REF',
    profileId: 'PROFILE',
    networkId: 'NETWORK_IDENTIFIER',
  },
}
<commit_msg>Fix a typo when mapping the atomics discrete property from the database to js object<commit_after>/**
 *
 *    Copyright (c) 2020 Silicon Labs
 *
 *    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.
 */

/**
 * This module provides mappings between database columns and JS keys.
 *
 * @module DB API: DB mappings between columns and JS object keys.
 */

exports.map = {
  package: (x) => {
    if (x == null) return undefined
    return {
      id: x.PACKAGE_ID,
      path: x.PATH,
      crc: x.CRC,
      type: x.TYPE,
      version: x.VERSION,
      parentId: x.PARENT_PACKAGE_REF,
    }
  },
  options: (x) => {
    if (x == null) return undefined
    return {
      id: x.OPTION_ID,
      packageRef: x.PACKAGE_REF,
      optionCategory: x.OPTION_CATEGORY,
      optionCode: x.OPTION_CODE,
      optionLabel: x.OPTION_LABEL,
    }
  },
  optionDefaults: (x) => {
    if (x == null) return undefined
    return {
      id: x.OPTION_DEFAULT_ID,
      packageRef: x.PACKAGE_REF,
      optionCategory: x.OPTION_CATEGORY,
      optionRef: x.OPTION_REF,
    }
  },
  trackedFile: (x) => {
    if (x == null) return undefined
    return {
      path: x.PATH,
      crc: x.CRC,
    }
  },
  cluster: (x) => {
    if (x == null) return undefined
    return {
      id: x.CLUSTER_ID,
      code: x.CODE,
      manufacturerCode: x.MANUFACTURER_CODE,
      label: x.NAME,
      caption: x.DESCRIPTION,
      define: x.DEFINE,
      domainName: x.DOMAIN_NAME,
    }
  },

  attribute: (x) => {
    if (x == null) return undefined
    return {
      id: x.ATTRIBUTE_ID,
      clusterRef: x.CLUSTER_REF,
      code: x.CODE,
      clusterCode: x.CLUSTER_CODE,
      manufacturerCode: x.MANUFACTURER_CODE,
      label: x.NAME,
      type: x.TYPE,
      side: x.SIDE,
      define: x.DEFINE,
      min: x.MIN,
      max: x.MAX,
      minLength: x.MIN_LENGTH,
      maxLength: x.MAX_LENGTH,
      isWritable: x.IS_WRITABLE,
      defaultValue: x.DEFAULT_VALUE,
      isOptional: x.IS_OPTIONAL,
      isReportable: x.IS_REPORTABLE,
    }
  },

  command: (x) => {
    if (x == null) return undefined
    return {
      id: x.COMMAND_ID,
      clusterRef: x.CLUSTER_REF,
      code: x.CODE,
      manufacturerCode: x.MANUFACTURER_CODE,
      label: x.NAME,
      name: x.NAME,
      description: x.DESCRIPTION,
      source: x.SOURCE,
      isOptional: x.IS_OPTIONAL,
      clusterCode: x.CLUSTER_CODE,
      clusterName: x.CLUSTER_NAME,
      argName: x.ARG_NAME,
      argType: x.ARG_TYPE,
      argIsArray: x.ARG_IS_ARRAY,
    }
  },

  commandArgument: (x) => {
    if (x == null) return undefined
    return {
      commandRef: x.COMMAND_REF,
      label: x.NAME,
      name: x.NAME,
      type: x.TYPE,
      code: x.CODE,
      isArray: x.IS_ARRAY,
      caption: `Command argument of type ${x.TYPE}`,
    }
  },

  domain: (x) => {
    if (x == null) return undefined
    return {
      id: x.DOMAIN_ID,
      label: x.NAME,
      caption: `Domain, named ${x.NAME}`,
    }
  },

  enum: (x) => {
    if (x == null) return undefined
    return {
      id: x.ENUM_ID,
      label: x.NAME,
      type: x.TYPE,
      caption: `Enum of type ${x.TYPE}`,
    }
  },

  enumItem: (x) => {
    if (x == null) return undefined
    return {
      label: x.NAME,
      value: x.VALUE,
      enumRef: x.ENUM_REF,
      caption: `EnumItem, named ${x.NAME}`,
    }
  },

  struct: (x) => {
    if (x == null) return undefined
    return {
      id: x.STRUCT_ID,
      label: x.NAME,
      itemCnt: x.ITEM_COUNT,
      caption: `Struct, named ${x.NAME}`,
    }
  },

  structItem: (x) => {
    if (x == null) return undefined
    return {
      label: x.NAME,
      structRef: x.STRUCT_REF,
      type: x.TYPE,
      caption: `Struct Item of type ${x.TYPE}`,
    }
  },

  atomic: (x) => {
    if (x == null) return undefined
    return {
      atomicId: x.ATOMIC_IDENTIFIER,
      name: x.NAME,
      description: x.DESCRIPTION,
      size: x.ATOMIC_SIZE,
      discrete: x.DISCRETE,
    }
  },

  bitmap: (x) => {
    if (x == null) return undefined
    return {
      id: x.BITMAP_ID,
      label: x.NAME,
      type: x.TYPE,
    }
  },

  bitmapField: (x) => {
    if (x == null) return undefined
    return {
      label: x.NAME,
      mask: x.MASK,
      bitmapRef: x.BITMAP_REF,
      caption: `BitmapField, named ${x.NAME}`,
    }
  },

  deviceType: (x) => {
    if (x == null) return undefined
    return {
      id: x.DEVICE_TYPE_ID,
      code: x.CODE,
      profileId: x.PROFILE_ID,
      domain: x.DOMAIN,
      label: x.NAME,
      caption: x.DESCRIPTION,
    }
  },

  deviceTypeCluster: (x) => {
    if (x == null) return undefined
    return {
      id: x.DEVICE_TYPE_CLUSTER_ID,
      deviceTypeRef: x.DEVICE_TYPE_REF,
      clusterRef: x.CLUSTER_REF,
      clusterName: x.CLUSTER_NAME,
      includeClient: x.INCLUDE_CLIENT,
      includeServer: x.INCLUDE_SERVER,
      lockClient: x.LOCK_CLIENT,
      lockServer: x.LOCK_SERVER,
    }
  },

  deviceTypeAttribute: (x) => {
    if (x == null) return undefined
    return {
      deviceTypeClusterRef: x.DEVICE_TYPE_CLUSTER_REF,
      attributeRef: x.ATTRIBUTE_REF,
      attributeName: x.ATTRIBUTE_NAME,
    }
  },

  deviceTypeCommand: (x) => {
    if (x == null) return undefined
    return {
      deviceTypeClusterRef: x.DEVICE_TYPE_CLUSTER_REF,
      commandRef: x.COMMAND_REF,
      commandName: x.COMMAND_NAME,
    }
  },

  endpoint: (x) => {
    if (x == null) return undefined
    return {
      id: x.ENDPOINT_ID,
      endpointRef: x.ENDPOINT_ID,
      sessionRef: x.SESSION_REF,
      endpointId: x.ENDPOINT_IDENTIFIER,
      endpointTypeRef: x.ENDPOINT_TYPE_REF,
      profileId: x.PROFILE,
      networkId: x.NETWORK_IDENTIFIER,
    }
  },
  endpointType: (x) => {
    if (x == null) return undefined
    return {
      id: x.ENDPOINT_TYPE_ID,
      endpointTypeId: x.ENDPOINT_TYPE_ID,
      sessionRef: x.SESSION_REF,
      name: x.NAME,
      deviceTypeRef: x.DEVICE_TYPE_REF,
    }
  },
  endpointTypeCluster: (x) => {
    if (x == null) return undefined
    return {
      endpointTypeClusterId: x.ENDPOINT_TYPE_CLUSTER_ID,
      endpointTypeRef: x.ENDPOINT_TYPE_REF,
      clusterRef: x.CLUSTER_REF,
      side: x.SIDE,
      enabled: x.ENABLED,
    }
  },

  endpointTypeAttribute: (x) => {
    if (x == null) return undefined
    return {
      endpointTypeRef: x.ENDPOINT_TYPE_REF,
      clusterRef: x.CLUSTER_REF,
      attributeRef: x.ATTRIBUTE_REF,
      included: x.INCLUDED,
      storageOption: x.STORAGE_OPTION,
      singleton: x.SINGLETON,
      bounded: x.BOUNDED,
      defaultValue: x.DEFAULT_VALUE,
      includedReportable: x.INCLUDED_REPORTABLE,
      minInterval: x.MIN_INTERVAL,
      maxInterval: x.MAX_INTERVAL,
      reportableChange: x.REPORTABLE_CHANGE,
    }
  },

  endpointTypeCommand: (x) => {
    if (x == null) return undefined
    return {
      endpointTypeRef: x.ENDPOINT_TYPE_REF,
      clusterRef: x.CLUSTER_REF,
      commandRef: x.COMMAND_REF,
      incoming: x.INCOMING,
      outgoing: x.OUTGOING,
    }
  },

  packageExtension: (x) => {
    if (x == null) return undefined
    return {
      entity: x.ENTITY,
      property: x.PROPERTY,
      type: x.TYPE,
      configurability: x.CONFIGURABILITY,
      label: x.LABEL,
      globalDefault: x.GLOBAL_DEFAULT,
    }
  },
  packageExtensionDefault: (x) => {
    if (x == null) return undefined
    return {
      entityCode: x.ENTITY_CODE,
      parentCode: x.PARENT_CODE,
      value: x.VALUE,
    }
  },
}

exports.reverseMap = {
  endpoint: {
    endpointRef: 'ENDPOINT_ID',
    sessionRef: 'SESSION_REF',
    endpointId: 'ENDPOINT_IDENTIFIER',
    endpointTypeRef: 'ENDPOINT_TYPE_REF',
    profileId: 'PROFILE',
    networkId: 'NETWORK_IDENTIFIER',
  },
}
<|endoftext|>


<commit_before>/**
 * Defines a Reset CSS for Action Drawer elements. Webpages may have
 * specialized styling for <div>, <p>, or other types of elements.
 * This set will essentially reset the styles that are applied to
 * elements underneath the root Action Drawer.
 *
 * If there were more adoption, this would be the same as applying a
 * scoped <style> element. That would also allow us to avoid prefixing
 * all selectors with the drawer ID to have higher specificity.
 *
 * The convention in the related modules will be to start the CSS
 * declaration with the drawerSelector, and to join each subsequent
 * line with a space and the drawerSelector.
 *
 * This assumes that each line holds a single selector, and that
 * selectors are not split between lines. Exceptions to this format
 * should be handled individually.
 *
 * (Formatting the CSS this way reduces the clutter of having the
 *  same text repeated constantly)
 *
 */
betterlink_user_interface.createModule("Drawer Reset CSS", function(api, apiInternal) {
	var drawerSelector = "#betterlink-drawer ";
	apiInternal.drawerResetCss =
		[drawerSelector + "div { background: inherit;",
								"border: none;",
								"border-radius: 0;",
								"margin: 0;",
								"padding: 0; }"].join(' ');

	apiInternal.drawerSelector = drawerSelector;
});
<commit_msg>extend reset CSS to more elements<commit_after>/**
 * Defines a Reset CSS for Action Drawer elements. Webpages may have
 * specialized styling for <div>, <p>, or other types of elements.
 * This set will essentially reset the styles that are applied to
 * elements underneath the root Action Drawer.
 *
 * If there were more adoption, this would be the same as applying a
 * scoped <style> element. That would also allow us to avoid prefixing
 * all selectors with the drawer ID to have higher specificity.
 *
 * The convention in the related modules will be to start the CSS
 * declaration with the drawerSelector, and to join each subsequent
 * line with a space and the drawerSelector.
 *
 * This assumes that each line holds a single selector, and that
 * selectors are not split between lines. Exceptions to this format
 * should be handled individually.
 *
 * (Formatting the CSS this way reduces the clutter of having the
 *  same text repeated constantly)
 *
 */
betterlink_user_interface.createModule("Drawer Reset CSS", function(api, apiInternal) {
	var drawerSelector = "#betterlink-drawer ";
	var elements = drawerSelector + ["div","span","h1","h2","p","article","aside","footer","header","nav","section"].join(',' + drawerSelector);
	apiInternal.drawerResetCss =
		[   elements +"{ border: none;",
						"border-radius: 0;",
						"margin: 0;",
						"padding: 0;",
						"vertical-align: baseline;",
						"font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; }",

			drawerSelector + "div { background: inherit; }"].join(' ');

	apiInternal.drawerSelector = drawerSelector;
});
<|endoftext|>