<commit_before>var Vec3 = require('vec3').Vec3;
var _ = require('lodash');
var Entity = require('../entity');
var conv = require('../conversions');
var NAMED_ENTITY_HEIGHT = 1.62;
var CROUCH_HEIGHT = NAMED_ENTITY_HEIGHT - 0.08;
var entitiesData = require('node-minecraft-data').entities;
var Item = require('../item');

module.exports = inject;

var animationEvents = {
  0: 'entitySwingArm',
  1: 'entityHurt',
  2: 'entityWake',
  3: 'entityEat',
  4: 'entityCriticalEffect',
  5: 'entityMagicCriticalEffect'
};

var entityStatusEvents = {
  2: 'entityHurt',
  3: 'entityDead',
  6: 'entityTaming',
  7: 'entityTamed',
  8: 'entityShakingOffWater',
  10: 'entityEatingGrass',
};

function inject(bot) {

  bot.findPlayer = bot.findPlayers = function(filter) {
    var filterFn = function(entity) {
      if(entity.type !== 'player') return false;
      if(filter == null) return true;
      if(typeof filter === 'object' && filter instanceof RegExp) {
        return entity.username.search(filter) !== -1;
      } else if(typeof filter === 'function') {
        return filter(entity);
      } else if(typeof filter === 'string') {
        return entity.username.toLowerCase() === filter.toLowerCase();
      }
      return false;
    };
    var resultSet = _.transform(bot.entities, function(players, entity) {
      if(filterFn(entity)) players.push(entity);
    }, []);

    if(typeof filter === 'string') {
      switch(resultSet.length) {
        case 0:
          return null;
        case 1:
          return resultSet[0];
        default:
          return resultSet;
      }
    }
    return resultSet;
  };

  bot.players = {};
  bot.uuidToUsername = {};
  bot.entities = {};

  bot._client.once('login', function(packet) {
    // login
    bot.entity = fetchEntity(packet.entityId);
    bot.entity.username = bot.username;
    bot.entity.type = 'player';
  });

  bot._client.on('entity_equipment', function(packet) {
    // entity equipment
    var entity = fetchEntity(packet.entityId);
    entity.setEquipment(packet.slot, packet.item ? Item.fromNotch(packet.item) : null);
    bot.emit('entityEquip', entity);
  });

  bot._client.on('bed', function(packet) {
    // use bed
    var entity = fetchEntity(packet.entityId);
    entity.position.set(packet.location.x, packet.location.y, packet.location.z);
    bot.emit('entitySleep', entity);
  });

  bot._client.on('animation', function(packet) {
    // animation
    var entity = fetchEntity(packet.entityId);
    var eventName = animationEvents[packet.animation];
    if(eventName) bot.emit(eventName, entity);
  });

  bot._client.on('named_entity_spawn', function(packet) {
    // in case player_info packet was not sent before named_entity_spawn : ignore named_entity_spawn (see #213)
    if(packet.playerUUID in bot.uuidToUsername) {
      // spawn named entity
      var entity = fetchEntity(packet.entityId);
      entity.type = 'player';
      entity.username = bot.uuidToUsername[packet.playerUUID];
      entity.uuid = packet.playerUUID;
      entity.dataBlobs = packet.data;
      entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
      entity.yaw = conv.fromNotchianYawByte(packet.yaw);
      entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
      entity.height = NAMED_ENTITY_HEIGHT;
      entity.metadata = parseMetadata(packet.metadata);
      if(!bot.players[entity.username].entity) {
        bot.players[entity.username].entity = entity;
      }
      bot.emit('entitySpawn', entity);
    }
  });
  bot.on("entityCrouch", function(entity) {
    entity.height = CROUCH_HEIGHT;
  });
  bot.on("entityUncrouch", function(entity) {
    entity.height = NAMED_ENTITY_HEIGHT;
  });

  bot._client.on('collect', function(packet) {
    // collect item
    var collector = fetchEntity(packet.collectorEntityId);
    var collected = fetchEntity(packet.collectedEntityId);
    bot.emit('playerCollect', collector, collected);
  });

  bot._client.on('spawn_entity', function(packet) {
    // spawn object/vehicle
    var entity = fetchEntity(packet.entityId);
    entity.type = 'object';
    var entityData = entitiesData[packet.type];
    entity.objectType = entityData.displayName;
    entity.displayName = entityData.displayName;
    entity.entityType = entityData.id;
    entity.name = entityData.name;
    entity.kind = entityData.type;
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    entity.objectData = packet.objectData;
    bot.emit('entitySpawn', entity);
  });

  bot._client.on('spawn_entity_experience_orb', function(packet) {
    var entity = fetchEntity(packet.entityId);
    entity.type = 'orb';
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.count = packet.count;
    bot.emit('entitySpawn', entity);
  });

  bot._client.on('spawn_entity_living', function(packet) {
    // spawn mob
    var entity = fetchEntity(packet.entityId);
    entity.type = 'mob';
    var entityData = entitiesData[packet.type];
    entity.mobType = entityData.displayName;
    entity.displayName = entityData.displayName;
    entity.entityType = entityData.id;
    entity.name = entityData.name;
    entity.kind = entityData.type;
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    entity.headPitch = conv.fromNotchianPitchByte(packet.headPitch);
    var notchVel = new Vec3(packet.velocityX, packet.velocityY, packet.velocityZ);
    entity.velocity.update(conv.fromNotchVelocity(notchVel));
    entity.metadata = parseMetadata(packet.metadata);
    bot.emit('entitySpawn', entity);
  });

  bot._client.on('entity_velocity', function(packet) {
    // entity velocity
    var entity = fetchEntity(packet.entityId);
    var notchVel = new Vec3(packet.velocityX, packet.velocityY, packet.velocityZ);
    entity.velocity.update(conv.fromNotchVelocity(notchVel));
  });

  bot._client.on('entity_destroy', function(packet) {
    // destroy entity
    packet.entityIds.forEach(function(id) {
      var entity = fetchEntity(id);
      bot.emit('entityGone', entity);
      entity.isValid = false;
      if(entity.username && bot.players[entity.username]) {
        bot.players[entity.username].entity = null;
      }
      delete bot.entities[id];
    });
  });

  bot._client.on('rel_entity_move', function(packet) {
    // entity relative move
    var entity = fetchEntity(packet.entityId);
    entity.position.translate(packet.dX / 32, packet.dY / 32, packet.dZ / 32);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_look', function(packet) {
    // entity look
    var entity = fetchEntity(packet.entityId);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_move_look', function(packet) {
    // entity look and relative move
    var entity = fetchEntity(packet.entityId);
    entity.position.translate(packet.dX / 32, packet.dY / 32, packet.dZ / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_teleport', function(packet) {
    // entity teleport
    var entity = fetchEntity(packet.entityId);
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_head_rotation', function(packet) {
    // entity head look
    var entity = fetchEntity(packet.entityId);
    entity.headYaw = conv.fromNotchianYawByte(packet.headYaw);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_status', function(packet) {
    // entity status
    var entity = fetchEntity(packet.entityId);
    var eventName = entityStatusEvents[packet.status];
    if(eventName) bot.emit(eventName, entity);
  });

  bot._client.on('attach_entity', function(packet) {
    // attach entity
    var entity = fetchEntity(packet.entityId);
    if(packet.vehicleId === -1) {
      var vehicle = entity.vehicle;
      delete entity.vehicle;
      bot.emit('entityDetach', entity, vehicle);
    } else {
      entity.vehicle = fetchEntity(packet.vehicleId);
      bot.emit('entityAttach', entity, entity.vehicle);
    }
  });

  bot._client.on('entity_metadata', function(packet) {
    // entity metadata
    var entity = fetchEntity(packet.entityId);
    entity.metadata = parseMetadata(packet.metadata);
    bot.emit('entityUpdate', entity);
  });

  bot._client.on('entity_effect', function(packet) {
    // entity effect
    var entity = fetchEntity(packet.entityId);
    var effect = {
      id: packet.effectId,
      amplifier: packet.amplifier,
      duration: packet.duration,
    };
    entity.effects[effect.id] = effect;
    bot.emit('entityEffect', entity, effect);
  });

  bot._client.on('remove_entity_effect', function(packet) {
    // remove entity effect
    var entity = fetchEntity(packet.entityId);
    var effect = entity.effects[packet.effectId];
    delete entity.effects[effect.id];
    bot.emit('entityEffectEnd', entity, effect);
  });

  bot._client.on('spawn_entity_weather', function(packet) {
    // spawn global entity
    var entity = fetchEntity(packet.entityId);
    entity.type = 'global';
    entity.globalType = 'thunderbolt';
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    bot.emit('entitySpawn', entity);
  });

  bot.on('spawn', function() {
    bot.emit('entitySpawn', bot.entity);
  });
  bot._client.on('player_info', function(packet) {
    // player list item(s)
    packet.data.forEach(function(item) {
      var playerEntity = bot.findPlayers(item.name);
      var player = bot.uuidToUsername[item.UUID] ? bot.players[bot.uuidToUsername[item.UUID]] : null;
      if(packet.action === 0) {
        // New Player
        if(!player) {
          player = bot.players[item.name] = {username: item.name, ping: item.ping, uuid: item.UUID};
          bot.uuidToUsername[item.UUID] = item.name;
          bot.emit('playerJoined', player);
          // Just an Update
        } else player.ping = item.ping;

        player.entity = playerEntity;
      } else if(packet.action === 1) {
        // TODO: update gamemode
      } else if(packet.action === 2) {
        // TODO: update latency
      } else if(packet.action === 3) {
        // TODO: update display name
      } else if(packet.action === 4) {
        // Player has parted
        if(player) {
          player.entity = null;
          delete bot.players[player.username];
          delete bot.uuidToUsername[item.UUID];
          bot.emit('playerLeft', player);
        }
      }
    });
  });

  // attaching to a vehicle
  bot._client.on('attach_entity', function(packet) {
    if(packet.entityId !== bot.entity.id) return;
    var vehicle = bot.vehicle;
    if(packet.vehicleId === -1) {
      bot.vehicle = null;
      bot.emit("dismount", vehicle);
    } else {
      bot.vehicle = bot.entities[packet.vehicleId];
      bot.emit("mount");
    }
  });

  bot.attack = attack;
  bot.mount = mount;
  bot.dismount = dismount;
  bot.useOn = useOn;
  bot.moveVehicle = moveVehicle;

  function useOn(target) {
    // TODO: check if not crouching will make make this action always use the item
    useEntity(target, 0);
  }

  function attack(target, swing) {
    if(swing) {
      bot._client.write('arm_animation', {});
    }
    useEntity(target, 1);
  }

  function mount(target) {
    // TODO: check if crouching will make make this action always mount
    useEntity(target, 0);
  }

  function moveVehicle(left, forward) {
    bot._client.write('steer_vehicle', {
      "sideways": left,
      "forward": forward,
      "jump": 0x01
    });
  }

  function dismount() {
    if(bot.vehicle) {
      bot._client.write('steer_vehicle', {
        "sideways": 0.0,
        "forward": 0.0,
        "jump": 0x02
      });
    } else {
      bot.emit("error", new Error("dismount: not mounted"));
    }
  }

  function useEntity(target, leftClick, x, y, z) {
    if(x && y && z) {
      bot._client.write('use_entity', {
        target: target.id,
        mouse: leftClick,
        x: x,
        y: y,
        z: z
      });
    }
    else bot._client.write('use_entity', {
      target: target.id,
      mouse: leftClick
    });
  }

  function fetchEntity(id) {
    return bot.entities[id] || (bot.entities[id] = new Entity(id));
  }
}

function parseMetadata(metadata) {
  var o = {};
  metadata.forEach(function(pair) {
    o[pair.key] = pair.value;
  });
  return o;
}
<commit_msg>Check effect exists before delete<commit_after>var Vec3 = require('vec3').Vec3;
var _ = require('lodash');
var Entity = require('../entity');
var conv = require('../conversions');
var NAMED_ENTITY_HEIGHT = 1.62;
var CROUCH_HEIGHT = NAMED_ENTITY_HEIGHT - 0.08;
var entitiesData = require('node-minecraft-data').entities;
var Item = require('../item');

module.exports = inject;

var animationEvents = {
  0: 'entitySwingArm',
  1: 'entityHurt',
  2: 'entityWake',
  3: 'entityEat',
  4: 'entityCriticalEffect',
  5: 'entityMagicCriticalEffect'
};

var entityStatusEvents = {
  2: 'entityHurt',
  3: 'entityDead',
  6: 'entityTaming',
  7: 'entityTamed',
  8: 'entityShakingOffWater',
  10: 'entityEatingGrass',
};

function inject(bot) {

  bot.findPlayer = bot.findPlayers = function(filter) {
    var filterFn = function(entity) {
      if(entity.type !== 'player') return false;
      if(filter == null) return true;
      if(typeof filter === 'object' && filter instanceof RegExp) {
        return entity.username.search(filter) !== -1;
      } else if(typeof filter === 'function') {
        return filter(entity);
      } else if(typeof filter === 'string') {
        return entity.username.toLowerCase() === filter.toLowerCase();
      }
      return false;
    };
    var resultSet = _.transform(bot.entities, function(players, entity) {
      if(filterFn(entity)) players.push(entity);
    }, []);

    if(typeof filter === 'string') {
      switch(resultSet.length) {
        case 0:
          return null;
        case 1:
          return resultSet[0];
        default:
          return resultSet;
      }
    }
    return resultSet;
  };

  bot.players = {};
  bot.uuidToUsername = {};
  bot.entities = {};

  bot._client.once('login', function(packet) {
    // login
    bot.entity = fetchEntity(packet.entityId);
    bot.entity.username = bot.username;
    bot.entity.type = 'player';
  });

  bot._client.on('entity_equipment', function(packet) {
    // entity equipment
    var entity = fetchEntity(packet.entityId);
    entity.setEquipment(packet.slot, packet.item ? Item.fromNotch(packet.item) : null);
    bot.emit('entityEquip', entity);
  });

  bot._client.on('bed', function(packet) {
    // use bed
    var entity = fetchEntity(packet.entityId);
    entity.position.set(packet.location.x, packet.location.y, packet.location.z);
    bot.emit('entitySleep', entity);
  });

  bot._client.on('animation', function(packet) {
    // animation
    var entity = fetchEntity(packet.entityId);
    var eventName = animationEvents[packet.animation];
    if(eventName) bot.emit(eventName, entity);
  });

  bot._client.on('named_entity_spawn', function(packet) {
    // in case player_info packet was not sent before named_entity_spawn : ignore named_entity_spawn (see #213)
    if(packet.playerUUID in bot.uuidToUsername) {
      // spawn named entity
      var entity = fetchEntity(packet.entityId);
      entity.type = 'player';
      entity.username = bot.uuidToUsername[packet.playerUUID];
      entity.uuid = packet.playerUUID;
      entity.dataBlobs = packet.data;
      entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
      entity.yaw = conv.fromNotchianYawByte(packet.yaw);
      entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
      entity.height = NAMED_ENTITY_HEIGHT;
      entity.metadata = parseMetadata(packet.metadata);
      if(!bot.players[entity.username].entity) {
        bot.players[entity.username].entity = entity;
      }
      bot.emit('entitySpawn', entity);
    }
  });
  bot.on("entityCrouch", function(entity) {
    entity.height = CROUCH_HEIGHT;
  });
  bot.on("entityUncrouch", function(entity) {
    entity.height = NAMED_ENTITY_HEIGHT;
  });

  bot._client.on('collect', function(packet) {
    // collect item
    var collector = fetchEntity(packet.collectorEntityId);
    var collected = fetchEntity(packet.collectedEntityId);
    bot.emit('playerCollect', collector, collected);
  });

  bot._client.on('spawn_entity', function(packet) {
    // spawn object/vehicle
    var entity = fetchEntity(packet.entityId);
    entity.type = 'object';
    var entityData = entitiesData[packet.type];
    entity.objectType = entityData.displayName;
    entity.displayName = entityData.displayName;
    entity.entityType = entityData.id;
    entity.name = entityData.name;
    entity.kind = entityData.type;
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    entity.objectData = packet.objectData;
    bot.emit('entitySpawn', entity);
  });

  bot._client.on('spawn_entity_experience_orb', function(packet) {
    var entity = fetchEntity(packet.entityId);
    entity.type = 'orb';
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.count = packet.count;
    bot.emit('entitySpawn', entity);
  });

  bot._client.on('spawn_entity_living', function(packet) {
    // spawn mob
    var entity = fetchEntity(packet.entityId);
    entity.type = 'mob';
    var entityData = entitiesData[packet.type];
    entity.mobType = entityData.displayName;
    entity.displayName = entityData.displayName;
    entity.entityType = entityData.id;
    entity.name = entityData.name;
    entity.kind = entityData.type;
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    entity.headPitch = conv.fromNotchianPitchByte(packet.headPitch);
    var notchVel = new Vec3(packet.velocityX, packet.velocityY, packet.velocityZ);
    entity.velocity.update(conv.fromNotchVelocity(notchVel));
    entity.metadata = parseMetadata(packet.metadata);
    bot.emit('entitySpawn', entity);
  });

  bot._client.on('entity_velocity', function(packet) {
    // entity velocity
    var entity = fetchEntity(packet.entityId);
    var notchVel = new Vec3(packet.velocityX, packet.velocityY, packet.velocityZ);
    entity.velocity.update(conv.fromNotchVelocity(notchVel));
  });

  bot._client.on('entity_destroy', function(packet) {
    // destroy entity
    packet.entityIds.forEach(function(id) {
      var entity = fetchEntity(id);
      bot.emit('entityGone', entity);
      entity.isValid = false;
      if(entity.username && bot.players[entity.username]) {
        bot.players[entity.username].entity = null;
      }
      delete bot.entities[id];
    });
  });

  bot._client.on('rel_entity_move', function(packet) {
    // entity relative move
    var entity = fetchEntity(packet.entityId);
    entity.position.translate(packet.dX / 32, packet.dY / 32, packet.dZ / 32);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_look', function(packet) {
    // entity look
    var entity = fetchEntity(packet.entityId);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_move_look', function(packet) {
    // entity look and relative move
    var entity = fetchEntity(packet.entityId);
    entity.position.translate(packet.dX / 32, packet.dY / 32, packet.dZ / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_teleport', function(packet) {
    // entity teleport
    var entity = fetchEntity(packet.entityId);
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_head_rotation', function(packet) {
    // entity head look
    var entity = fetchEntity(packet.entityId);
    entity.headYaw = conv.fromNotchianYawByte(packet.headYaw);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_status', function(packet) {
    // entity status
    var entity = fetchEntity(packet.entityId);
    var eventName = entityStatusEvents[packet.status];
    if(eventName) bot.emit(eventName, entity);
  });

  bot._client.on('attach_entity', function(packet) {
    // attach entity
    var entity = fetchEntity(packet.entityId);
    if(packet.vehicleId === -1) {
      var vehicle = entity.vehicle;
      delete entity.vehicle;
      bot.emit('entityDetach', entity, vehicle);
    } else {
      entity.vehicle = fetchEntity(packet.vehicleId);
      bot.emit('entityAttach', entity, entity.vehicle);
    }
  });

  bot._client.on('entity_metadata', function(packet) {
    // entity metadata
    var entity = fetchEntity(packet.entityId);
    entity.metadata = parseMetadata(packet.metadata);
    bot.emit('entityUpdate', entity);
  });

  bot._client.on('entity_effect', function(packet) {
    // entity effect
    var entity = fetchEntity(packet.entityId);
    var effect = {
      id: packet.effectId,
      amplifier: packet.amplifier,
      duration: packet.duration,
    };
    entity.effects[effect.id] = effect;
    bot.emit('entityEffect', entity, effect);
  });

  bot._client.on('remove_entity_effect', function(packet) {
    // remove entity effect
    var entity = fetchEntity(packet.entityId);
    var effect = entity.effects[packet.effectId];
    if(effect) {
      delete entity.effects[effect.id];
    }
    bot.emit('entityEffectEnd', entity, effect);
  });

  bot._client.on('spawn_entity_weather', function(packet) {
    // spawn global entity
    var entity = fetchEntity(packet.entityId);
    entity.type = 'global';
    entity.globalType = 'thunderbolt';
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    bot.emit('entitySpawn', entity);
  });

  bot.on('spawn', function() {
    bot.emit('entitySpawn', bot.entity);
  });
  bot._client.on('player_info', function(packet) {
    // player list item(s)
    packet.data.forEach(function(item) {
      var playerEntity = bot.findPlayers(item.name);
      var player = bot.uuidToUsername[item.UUID] ? bot.players[bot.uuidToUsername[item.UUID]] : null;
      if(packet.action === 0) {
        // New Player
        if(!player) {
          player = bot.players[item.name] = {username: item.name, ping: item.ping, uuid: item.UUID};
          bot.uuidToUsername[item.UUID] = item.name;
          bot.emit('playerJoined', player);
          // Just an Update
        } else player.ping = item.ping;

        player.entity = playerEntity;
      } else if(packet.action === 1) {
        // TODO: update gamemode
      } else if(packet.action === 2) {
        // TODO: update latency
      } else if(packet.action === 3) {
        // TODO: update display name
      } else if(packet.action === 4) {
        // Player has parted
        if(player) {
          player.entity = null;
          delete bot.players[player.username];
          delete bot.uuidToUsername[item.UUID];
          bot.emit('playerLeft', player);
        }
      }
    });
  });

  // attaching to a vehicle
  bot._client.on('attach_entity', function(packet) {
    if(packet.entityId !== bot.entity.id) return;
    var vehicle = bot.vehicle;
    if(packet.vehicleId === -1) {
      bot.vehicle = null;
      bot.emit("dismount", vehicle);
    } else {
      bot.vehicle = bot.entities[packet.vehicleId];
      bot.emit("mount");
    }
  });

  bot.attack = attack;
  bot.mount = mount;
  bot.dismount = dismount;
  bot.useOn = useOn;
  bot.moveVehicle = moveVehicle;

  function useOn(target) {
    // TODO: check if not crouching will make make this action always use the item
    useEntity(target, 0);
  }

  function attack(target, swing) {
    if(swing) {
      bot._client.write('arm_animation', {});
    }
    useEntity(target, 1);
  }

  function mount(target) {
    // TODO: check if crouching will make make this action always mount
    useEntity(target, 0);
  }

  function moveVehicle(left, forward) {
    bot._client.write('steer_vehicle', {
      "sideways": left,
      "forward": forward,
      "jump": 0x01
    });
  }

  function dismount() {
    if(bot.vehicle) {
      bot._client.write('steer_vehicle', {
        "sideways": 0.0,
        "forward": 0.0,
        "jump": 0x02
      });
    } else {
      bot.emit("error", new Error("dismount: not mounted"));
    }
  }

  function useEntity(target, leftClick, x, y, z) {
    if(x && y && z) {
      bot._client.write('use_entity', {
        target: target.id,
        mouse: leftClick,
        x: x,
        y: y,
        z: z
      });
    }
    else bot._client.write('use_entity', {
      target: target.id,
      mouse: leftClick
    });
  }

  function fetchEntity(id) {
    return bot.entities[id] || (bot.entities[id] = new Entity(id));
  }
}

function parseMetadata(metadata) {
  var o = {};
  metadata.forEach(function(pair) {
    o[pair.key] = pair.value;
  });
  return o;
}
<|endoftext|>


<commit_before>'use strict';

var async = require('async');

module.exports = function (client, stanzas) {
    stanzas.use(require('../stanza/streamFeatures'));

    client.features = {
        negotiated: {},
        order: [],
        handlers: {}
    };

    client.registerFeature = function (name, priority, handler) {
        this.features.order.push({
            priority: priority,
            name: name
        });
        this.features.order.sort(function (a, b) {
            if (a.priority < b.priority) {
                return -1;
            }
            if (a.priority > b.priority) {
                return 1;
            }
            return 0;
        });
        this.features.handlers[name] = handler.bind(client);
    };

    client.on('streamFeatures', function (features) {
        var series = [];
        var negotiated = client.features.negotiated;
        var handlers = client.features.handlers;

        client.features.order.forEach(function (feature) {
            var name = feature.name;
            if (handlers[name] && !negotiated[name]) {
                series.push(function (cb) {
                    if (handlers[name] && !negotiated[name]) {
                        handlers[name](features, cb);
                    } else {
                        cb();
                    }
                });
            }
        });

        async.series(series, function (cmd, msg) {
            if (cmd === 'restart') {
                client.transport.restart();
            } else if (cmd === 'disconnect') {
                client.emit('stream:error', {
                    condition: 'policy-violation',
                    text: 'Failed to negotiate stream features: ' + msg
                });
                client.disconnect();
            }
        });
    });
};
<commit_msg>Only queue up feature handlers that match content in the stream:features stanza<commit_after>'use strict';

var async = require('async');

module.exports = function (client, stanzas) {
    stanzas.use(require('../stanza/streamFeatures'));

    client.features = {
        negotiated: {},
        order: [],
        handlers: {}
    };

    client.registerFeature = function (name, priority, handler) {
        this.features.order.push({
            priority: priority,
            name: name
        });
        this.features.order.sort(function (a, b) {
            if (a.priority < b.priority) {
                return -1;
            }
            if (a.priority > b.priority) {
                return 1;
            }
            return 0;
        });
        this.features.handlers[name] = handler.bind(client);
    };

    client.on('streamFeatures', function (features) {
        var series = [];
        var negotiated = client.features.negotiated;
        var handlers = client.features.handlers;

        client.features.order.forEach(function (feature) {
            var name = feature.name;
            if (features[name] && handlers[name] && !negotiated[name]) {
                series.push(function (cb) {
                    if (!negotiated[name]) {
                        handlers[name](features, cb);
                    } else {
                        cb();
                    }
                });
            }
        });

        async.series(series, function (cmd, msg) {
            if (cmd === 'restart') {
                client.transport.restart();
            } else if (cmd === 'disconnect') {
                client.emit('stream:error', {
                    condition: 'policy-violation',
                    text: 'Failed to negotiate stream features: ' + msg
                });
                client.disconnect();
            }
        });
    });
};
<|endoftext|>


<commit_before>'use strict';

// Based on: https://raw.github.com/imathis/octopress/master/plugins/code_block.rb

var util = require('hexo-util');
var highlight = util.highlight;
var stripIndent = require('strip-indent');

var rCaptionUrlTitle = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)\s+(.+)/i;
var rCaptionUrl = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)/i;
var rCaption = /(\S[\S\s]*)/;
var rLang = /\s*lang:(\w+)/i;
var rLineNumber = /\s*line_number:(\w+)/i;

/**
 * Code block tag
 *
 * Syntax:
 *   {% codeblock [title] [lang:language] [url] [link text] [line_number:(true|false)] %}
 *   code snippet
 *   {% endcodeblock %}
 */

module.exports = function(ctx) {

  return function codeTag(args, content) {
    var arg = args.join(' ');
    var config = ctx.config.highlight || {};

    if (!config.enable) {
      return '
' + content + '
';
    }

    var caption = '';
    var lang = '';
    var line_number = config.line_number;
    var match;

    if (rLang.test(arg)) {
      arg = arg.replace(rLang, function() {
        lang = arguments[1];
        return '';
      });
    }

    if (rLineNumber.test(arg)) {
      arg = arg.replace(rLineNumber, function() {
        line_number = arguments[1] === 'true';
        return '';
      });
    }

    if (rCaptionUrlTitle.test(arg)) {
      match = arg.match(rCaptionUrlTitle);
      caption = '' + match[1] + '' + match[4] + '';
    } else if (rCaptionUrl.test(arg)) {
      match = arg.match(rCaptionUrl);
      caption = '' + match[1] + 'link';
    } else if (rCaption.test(arg)) {
      match = arg.match(rCaption);
      caption = '' + match[1] + '';
    }

    content = stripIndent(content).trim();

    content = highlight(content, {
      lang: lang,
      caption: caption,
      gutter: line_number,
      tab: config.tab_replace,
      autoDetect: config.auto_detect
    });

    content = content.replace(/{/g, '{')
      .replace(/}/g, '}');

    return content;
  };
};
<commit_msg>might as well add a configuration option to enable/disable highlight all together.<commit_after>'use strict';

// Based on: https://raw.github.com/imathis/octopress/master/plugins/code_block.rb

var util = require('hexo-util');
var highlight = util.highlight;
var stripIndent = require('strip-indent');

var rCaptionUrlTitle = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)\s+(.+)/i;
var rCaptionUrl = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)/i;
var rCaption = /(\S[\S\s]*)/;
var rLang = /\s*lang:(\w+)/i;
var rLineNumber = /\s*line_number:(\w+)/i;
var rHighlight = /\s*highlight:(\w+)/i;

/**
 * Code block tag
 *
 * Syntax:
 *   {% codeblock [title] [lang:language] [url] [link text] [line_number:(true|false)] [highlight:(true|false)] %}
 *   code snippet
 *   {% endcodeblock %}
 */

module.exports = function(ctx) {
  return function codeTag(args, content) {
    var arg = args.join(' ');
    var config = ctx.config.highlight || {};
    var enable = config.enable;

    if (rHighlight.test(arg)) {
      arg = arg.replace(rHighlight, function() {
        enable = arguments[1] === 'true';
        return '';
      });
    }

    if (!enable) {
      return '
' + content + '
';
    }

    var caption = '';
    var lang = '';
    var line_number = config.line_number;
    var match;

    if (rLang.test(arg)) {
      arg = arg.replace(rLang, function() {
        lang = arguments[1];
        return '';
      });
    }

    if (rLineNumber.test(arg)) {
      arg = arg.replace(rLineNumber, function() {
        line_number = arguments[1] === 'true';
        return '';
      });
    }

    if (rCaptionUrlTitle.test(arg)) {
      match = arg.match(rCaptionUrlTitle);
      caption = '' + match[1] + '' + match[4] + '';
    } else if (rCaptionUrl.test(arg)) {
      match = arg.match(rCaptionUrl);
      caption = '' + match[1] + 'link';
    } else if (rCaption.test(arg)) {
      match = arg.match(rCaption);
      caption = '' + match[1] + '';
    }

    content = stripIndent(content).trim();

    content = highlight(content, {
      lang: lang,
      caption: caption,
      gutter: line_number,
      tab: config.tab_replace,
      autoDetect: config.auto_detect
    });

    content = content.replace(/{/g, '{')
      .replace(/}/g, '}');

    return content;
  };
};
<|endoftext|>


<commit_before>var esprima = require('esprima'),
    escodegen = require('escodegen');

// Executes visitor on the object and its children (recursively).
var traverse = function traverse(object, visitor) {
    var key, child;

    visitor.call(null, object);
    for (key in object) {
        if (object.hasOwnProperty(key)) {
            child = object[key];
            if (typeof child === 'object' && child !== null) {
                traverse(child, visitor);
            }
        }
    }
};

var powerAssertVariableDeclaration = {
    "type": "VariableDeclaration",
    "declarations": [
        {
            "type": "VariableDeclarator",
            "id": {
                "type": "Identifier",
                "name": "passert"
            },
            "init": {
                "type": "CallExpression",
                "callee": {
                    "type": "Identifier",
                    "name": "require"
                },
                "arguments": [
                    {
                        "type": "Literal",
                        "value": "../lib/power-assert"
                    }
                ]
            }
        }
    ],
    "kind": "var"
};

var isAssertOkCall = function (callee) {
    if (callee.type !== 'MemberExpression' || callee.computed !== false) {
        return false;
    }
    var ob = callee.object,
        prop = callee.property;
    return ((ob.type === 'Identifier' && ob.name === 'assert') && (prop.type === 'Identifier' && prop.name === 'ok'));
};

var isAssertCall = function (callee) {
    return (callee.type === 'Identifier' && callee.name === 'assert');
};

// enclose  in __ident__
var capture = function (target, ident) {
    return {
        "type": "CallExpression",
        "callee": {
            "type": "MemberExpression",
            "computed": false,
            "object": {
                "type": "Identifier",
                "name": "passert"
            },
            "property": {
                "type": "Identifier",
                "name": "__ident__"
            }
        },
        "arguments": [
            {
                "type": "Literal",
                "value": ident.name
            },
            target,
            {
                "type": "Literal",
                "value": ident.loc.start.column
            },
            {
                "type": "Literal",
                "value": ident.loc.end.column
            }
        ]
    };
};

// enclose assert expression in __expr__
var captureFirstArgument = function (firstArgument, line) {
    return {
        "type": "CallExpression",
        "callee": {
            "type": "MemberExpression",
            "computed": false,
            "object": {
                "type": "Identifier",
                "name": "passert"
            },
            "property": {
                "type": "Identifier",
                "name": "__expr__"
            }
        },
        "arguments": [
            firstArgument,
            {
                "type": "Literal",
                "value": line
            }
        ]
    };
};

// var captureIdent = function (ident) {
//     return capture(ident, ident);
// };
// var captureMember = function (member) {
//     member.object = captureIdent(member.object);
//     return capture(member, member.property);
// };

var captureRecursively = function (node) {
    switch (node.type) {
    case 'Identifier':
        return capture(node, node);
        //return captureIdent(node);
    case 'MemberExpression': // ex: ary.length
        node.object = captureRecursively(node.object);
        return capture(node, node.property);
        //return captureMember(node);
    case 'UnaryExpression':
        node.argument = captureRecursively(node.argument);
        return node;
    case 'BinaryExpression':
        node.left = captureRecursively(node.left);
        node.right = captureRecursively(node.right);
        return node;
    case 'LogicalExpression':
        node.left = captureRecursively(node.left);
        node.right = captureRecursively(node.right);
        return node;
    default:
        return node;
    }
};

var isSupported = function () {
    var supported = [
        'Identifier',
        'UnaryExpression',
        'BinaryExpression',
        'MemberExpression',
        'LogicalExpression'
    ];
    return function (node) {
        return supported.some(function (kind) { return node.type === kind; });
    };
};

var instrument = function (expression, line) {
    var firstArgument = expression.arguments[0];
    if (!isSupported(firstArgument)) {
        return;
    }
    firstArgument = captureRecursively(firstArgument);
    expression.arguments[0] = captureFirstArgument(firstArgument, line);
};

var modifyTree = function (syntax, lines) {
    traverse(syntax, function (node) {
        if (typeof node.type === 'undefined' || node.type !== 'ExpressionStatement') {
            return;
        }
        var expression = node.expression;
        if (typeof expression.type === 'undefined' || expression.type !== 'CallExpression') {
            return;
        }
        var callee = expression.callee;
        if (!(isAssertCall(callee) || isAssertOkCall(callee))) {
            return;
        }
        var line = lines[node.loc.start.line - 1];
        instrument(expression, line);
    });
};

var generator = function generator(source, options) {
    options = options || {};
    var lines = source.split("\n"),
        tree = esprima.parse(source, {tolerant: true, loc: true});
    modifyTree(tree, lines, options);

    if (options['module'] === 'CommonJS') {
        // insert "var passert = require('power-assert');"
        tree.body.unshift(powerAssertVariableDeclaration);
    }

    console.log(escodegen.generate(tree));
};

generator.modifyTree = modifyTree;
generator.instrument = instrument;

/*global exports:false*/
if (typeof exports !== 'undefined') {
    module.exports = generator;
}
<commit_msg>refactor<commit_after>var esprima = require('esprima'),
    escodegen = require('escodegen');

// Executes visitor on the object and its children (recursively).
var traverse = function traverse(object, visitor) {
    var key, child;

    visitor.call(null, object);
    for (key in object) {
        if (object.hasOwnProperty(key)) {
            child = object[key];
            if (typeof child === 'object' && child !== null) {
                traverse(child, visitor);
            }
        }
    }
};

var isSupported = function () {
    var supported = [
        'Identifier',
        'UnaryExpression',
        'BinaryExpression',
        'MemberExpression',
        'LogicalExpression'
    ];
    return function (node) {
        return supported.some(function (kind) { return node.type === kind; });
    };
};

var powerAssertVariableDeclaration = {
    "type": "VariableDeclaration",
    "declarations": [
        {
            "type": "VariableDeclarator",
            "id": {
                "type": "Identifier",
                "name": "passert"
            },
            "init": {
                "type": "CallExpression",
                "callee": {
                    "type": "Identifier",
                    "name": "require"
                },
                "arguments": [
                    {
                        "type": "Literal",
                        "value": "../lib/power-assert"
                    }
                ]
            }
        }
    ],
    "kind": "var"
};

var isAssertOkCall = function (callee) {
    if (callee.type !== 'MemberExpression' || callee.computed !== false) {
        return false;
    }
    var ob = callee.object,
        prop = callee.property;
    return ((ob.type === 'Identifier' && ob.name === 'assert') && (prop.type === 'Identifier' && prop.name === 'ok'));
};

var isAssertCall = function (callee) {
    return (callee.type === 'Identifier' && callee.name === 'assert');
};

// enclose  in __ident__
var capture = function (target, ident) {
    return {
        "type": "CallExpression",
        "callee": {
            "type": "MemberExpression",
            "computed": false,
            "object": {
                "type": "Identifier",
                "name": "passert"
            },
            "property": {
                "type": "Identifier",
                "name": "__ident__"
            }
        },
        "arguments": [
            {
                "type": "Literal",
                "value": ident.name
            },
            target,
            {
                "type": "Literal",
                "value": ident.loc.start.column
            },
            {
                "type": "Literal",
                "value": ident.loc.end.column
            }
        ]
    };
};

var captureRecursively = function (node) {
    switch (node.type) {
    case 'Identifier':
        return capture(node, node);
        //return captureIdent(node);
    case 'MemberExpression': // ex: foo.bar.baz
        node.object = captureRecursively(node.object);
        return capture(node, node.property);
        //return captureMember(node);
    case 'UnaryExpression':
        node.argument = captureRecursively(node.argument);
        return node;
    case 'BinaryExpression':
        node.left = captureRecursively(node.left);
        node.right = captureRecursively(node.right);
        return node;
    case 'LogicalExpression':
        node.left = captureRecursively(node.left);
        node.right = captureRecursively(node.right);
        return node;
    default:
        return node;
    }
};

// enclose assert expression in __expr__
var captureFirstArgument = function (firstArgument, line) {
    return {
        "type": "CallExpression",
        "callee": {
            "type": "MemberExpression",
            "computed": false,
            "object": {
                "type": "Identifier",
                "name": "passert"
            },
            "property": {
                "type": "Identifier",
                "name": "__expr__"
            }
        },
        "arguments": [
            firstArgument,
            {
                "type": "Literal",
                "value": line
            }
        ]
    };
};

var instrument = function (expression, line) {
    var firstArgument = expression.arguments[0];
    if (!isSupported(firstArgument)) {
        return;
    }
    firstArgument = captureRecursively(firstArgument);
    expression.arguments[0] = captureFirstArgument(firstArgument, line);
};

var modifyTree = function (syntax, lines) {
    traverse(syntax, function (node) {
        if (typeof node.type === 'undefined' || node.type !== 'ExpressionStatement') {
            return;
        }
        var expression = node.expression;
        if (typeof expression.type === 'undefined' || expression.type !== 'CallExpression') {
            return;
        }
        var callee = expression.callee;
        if (!(isAssertCall(callee) || isAssertOkCall(callee))) {
            return;
        }
        var line = lines[node.loc.start.line - 1];
        instrument(expression, line);
    });
};

var generator = function generator(source, options) {
    options = options || {};
    var lines = source.split("\n"),
        tree = esprima.parse(source, {tolerant: true, loc: true});
    modifyTree(tree, lines, options);

    if (options['module'] === 'CommonJS') {
        // insert "var passert = require('power-assert');"
        tree.body.unshift(powerAssertVariableDeclaration);
    }

    console.log(escodegen.generate(tree));
};

generator.modifyTree = modifyTree;
generator.instrument = instrument;

/*global exports:false*/
if (typeof exports !== 'undefined') {
    module.exports = generator;
}
<|endoftext|>


<commit_before>var utils = require('./utils');

function Request(method,url,options,callback){
  var self = this;
  self.cb = callback;
  self.options = options;
  self.xhr = new XMLHttpRequest();
  self.xhr.onreadystatechange = self.onLoad.bind(self);
  self.xhr.open(method,url);
  if(options.withCredentials){
    self.xhr.withCredentials = options.withCredentials;
  }
  self.xhr.send(JSON.stringify(options.body));
  self.opened = false;
  self.done = false;
  return self;
}
Request.prototype.onLoad = function onLoad() {
  var self = this;
  var XHR = XMLHttpRequest;
  var s = self.xhr.readyState;
  if(s === XHR.OPENED && !self.opened) {
    self.xhr.setRequestHeader('Authorization', 'Bearer '+self.options.authToken);
    self.xhr.setRequestHeader('Content-Type','application/json');
    self.opened = true;
  }
  if(s === XHR.DONE && !self.done) {

    try{
      var headers = self.responseHeaders = self.getHeadersObject();
      var body = (typeof self.xhr.responseText === 'string' && self.xhr.responseText !== '') ? JSON.parse(self.xhr.responseText) : {};
      var status = self.xhr.status;
      var newToken = ( (headers['Authorization'] || '').match(/Bearer (.*)$/i) || [])[1] || '';
      self.cb(status < 400 ? null : body,
        newToken,
        self,
        status < 400 ? body : null
      );
      self.done = true;
    }catch(e){
      self.cb(e);
    }
  }
};
Request.prototype.getHeadersObject = function getHeadersObject() {
  var self = this;
  var all = self.xhr.getAllResponseHeaders().trim().split('\n');
  return all.reduce(function(acc,str){
    var x = str.split(': ');
    acc[x[0]] = x[1].trim();
    return acc;
  },{});
};

function RequestExecutor(authToken){
  this.authToken = authToken;
  this.terminated = false;
}

RequestExecutor.prototype.execute = function(method,url,options,callback) {
  var self = this;
  if(self.terminated){
    return callback(new Error('Request executor terminated, you must initiate a new flow from the service provider'));
  }
  var opts = typeof options === 'object' ? options : {
    body: null
  };
  opts.authToken = self.authToken;

  var cb = typeof options === 'function' ? options : ( callback || utils.noop);
  var req = new Request(method,url,opts,function onDone(err,newToken,request,body){
    self.authToken = newToken;
    if(!self.authToken){
      self.terminated = true;
    }
    if(err){
      return cb(err);
    }
    var redirectUrl = request.responseHeaders['Stormpath-SSO-Redirect-Location'];
    if(redirectUrl){
      body.redirectUrl = redirectUrl;
    }
    cb(err,body);
  });
  return req;
};

module.exports = RequestExecutor;<commit_msg>only terminate executor if !err and !authtoken<commit_after>var utils = require('./utils');

function Request(method,url,options,callback){
  var self = this;
  self.cb = callback;
  self.options = options;
  self.xhr = new XMLHttpRequest();
  self.xhr.onreadystatechange = self.onLoad.bind(self);
  self.xhr.open(method,url);
  if(options.withCredentials){
    self.xhr.withCredentials = options.withCredentials;
  }
  self.xhr.send(JSON.stringify(options.body));
  self.opened = false;
  self.done = false;
  return self;
}
Request.prototype.onLoad = function onLoad() {
  var self = this;
  var XHR = XMLHttpRequest;
  var s = self.xhr.readyState;
  if(s === XHR.OPENED && !self.opened) {
    self.xhr.setRequestHeader('Authorization', 'Bearer '+self.options.authToken);
    self.xhr.setRequestHeader('Content-Type','application/json');
    self.opened = true;
  }
  if(s === XHR.DONE && !self.done) {

    try{
      var headers = self.responseHeaders = self.getHeadersObject();
      var body = (typeof self.xhr.responseText === 'string' && self.xhr.responseText !== '') ? JSON.parse(self.xhr.responseText) : {};
      var status = self.xhr.status;
      var newToken = ( (headers['Authorization'] || '').match(/Bearer (.*)$/i) || [])[1] || '';
      self.cb(status < 400 ? null : body,
        newToken,
        self,
        status < 400 ? body : null
      );
      self.done = true;
    }catch(e){
      self.cb(e);
    }
  }
};
Request.prototype.getHeadersObject = function getHeadersObject() {
  var self = this;
  var all = self.xhr.getAllResponseHeaders().trim().split('\n');
  return all.reduce(function(acc,str){
    var x = str.split(': ');
    acc[x[0]] = x[1].trim();
    return acc;
  },{});
};

function RequestExecutor(authToken){
  this.authToken = authToken;
  this.terminated = false;
}

RequestExecutor.prototype.execute = function(method,url,options,callback) {
  var self = this;
  if(self.terminated){
    return callback(new Error('Request executor terminated, you must initiate a new flow from the service provider'));
  }
  var opts = typeof options === 'object' ? options : {
    body: null
  };
  opts.authToken = self.authToken;

  var cb = typeof options === 'function' ? options : ( callback || utils.noop);
  var req = new Request(method,url,opts,function onDone(err,newToken,request,body){
    self.authToken = newToken;
    if(!err && !self.authToken){
      self.terminated = true;
    }
    if(err){
      return cb(err);
    }
    var redirectUrl = request.responseHeaders['Stormpath-SSO-Redirect-Location'];
    if(redirectUrl){
      body.redirectUrl = redirectUrl;
    }
    cb(err,body);
  });
  return req;
};

module.exports = RequestExecutor;<|endoftext|>


<commit_before>'use strict'
const _ = require('lodash')
const assert = require('chai').assert

function startTimer (res, section, isSequence) {
  isSequence = isSequence || false
  res.requestProfiler = res.requestProfiler ? res.requestProfiler : { timers: {}, counters: {} }
  let timers = res.requestProfiler['timers']

  let item
  if (section && isSequence) {
    let seq
    if (timers[section]) {
      seq = timers[section]
    } else {
      seq = timers[section] = []
    }
    item = {}
    seq.push(item)
  } else if (section) {
    item = timers[section] = {}
  } else {
    item = timers['total'] = {}
  }

  item.start = process.hrtime()
}

function stopTimer (res, section) {
  if (!res.requestProfiler) { return }

  let timers = res.requestProfiler['timers']
  let item

  if (section && _.isArray(timers[section])) {
    item = _.last(timers[section])
  } else if (section) {
    item = _.result(timers, section)
    assert(_.isArray(item.start),
      `request-profiler.stopTimer() profile timer section '${section}' should be started somewhere to be stopped .. `)
  } else {
    assert(_.isArray(timers['total'].start),
      `request-profiler.stopTimer() profile timer should be started somewhere to be stopped .. `)
    item = timers['total']
  }

  const diff = process.hrtime(item.start)
  item.duration = diff[0] * 1e3 + diff[1] * 1e-6
}

function getTimer (res, section) {
  if (!res.requestProfiler) { return null }

  assert(_.isObject(res.requestProfiler.timers), `request-profiler.getAllTimers() profile timer should be started at least once`)

  let timers = res.requestProfiler['timers']

  if (section && _.isArray(timers[section])) {
    const seq = _.map(timers[section], item => item.duration)
    const total = _.sum(seq)
    //return { total, seq }
    return {
      total,
      // p50: stats.percentile(seq, 0.5),
      // p80: stats.percentile(seq, 0.8),
      // p90: stats.percentile(seq, 0.9),
      // p95: stats.percentile(seq, 0.95),
      // p100: stats.percentile(seq, 1)
    }
  } else if (section) {
    assert(_.result(timers, section).duration,
      `request-profiler.stop() profile timer section '${section}' should be started somewhere `)
    return timers[section].duration
  } else {
    return _.mapValues(timers, item => {
      if (_.has(item, 'duration')) {
        return item.duration
      } else if (_.isArray(item)){
        const seq = _.map(item, val => val.duration)
        const total = _.sum(seq)
        //return { total, seq }
        return {
          total,
          // p50: stats.percentile(seq, 0.5),
          // p80: stats.percentile(seq, 0.8),
          // p90: stats.percentile(seq, 0.9),
          // p95: stats.percentile(seq, 0.95),
          // p100: stats.percentile(seq, 1)
        }
      } else {
        return item
      }
    })
  }
}

function incCounter (res, section) {
  section = section || 'total'
  res.requestProfiler = res.requestProfiler ? res.requestProfiler : { timers: {}, counters: {} }
  let counters = res.requestProfiler['counters']

  counters[section] = counters[section] || 0
  counters[section] += 1
}

function decCounter (res, section) {
  section = section || 'total'
  res.requestProfiler = res.requestProfiler ? res.requestProfiler : { counters: {} }
  let counters = res.requestProfiler['counters']

  counters[section] = counters[section] || 0
  counters[section] -= 1
}

function getCounter (res, section) {
  if (!res.requestProfiler) { return null }

  let counters = res.requestProfiler['counters']

  if (section) {
    return counters[section]
  } else {
    return counters
  }

}

module.exports = {
  startTimer,
  stopTimer,
  getTimer,
  incCounter,
  decCounter,
  getCounter
}<commit_msg>* fix req logger with server static<commit_after>'use strict'
const _ = require('lodash')
const assert = require('chai').assert

function startTimer (res, section, isSequence) {
  isSequence = isSequence || false
  res.requestProfiler = res.requestProfiler ? res.requestProfiler : { timers: {}, counters: {} }
  let timers = res.requestProfiler['timers']

  let item
  if (section && isSequence) {
    let seq
    if (timers[section]) {
      seq = timers[section]
    } else {
      seq = timers[section] = []
    }
    item = {}
    seq.push(item)
  } else if (section) {
    item = timers[section] = {}
  } else {
    item = timers['total'] = {}
  }

  item.start = process.hrtime()
}

function stopTimer (res, section) {
  if (!res.requestProfiler) { return }

  let timers = res.requestProfiler['timers']
  let item

  if (section && _.isArray(timers[section])) {
    item = _.last(timers[section])
  } else if (section) {
    item = _.result(timers, section)
    assert(_.isArray(item.start),
      `request-profiler.stopTimer() profile timer section '${section}' should be started somewhere to be stopped .. `)
  } else {
    assert(_.isArray(timers['total'].start),
      `request-profiler.stopTimer() profile timer should be started somewhere to be stopped .. `)
    item = timers['total']
  }

  const diff = process.hrtime(item.start)
  item.duration = diff[0] * 1e3 + diff[1] * 1e-6
}

function getTimer (res, section) {
  if (!res.requestProfiler) { return null }

  if (!_.isObject(res.requestProfiler.timers)) { return null }

  let timers = res.requestProfiler['timers']

  if (section && _.isArray(timers[section])) {
    const seq = _.map(timers[section], item => item.duration)
    const total = _.sum(seq)
    return { total }
  } else if (section) {
    if (!_.result(timers, section).duration) { return null }
    return timers[section].duration
  } else {
    return _.mapValues(timers, item => {
      if (_.has(item, 'duration')) {
        return item.duration
      } else if (_.isArray(item)){
        const seq = _.map(item, val => val.duration)
        const total = _.sum(seq)
        return {
          total
        }
      } else {
        return item
      }
    })
  }
}

function incCounter (res, section) {
  section = section || 'total'
  res.requestProfiler = res.requestProfiler ? res.requestProfiler : { timers: {}, counters: {} }
  let counters = res.requestProfiler['counters']

  counters[section] = counters[section] || 0
  counters[section] += 1
}

function decCounter (res, section) {
  section = section || 'total'
  res.requestProfiler = res.requestProfiler ? res.requestProfiler : { counters: {} }
  let counters = res.requestProfiler['counters']

  counters[section] = counters[section] || 0
  counters[section] -= 1
}

function getCounter (res, section) {
  if (!res.requestProfiler) { return null }

  let counters = res.requestProfiler['counters']

  if (section) {
    return counters[section]
  } else {
    return counters
  }

}

module.exports = {
  startTimer,
  stopTimer,
  getTimer,
  incCounter,
  decCounter,
  getCounter
}<|endoftext|>


<commit_before>/**
 * @file Request kernel for server
 * @author treelite(c.xinle@gmail.com);
 */

var request = require('request');
var METHOD = require('../const').METHOD;
var util = require('../util');

/**
 * 请求核心
 *
 * @constructor
 * @param {Object} options 请求参数
 * @param {Requester} requester 请求对象
 */
function Kernel(options, requester) {
    var headers = options.headers;
    // POST请求添加默认的`Content-Type`
    if (options.method === METHOD.POST
        && !util.findHeader(headers, 'Content-Type')
    ) {
        headers['Content-Type'] = 'application/x-www-form-urlencoded';
    }

    var data = options.data;
    if (!util.isString(data) && options.stringify !== false) {
        data = util.stringify(data);
    }

    var timeout = false;
    var xhr = request(
        {
            url: options.url,
            method: options.method,
            headers: headers,
            body: data,
            timeout: options.timeout
        },
        function (error, res, data) {
            if (!error) {
                // 与client端兼容
                res.status = res.statusCode;
            }
            // Hack Timeout
            else if (error.code.toLowerCase().indexOf('timedout') >= 0) {
                timeout = true;
                error = 'timeout';
            }
            requester.handle(error, res, data);
        }
    );

    // Hack abort
    // abort总是会在timeout之前被触发...
    function abortError() {
        if (!timeout) {
            requester.handle('abort');
        }
    }

    xhr.on('abort', function () {
        process.nextTick(abortError);
    });

    return xhr;
}

module.exports = Kernel;
<commit_msg>Add log for server request<commit_after>/**
 * @file Request kernel for server
 * @author treelite(c.xinle@gmail.com);
 */

var request = require('request');
var METHOD = require('../const').METHOD;
var util = require('../util');

/**
 * 纪录日志
 *
 * @inner
 * @param {Object} requester 请求对象
 * @param {string} level 日志级别
 */
function log(requester, level) {
    var context = requester.context;
    var logger = context.data.logger;
    if (!logger || !logger[level]) {
        return;
    }

    var args = Array.prototype.slice.call(arguments, 2);
    args[0] = 'AJAX-' + requester.uid + ' ' + args[0];
    logger[level].apply(logger, args);
}

/**
 * 请求核心
 *
 * @constructor
 * @param {Object} options 请求参数
 * @param {Requester} requester 请求对象
 */
function Kernel(options, requester) {
    var headers = options.headers;
    // POST请求添加默认的`Content-Type`
    if (options.method === METHOD.POST
        && !util.findHeader(headers, 'Content-Type')
    ) {
        headers['Content-Type'] = 'application/x-www-form-urlencoded';
    }

    var data = options.data;
    if (!util.isString(data) && options.stringify !== false) {
        data = util.stringify(data);
    }

    log(requester, 'info', 'request %s %s %s', options.method, options.url, data);
    var timeout = false;
    var xhr = request(
        {
            url: options.url,
            method: options.method,
            headers: headers,
            body: data,
            timeout: options.timeout
        },
        function (error, res, data) {
            if (!error) {
                // 与client端兼容
                var status = res.status = res.statusCode;
                var level = status >= 400 ? 'error' : 'info';
                log(requester, level, 'response %s\n%s', status, data);
            }
            else {
                log(requester, 'error', error.stack);
                // Hack Timeout
                if (error.code.toLowerCase().indexOf('timedout') >= 0) {
                    timeout = true;
                    error = 'timeout';
                }
            }
            requester.handle(error, res, data);
        }
    );

    // Hack abort
    // abort总是会在timeout之前被触发...
    function abortError() {
        if (!timeout) {
            requester.handle('abort');
        }
    }

    xhr.on('abort', function () {
        process.nextTick(abortError);
    });

    return xhr;
}

module.exports = Kernel;
<|endoftext|>


<commit_before>/**
 * @fileoverview
 */

'use strict';

const Section = require('./section');

module.exports =
class SectionizedLine {
  constructor() {
    this._prefix = false;
    this._valid = false;

    this._trailingComment = '';

    this._currentSection = new Section();
    this._sections = [];
  }

  get before() {
    return this._currentSection.before;
  }

  set before(value) {
    this._currentSection.before = value;
  }

  get after() {
    return this._currentSection.after;
  }

  set after(value) {
    this._currentSection.after = value;
  }

  get trailingComment() {
    return this._trailingComment;
  }

  set trailingComment(value) {
    this._trailingComment = value;
  }

  set prefix(value) {
    this._prefix = value;
  }

  set valid(value) {
    this._valid = value;
  }

  get sections() {
    return this._sections;
  }

  get length() {
    return this._sections.length;
  }

  add(isLast) {
    this._currentSection.sanitize();
    this._sections.push(this._currentSection);

    this._currentSection = new Section();
  }

  hasPrefix() {
    return !!this._prefix;
  }

  isValid() {
    return this._valid;
  }
}
<commit_msg>Remove unused param<commit_after>/**
 * @fileoverview
 */

'use strict';

const Section = require('./section');

module.exports =
class SectionizedLine {
  constructor() {
    this._prefix = false;
    this._valid = false;

    this._trailingComment = '';

    this._currentSection = new Section();
    this._sections = [];
  }

  get before() {
    return this._currentSection.before;
  }

  set before(value) {
    this._currentSection.before = value;
  }

  get after() {
    return this._currentSection.after;
  }

  set after(value) {
    this._currentSection.after = value;
  }

  get trailingComment() {
    return this._trailingComment;
  }

  set trailingComment(value) {
    this._trailingComment = value;
  }

  set prefix(value) {
    this._prefix = value;
  }

  set valid(value) {
    this._valid = value;
  }

  get sections() {
    return this._sections;
  }

  get length() {
    return this._sections.length;
  }

  add() {
    this._currentSection.sanitize();
    this._sections.push(this._currentSection);

    this._currentSection = new Section();
  }

  hasPrefix() {
    return !!this._prefix;
  }

  isValid() {
    return this._valid;
  }
}
<|endoftext|>


<commit_before>'use strict';
var _ = require('lodash');
var inflection = require('inflection');

module.exports = function (collectionName, record, payload, opts) {

  function dasherize(attribute) {
    return inflection.dasherize(inflection.underscore(attribute));
  }

  function keyForAttribute(attribute) {
    if (_.isPlainObject(attribute)) {
      return _.mapKeys(attribute, function (value, key) {
        return keyForAttribute(key);
      });
    } else if (_.isArray(attribute)) {
      return attribute.map(function (attr) {
        return keyForAttribute(attr);
      });
    } else {
      if (_.isFunction(opts.keyForAttribute)) {
        return opts.keyForAttribute(attribute);
      } else {
        return dasherize(attribute);
      }
    }
  }

  function getId() {
    return opts.id || 'id';
  }

  function getRef(current, item, opts) {
    if (_.isFunction(opts.ref)) {
      return opts.ref(current, item);
    } else {
      return String(item[opts.ref]);
    }
  }

  function getType(str) {
    var type = str;

    if (_.isFunction(opts.typeForAttribute)) {
      type = opts.typeForAttribute(str);
    } else if (_.isUndefined(opts.pluralizeType) || opts.pluralizeType) {
      type = inflection.pluralize(type);
    }

    return type;
  }

  function getLinks(current, links) {
    return _.mapValues(links, function (value) {
      if (_.isFunction(value)) {
        return value(record, current);
      } else {
        return value;
      }
    });
  }

  function isAttrAllowed(attributes, attr) {
    return attributes.indexOf(attr) > -1;
  }

  function pick(obj, attributes) {
    return _.mapKeys(_.pick(obj, attributes), function (value, key) {
      return keyForAttribute(key);
    });
  }

  function isComplexType(obj) {
    return _.isArray(obj) || _.isPlainObject(obj);
  }

  function isCompoundDocumentIncluded(included, item) {
    return _.find(payload.included, { id: item.id, type: item.type });
  }

  function pushToIncluded(dest, include) {
    if (!isCompoundDocumentIncluded(dest, include)) {
      if (!dest.included) { dest.included = []; }
      dest.included.push(include);
    }
  }

  this.serialize = function (dest, current, attribute, opts) {
    var that = this;

    if (isComplexType(current[attribute])) {
      if (opts && opts.ref) {
        // Embedded with relationships.
        if (!dest.relationships) { dest.relationships = {}; }

        var data = null;
        if (_.isArray(current[attribute])) {
          data = current[attribute].map(function (item) {
            return that.serializeRef(item, current, attribute, opts);
          });
        } else if (_.isPlainObject(current[attribute])) {
          data = that.serializeRef(current[attribute], current, attribute, opts);
        }

        dest.relationships[keyForAttribute(attribute)] = { data: data };
        if (opts.relationshipLinks) {
          dest.relationships[keyForAttribute(attribute)].links =
            getLinks(current[attribute], opts.relationshipLinks);
        }
      } else if (_.isPlainObject(current[attribute])) {
        // Embedded without relationships.
        (dest.attributes || dest)[keyForAttribute(attribute)] =
          _.transform(current[attribute], function (result, value, key) {
            if (isComplexType(value)) {
              that.serialize(result, current[attribute], key, opts[key]);
            } else {
              result[keyForAttribute(key)] = value;
            }
          });
      } else {
        dest.attributes[keyForAttribute(attribute)] =
          keyForAttribute(current[attribute]);
      }
    } else {
      dest.attributes[keyForAttribute(attribute)] = current[attribute];
    }
  };

  this.serializeRef = function (dest, current, attribute, opts) {
    var that = this;
    var id = getRef(current, dest, opts);
    var type = getType(attribute);

    var relationships = opts.attributes.filter(function (attr) {
      return opts[attr];
    });

    var includedAttrs = opts.attributes.filter(function (attr) {
      return !opts[attr];
    });

    var included = {
      type: type,
      id: id,
      attributes: pick(dest, includedAttrs)
    };

    relationships.forEach(function (relationship) {
      if (isComplexType(dest[relationship])) {
        that.serialize(included, dest, relationship, opts[relationship]);
      }
    });

    if (_.isUndefined(opts.included) || opts.included) {
      if (opts.includedLinks) {
        included.links = getLinks(dest, opts.includedLinks);
      }

      pushToIncluded(payload, included);
    }

    return { type: type, id: id };
  };

  this.perform = function () {
    var that = this;

    // Top-level data.
    var data = {
      type: getType(collectionName),
      id: String(record[getId()]),
      attributes: {}
    };

    // Data links.
    if (opts.dataLinks) {
      data.links = getLinks(record, opts.dataLinks);
    }

    _.each(_.keys(record), function (attribute) {
      if (!isAttrAllowed(opts.attributes, attribute)) { return; }
      that.serialize(data, record, attribute, opts[attribute]);
    });

    return data;
  };
};
<commit_msg>Avoid to have relationships key in the attributes key<commit_after>'use strict';
var _ = require('lodash');
var inflection = require('inflection');

module.exports = function (collectionName, record, payload, opts) {

  function dasherize(attribute) {
    return inflection.dasherize(inflection.underscore(attribute));
  }

  function keyForAttribute(attribute) {
    if (_.isPlainObject(attribute)) {
      return _.transform(attribute, function (result, value, key) {
        if (isComplexType(value)) {
          result[keyForAttribute(key)] = keyForAttribute(value);
        } else {
          result[keyForAttribute(key)] = value;
        }
      });
    } else if (_.isArray(attribute)) {
      return attribute.map(function (attr) {
        return keyForAttribute(attr);
      });
    } else {
      if (_.isFunction(opts.keyForAttribute)) {
        return opts.keyForAttribute(attribute);
      } else {
        return dasherize(attribute);
      }
    }
  }

  function getId() {
    return opts.id || 'id';
  }

  function getRef(current, item, opts) {
    if (_.isFunction(opts.ref)) {
      return opts.ref(current, item);
    } else {
      return String(item[opts.ref]);
    }
  }

  function getType(str) {
    var type = str;

    if (_.isFunction(opts.typeForAttribute)) {
      type = opts.typeForAttribute(str);
    } else if (_.isUndefined(opts.pluralizeType) || opts.pluralizeType) {
      type = inflection.pluralize(type);
    }

    return type;
  }

  function getLinks(current, links) {
    return _.mapValues(links, function (value) {
      if (_.isFunction(value)) {
        return value(record, current);
      } else {
        return value;
      }
    });
  }

  function isAttrAllowed(attributes, attr) {
    return attributes.indexOf(attr) > -1;
  }

  function pick(obj, attributes) {
    return _.mapKeys(_.pick(obj, attributes), function (value, key) {
      return keyForAttribute(key);
    });
  }

  function isComplexType(obj) {
    return _.isArray(obj) || _.isPlainObject(obj);
  }

  function isCompoundDocumentIncluded(included, item) {
    return _.find(payload.included, { id: item.id, type: item.type });
  }

  function pushToIncluded(dest, include) {
    if (!isCompoundDocumentIncluded(dest, include)) {
      if (!dest.included) { dest.included = []; }
      dest.included.push(include);
    }
  }

  this.serialize = function (dest, current, attribute, opts) {
    var that = this;

    if (isComplexType(current[attribute])) {
      if (opts && opts.ref) {
        // Embedded with relationships.
        if (!dest.relationships) { dest.relationships = {}; }

        var data = null;
        if (_.isArray(current[attribute])) {
          data = current[attribute].map(function (item) {
            return that.serializeRef(item, current, attribute, opts);
          });
        } else if (_.isPlainObject(current[attribute])) {
          data = that.serializeRef(current[attribute], current, attribute, opts);
        }

        dest.relationships[keyForAttribute(attribute)] = { data: data };
        if (opts.relationshipLinks) {
          dest.relationships[keyForAttribute(attribute)].links =
            getLinks(current[attribute], opts.relationshipLinks);
        }
      } else {
        dest.attributes[keyForAttribute(attribute)] =
          keyForAttribute(current[attribute]);
      }
    } else {
      dest.attributes[keyForAttribute(attribute)] = current[attribute];
    }
  };

  this.serializeRef = function (dest, current, attribute, opts) {
    var that = this;
    var id = getRef(current, dest, opts);
    var type = getType(attribute);

    var relationships = opts.attributes.filter(function (attr) {
      return opts[attr];
    });

    var includedAttrs = opts.attributes.filter(function (attr) {
      return !opts[attr];
    });

    var included = {
      type: type,
      id: id,
      attributes: pick(dest, includedAttrs)
    };

    relationships.forEach(function (relationship) {
      if (isComplexType(dest[relationship])) {
        that.serialize(included, dest, relationship, opts[relationship]);
      }
    });

    if (_.isUndefined(opts.included) || opts.included) {
      if (opts.includedLinks) {
        included.links = getLinks(dest, opts.includedLinks);
      }

      pushToIncluded(payload, included);
    }

    return { type: type, id: id };
  };

  this.perform = function () {
    var that = this;

    // Top-level data.
    var data = {
      type: getType(collectionName),
      id: String(record[getId()]),
      attributes: {}
    };

    // Data links.
    if (opts.dataLinks) {
      data.links = getLinks(record, opts.dataLinks);
    }

    _.each(_.keys(record), function (attribute) {
      if (!isAttrAllowed(opts.attributes, attribute)) { return; }
      that.serialize(data, record, attribute, opts[attribute]);
    });

    return data;
  };
};
<|endoftext|>


<commit_before>'use strict';

var async = require('async');
var dot = require('dot');
dot.templateSettings.strip = false;

var Node = require('../node/node');
var Source = require('../node/nodes/source');

var debug = require('../util/debug')('database-service');

var QueryRunner = require('../postgresql/query-runner');
var QueryParser = require('../postgresql/query-parser');
var BatchClient = require('../postgresql/batch-client');

var QUERY_RUNNER_READONLY_OP = true;
var QUERY_RUNNER_WRITE_OP = !QUERY_RUNNER_READONLY_OP;

var createQueryTemplate = dot.template([
    'CREATE TABLE {{=it._targetTableName}} AS',
    '{{=it._query}}',
    'LIMIT 0'
].join('\n'));

function DatabaseService(dbParams, batchParams) {
    this.queryRunner = new QueryRunner(dbParams);
    this.queryParser = new QueryParser(this.queryRunner);

    var inlineExecution = batchParams.inlineExecution || false;
    var hostHeaderTemplate = batchParams.hostHeaderTemplate || '{{=it.username}}.localhost.lan';
    this.batchClient = new BatchClient(
        batchParams.endpoint,
        batchParams.username,
        batchParams.apiKey,
        hostHeaderTemplate,
        inlineExecution,
        this.queryRunner
    );
}

module.exports = DatabaseService;

DatabaseService.prototype.run = function(query, callback) {
    this.queryRunner.run(query, QUERY_RUNNER_READONLY_OP, callback);
};

var affectedTableRegexCache = {
    bbox: /!bbox!/g,
    scaleDenominator: /!scale_denominator!/g,
    pixelWidth: /!pixel_width!/g,
    pixelHeight: /!pixel_height!/g
};

DatabaseService.prototype.getLastUpdatedTimeFromAffectedTables = function(sql, callback) {
    sql = sql
        .replace(affectedTableRegexCache.bbox, 'ST_MakeEnvelope(0,0,0,0)')
        .replace(affectedTableRegexCache.scaleDenominator, '0')
        .replace(affectedTableRegexCache.pixelWidth, '1')
        .replace(affectedTableRegexCache.pixelHeight, '1');
    sql = 'SELECT max(updated_at) FROM CDB_QueryTables_Updated_At($camshaft$' + sql + '$camshaft$)';

    this.queryRunner.run(sql, QUERY_RUNNER_READONLY_OP, function(err, resultSet) {
        if (err) {
            return callback(err);
        }

        var defaultLastUpdatedTime = new Date();
        var rows = resultSet.rows || [defaultLastUpdatedTime];
        var lastUpdatedTimeFromAffectedTables = (rows[0] && rows[0].max) || defaultLastUpdatedTime;

        return callback(null, lastUpdatedTimeFromAffectedTables);
    });
};

DatabaseService.prototype.createTable = function(targetTableName, query, callback) {
    var createQuery = createQueryTemplate({
        _targetTableName: targetTableName,
        _query: query
    });

    this.queryRunner.run(createQuery, QUERY_RUNNER_WRITE_OP, function(err, resultSet) {
        debug('TODO: register table');
        return callback(err, resultSet);
    });
};

DatabaseService.prototype.tableExists = function(targetTableName, callback) {
    var existsQuery = 'select * from ' + targetTableName + ' limit 0';
    this.queryRunner.run(existsQuery, QUERY_RUNNER_READONLY_OP, function(err, resultSet) {
        var tableExists = !err && resultSet;
        return callback(null, tableExists);
    });
};

DatabaseService.prototype.createTableIfNotExists = function(targetTableName, outputQuery, callback) {
    var self = this;
    this.tableExists(targetTableName, function(err, tableExists) {
        if (tableExists) {
            return callback(null, tableExists);
        }

        self.createTable(targetTableName, outputQuery, function(err) {
            if (err) {
                return callback(err, tableExists);
            }

            return callback(null, tableExists);
        });
    });
};

DatabaseService.prototype.getColumnNames = function(query, callback) {
    this.queryParser.getColumnNames(query, callback);
};

DatabaseService.prototype.enqueue = function(queries, callback) {
    this.batchClient.enqueue(queries, callback);
};

var registerNodeQueryTemplate = dot.template([
    'SELECT',
    '    \'{{=it._nodeId}}\',',
    '    $json_def${{=it._analysisDef}}$json_def$::json,',
    '    {{=it._inputNodes}},',
    '    {{=it._updatedAt}},',
    '    \'{{=it._status}}\'',
    'WHERE NOT EXISTS (SELECT * FROM cdb_analysis_catalog WHERE node_id = \'{{=it._nodeId}}\')'
].join('\n'));

var EMPTY_ARRAY_SQL = '\'{}\'';
function pgArray(input, cast) {
    if (input.length === 0) {
        return cast ? ('ARRAY[]::' + cast + '[]') : EMPTY_ARRAY_SQL;
    }
    return 'ARRAY[' + input.join(', ') + ']';
}

function pgQuoteCastMapper(cast) {
    return function(input) {
        return '\'' + input + '\'' + (cast ? ('::' + cast) : '');
    };
}

DatabaseService.prototype.setUpdatedAtForSources = function(analysis, callback) {
    if (analysis.getRoot().type === Source.TYPE) {
        return callback(null);
    }

    var self = this;

    var sourceNodes = analysis.getNodes().filter(function(node) { return node.type === Source.TYPE; });
    async.map(sourceNodes,
        function(node, done) {
            node.getAndSetUpdatedAt(self, done);
        },
        callback
    );
};

DatabaseService.prototype.registerAnalysisInCatalog = function(analysis, callback) {
    var nodes = analysis.getSortedNodes();
    if (!Array.isArray(nodes) || nodes.length === 0 || analysis.getRoot().type === Source.TYPE) {
        return callback(null);
    }
    var nodesSql = [];
    nodes.forEach(function(node) {
        debug(node.type, node.getUpdatedAt());
        var updatedAt = node.getUpdatedAt();
        nodesSql.push(registerNodeQueryTemplate({
            _nodeId: node.id(),
            _inputNodes: pgArray(
                node.getInputNodes().map(function(node) { return node.id(); }).map(pgQuoteCastMapper()), 'char[40]'
            ),
            _updatedAt: (updatedAt === null ? 'NULL' : 'TIMESTAMP WITH TIME ZONE \'' + updatedAt.toISOString() + '\''),
            _status: node.getStatus(),
            _analysisDef: JSON.stringify(node.toJSON())
        }));
    });
    var sql = 'INSERT INTO cdb_analysis_catalog' +
        '(node_id, analysis_def, input_nodes, updated_at, status)\n' + nodesSql.join('\nUNION ALL\n');

    debug('Registering node with SQL: %s', sql);
    this.queryRunner.run(sql, QUERY_RUNNER_WRITE_OP, function(err, resultSet) {
        debug(err, resultSet);
        return callback(err);
    });
};

function deleteFromCacheTableQuery(targetTableName) {
    return 'DELETE FROM ' + targetTableName;
}

function populateCacheTableQuery(targetTableName, outputQuery) {
    return 'INSERT INTO ' + targetTableName + ' ' + outputQuery;
}

function updateNodeAtAnalysisCatalogQuery(nodeId, status) {
    return [
        'UPDATE cdb_analysis_catalog SET',
            'status = \'' + status + '\',',
        'updated_at = NOW()',
            'WHERE node_id = \'' + nodeId + '\''
    ].join('\n');
}

DatabaseService.prototype.queueAnalysisOperations = function(analysis, callback) {
    if (analysis.getRoot().type === Source.TYPE) {
        return callback(null);
    }

    var self = this;

    var sortedNodes = analysis.getSortedNodes();
    var sortedNodesIds = sortedNodes.map(function(node) { return node.id(); });
    var nodesById = analysis.getNodes().reduce(function(byId, node) {
        if (!byId.hasOwnProperty(node.id())) {
            byId[node.id()] = [];
        }
        byId[node.id()].push(node);
        return byId;
    }, {});
    var query = [
        'SELECT node_id, updated_at, status',
        'FROM cdb_analysis_catalog',
        'WHERE node_id IN (',
            sortedNodesIds
                .filter(function(node) { return node.type !== Source.TYPE; })
                .map(pgQuoteCastMapper()),
        ')'
    ].join('\n');

    this.queryRunner.run(query, function(err, resultSet) {
        if (err) {
            return callback(err);
        }

        var rows = resultSet.rows || [];
        rows.forEach(function(row) {
            var nodeId = row.node_id;
            var nodes = nodesById[nodeId];
            if (nodes) {
                nodes.forEach(function(node) {
                    node.setUpdatedAt(row.updated_at || null);
                    node.setStatus(row.status);
                });
            }
        });

        var nodesToQueueForUpdate = [];

        sortedNodes.forEach(function(node) {
            var inputNodes = node.getInputNodes();
            var startingLastUpdate = (inputNodes.length > 0 ? null : node.getUpdatedAt());
            var inputNodesLastUpdate = inputNodes.reduce(function(lastUpdate, inputNode) {
                var inputNodeUpdatedAt = inputNode.getUpdatedAt();

                if (inputNodeUpdatedAt === null) {
                    return new Date();
                }

                if (lastUpdate === null) {
                    return inputNodeUpdatedAt;
                }

                return lastUpdate.getTime() > inputNodeUpdatedAt.getTime() ? lastUpdate : inputNodeUpdatedAt;
            }, startingLastUpdate);

            var nodeUpdatedAt = node.getUpdatedAt();
            if (nodeUpdatedAt === null || inputNodesLastUpdate.getTime() > nodeUpdatedAt.getTime()) {
                nodesToQueueForUpdate.push(node);
            }
        });

        var asyncQueries = [];

        // we want to mark all nodes updated_at to NOW() and status to pending before doing any work on them
        nodesToQueueForUpdate.forEach(function(nodeToUpdate) {
            var status = nodeToUpdate.getInputNodes().reduce(function(_status, inputNode) {
                if (inputNode.getStatus() === Node.STATUS.PENDING) {
                    return Node.STATUS.WAITING;
                }
                return _status;
            }, Node.STATUS.PENDING);
            asyncQueries.push(updateNodeAtAnalysisCatalogQuery(nodeToUpdate.id(), status));
            nodeToUpdate.setStatus(status);
        });

        nodesToQueueForUpdate.forEach(function(nodeToUpdate) {
            if (nodeToUpdate.shouldCacheQuery()) {
                var targetTableName = nodeToUpdate.getTargetTable();
                asyncQueries.push(deleteFromCacheTableQuery(targetTableName));
                asyncQueries.push(updateNodeAtAnalysisCatalogQuery(nodeToUpdate.id(), Node.STATUS.RUNNING));
                asyncQueries.push(populateCacheTableQuery(targetTableName, nodeToUpdate.sql()));
            }
            asyncQueries.push(updateNodeAtAnalysisCatalogQuery(nodeToUpdate.id(), Node.STATUS.READY));
        });

        if (asyncQueries.length > 0) {
            return self.enqueue(asyncQueries, callback);
        } else {
            return callback(null);
        }
    });
};

var trackNodeQueryTemplate = dot.template([
    'WITH RECURSIVE search_dag(node_id, input_nodes) AS (',
    '    SELECT node_id, input_nodes FROM cdb_analysis_catalog WHERE node_id = \'{{=it._nodeId}}\'',
    '    UNION',
    '    -- recursive search',
    '    SELECT c.node_id, c.input_nodes',
    '    FROM search_dag s, cdb_analysis_catalog c',
    '    WHERE c.node_id = ANY(s.input_nodes)',
    ')',
    'UPDATE cdb_analysis_catalog',
    'SET',
    '    used_at = NOW(),',
    '    hits = hits + 1,',
    '    last_used_from = \'{{=it._nodeId}}\'',
    'WHERE node_id IN (select node_id from search_dag)'
].join('\n'));

DatabaseService.prototype.trackAnalysis = function(analysis, callback) {
    var node = analysis.getRoot();

    // we don't want to track source nodes as high level nodes
    if (node.type === Source.TYPE) {
        return callback(null);
    }
    var sql = trackNodeQueryTemplate({ _nodeId: node.id() });
    debug('Tracking analysis with SQL: %s', sql);
    this.queryRunner.run(sql, QUERY_RUNNER_WRITE_OP, function(err, resultSet) {
        debug(err, resultSet);
        return callback(err);
    });
};
<commit_msg>Use setStatusFromInputNodes after previous input nodes have been updated<commit_after>'use strict';

var async = require('async');
var dot = require('dot');
dot.templateSettings.strip = false;

var Node = require('../node/node');
var Source = require('../node/nodes/source');

var debug = require('../util/debug')('database-service');

var QueryRunner = require('../postgresql/query-runner');
var QueryParser = require('../postgresql/query-parser');
var BatchClient = require('../postgresql/batch-client');

var QUERY_RUNNER_READONLY_OP = true;
var QUERY_RUNNER_WRITE_OP = !QUERY_RUNNER_READONLY_OP;

var createQueryTemplate = dot.template([
    'CREATE TABLE {{=it._targetTableName}} AS',
    '{{=it._query}}',
    'LIMIT 0'
].join('\n'));

function DatabaseService(dbParams, batchParams) {
    this.queryRunner = new QueryRunner(dbParams);
    this.queryParser = new QueryParser(this.queryRunner);

    var inlineExecution = batchParams.inlineExecution || false;
    var hostHeaderTemplate = batchParams.hostHeaderTemplate || '{{=it.username}}.localhost.lan';
    this.batchClient = new BatchClient(
        batchParams.endpoint,
        batchParams.username,
        batchParams.apiKey,
        hostHeaderTemplate,
        inlineExecution,
        this.queryRunner
    );
}

module.exports = DatabaseService;

DatabaseService.prototype.run = function(query, callback) {
    this.queryRunner.run(query, QUERY_RUNNER_READONLY_OP, callback);
};

var affectedTableRegexCache = {
    bbox: /!bbox!/g,
    scaleDenominator: /!scale_denominator!/g,
    pixelWidth: /!pixel_width!/g,
    pixelHeight: /!pixel_height!/g
};

DatabaseService.prototype.getLastUpdatedTimeFromAffectedTables = function(sql, callback) {
    sql = sql
        .replace(affectedTableRegexCache.bbox, 'ST_MakeEnvelope(0,0,0,0)')
        .replace(affectedTableRegexCache.scaleDenominator, '0')
        .replace(affectedTableRegexCache.pixelWidth, '1')
        .replace(affectedTableRegexCache.pixelHeight, '1');
    sql = 'SELECT max(updated_at) FROM CDB_QueryTables_Updated_At($camshaft$' + sql + '$camshaft$)';

    this.queryRunner.run(sql, QUERY_RUNNER_READONLY_OP, function(err, resultSet) {
        if (err) {
            return callback(err);
        }

        var defaultLastUpdatedTime = new Date();
        var rows = resultSet.rows || [defaultLastUpdatedTime];
        var lastUpdatedTimeFromAffectedTables = (rows[0] && rows[0].max) || defaultLastUpdatedTime;

        return callback(null, lastUpdatedTimeFromAffectedTables);
    });
};

DatabaseService.prototype.createTable = function(targetTableName, query, callback) {
    var createQuery = createQueryTemplate({
        _targetTableName: targetTableName,
        _query: query
    });

    this.queryRunner.run(createQuery, QUERY_RUNNER_WRITE_OP, function(err, resultSet) {
        debug('TODO: register table');
        return callback(err, resultSet);
    });
};

DatabaseService.prototype.tableExists = function(targetTableName, callback) {
    var existsQuery = 'select * from ' + targetTableName + ' limit 0';
    this.queryRunner.run(existsQuery, QUERY_RUNNER_READONLY_OP, function(err, resultSet) {
        var tableExists = !err && resultSet;
        return callback(null, tableExists);
    });
};

DatabaseService.prototype.createTableIfNotExists = function(targetTableName, outputQuery, callback) {
    var self = this;
    this.tableExists(targetTableName, function(err, tableExists) {
        if (tableExists) {
            return callback(null, tableExists);
        }

        self.createTable(targetTableName, outputQuery, function(err) {
            if (err) {
                return callback(err, tableExists);
            }

            return callback(null, tableExists);
        });
    });
};

DatabaseService.prototype.getColumnNames = function(query, callback) {
    this.queryParser.getColumnNames(query, callback);
};

DatabaseService.prototype.enqueue = function(queries, callback) {
    this.batchClient.enqueue(queries, callback);
};

var registerNodeQueryTemplate = dot.template([
    'SELECT',
    '    \'{{=it._nodeId}}\',',
    '    $json_def${{=it._analysisDef}}$json_def$::json,',
    '    {{=it._inputNodes}},',
    '    {{=it._updatedAt}},',
    '    \'{{=it._status}}\'',
    'WHERE NOT EXISTS (SELECT * FROM cdb_analysis_catalog WHERE node_id = \'{{=it._nodeId}}\')'
].join('\n'));

var EMPTY_ARRAY_SQL = '\'{}\'';
function pgArray(input, cast) {
    if (input.length === 0) {
        return cast ? ('ARRAY[]::' + cast + '[]') : EMPTY_ARRAY_SQL;
    }
    return 'ARRAY[' + input.join(', ') + ']';
}

function pgQuoteCastMapper(cast) {
    return function(input) {
        return '\'' + input + '\'' + (cast ? ('::' + cast) : '');
    };
}

DatabaseService.prototype.setUpdatedAtForSources = function(analysis, callback) {
    if (analysis.getRoot().type === Source.TYPE) {
        return callback(null);
    }

    var self = this;

    var sourceNodes = analysis.getNodes().filter(function(node) { return node.type === Source.TYPE; });
    async.map(sourceNodes,
        function(node, done) {
            node.getAndSetUpdatedAt(self, done);
        },
        callback
    );
};

DatabaseService.prototype.registerAnalysisInCatalog = function(analysis, callback) {
    var nodes = analysis.getSortedNodes();
    if (!Array.isArray(nodes) || nodes.length === 0 || analysis.getRoot().type === Source.TYPE) {
        return callback(null);
    }
    var nodesSql = [];
    nodes.forEach(function(node) {
        debug(node.type, node.getUpdatedAt());
        var updatedAt = node.getUpdatedAt();
        nodesSql.push(registerNodeQueryTemplate({
            _nodeId: node.id(),
            _inputNodes: pgArray(
                node.getInputNodes().map(function(node) { return node.id(); }).map(pgQuoteCastMapper()), 'char[40]'
            ),
            _updatedAt: (updatedAt === null ? 'NULL' : 'TIMESTAMP WITH TIME ZONE \'' + updatedAt.toISOString() + '\''),
            _status: node.getStatus(),
            _analysisDef: JSON.stringify(node.toJSON())
        }));
    });
    var sql = 'INSERT INTO cdb_analysis_catalog' +
        '(node_id, analysis_def, input_nodes, updated_at, status)\n' + nodesSql.join('\nUNION ALL\n');

    debug('Registering node with SQL: %s', sql);
    this.queryRunner.run(sql, QUERY_RUNNER_WRITE_OP, function(err, resultSet) {
        debug(err, resultSet);
        return callback(err);
    });
};

function deleteFromCacheTableQuery(targetTableName) {
    return 'DELETE FROM ' + targetTableName;
}

function populateCacheTableQuery(targetTableName, outputQuery) {
    return 'INSERT INTO ' + targetTableName + ' ' + outputQuery;
}

function updateNodeAtAnalysisCatalogQuery(nodeId, status) {
    return [
        'UPDATE cdb_analysis_catalog SET',
            'status = \'' + status + '\',',
        'updated_at = NOW()',
            'WHERE node_id = \'' + nodeId + '\''
    ].join('\n');
}

DatabaseService.prototype.queueAnalysisOperations = function(analysis, callback) {
    if (analysis.getRoot().type === Source.TYPE) {
        return callback(null);
    }

    var self = this;

    var sortedNodes = analysis.getSortedNodes();
    var sortedNodesIds = sortedNodes.map(function(node) { return node.id(); });
    var nodesById = analysis.getNodes().reduce(function(byId, node) {
        if (!byId.hasOwnProperty(node.id())) {
            byId[node.id()] = [];
        }
        byId[node.id()].push(node);
        return byId;
    }, {});
    var query = [
        'SELECT node_id, updated_at, status',
        'FROM cdb_analysis_catalog',
        'WHERE node_id IN (',
            sortedNodesIds
                .filter(function(node) { return node.type !== Source.TYPE; })
                .map(pgQuoteCastMapper()),
        ')'
    ].join('\n');

    this.queryRunner.run(query, function(err, resultSet) {
        if (err) {
            return callback(err);
        }

        var rows = resultSet.rows || [];
        rows.forEach(function(row) {
            var nodeId = row.node_id;
            var nodes = nodesById[nodeId];
            if (nodes) {
                nodes.forEach(function(node) {
                    node.setUpdatedAt(row.updated_at || null);
                    node.setStatus(row.status);
                });
            }
        });

        var nodesToQueueForUpdate = [];

        sortedNodes.forEach(function(node) {
            var inputNodes = node.getInputNodes();
            var startingLastUpdate = (inputNodes.length > 0 ? null : node.getUpdatedAt());
            var inputNodesLastUpdate = inputNodes.reduce(function(lastUpdate, inputNode) {
                var inputNodeUpdatedAt = inputNode.getUpdatedAt();

                if (inputNodeUpdatedAt === null) {
                    return new Date();
                }

                if (lastUpdate === null) {
                    return inputNodeUpdatedAt;
                }

                return lastUpdate.getTime() > inputNodeUpdatedAt.getTime() ? lastUpdate : inputNodeUpdatedAt;
            }, startingLastUpdate);

            var nodeUpdatedAt = node.getUpdatedAt();
            if (nodeUpdatedAt === null || inputNodesLastUpdate.getTime() > nodeUpdatedAt.getTime()) {
                nodesToQueueForUpdate.push(node);
            }
        });

        var asyncQueries = [];

        // we want to mark all nodes updated_at to NOW() and status to pending before doing any work on them
        nodesToQueueForUpdate.forEach(function(nodeToUpdate) {
            nodeToUpdate.setStatusFromInputNodes();
            asyncQueries.push(updateNodeAtAnalysisCatalogQuery(nodeToUpdate.id(), nodeToUpdate.getStatus()));
        });

        nodesToQueueForUpdate.forEach(function(nodeToUpdate) {
            if (nodeToUpdate.shouldCacheQuery()) {
                var targetTableName = nodeToUpdate.getTargetTable();
                asyncQueries.push(deleteFromCacheTableQuery(targetTableName));
                asyncQueries.push(updateNodeAtAnalysisCatalogQuery(nodeToUpdate.id(), Node.STATUS.RUNNING));
                asyncQueries.push(populateCacheTableQuery(targetTableName, nodeToUpdate.sql()));
            }
            asyncQueries.push(updateNodeAtAnalysisCatalogQuery(nodeToUpdate.id(), Node.STATUS.READY));
        });

        if (asyncQueries.length > 0) {
            return self.enqueue(asyncQueries, callback);
        } else {
            return callback(null);
        }
    });
};

var trackNodeQueryTemplate = dot.template([
    'WITH RECURSIVE search_dag(node_id, input_nodes) AS (',
    '    SELECT node_id, input_nodes FROM cdb_analysis_catalog WHERE node_id = \'{{=it._nodeId}}\'',
    '    UNION',
    '    -- recursive search',
    '    SELECT c.node_id, c.input_nodes',
    '    FROM search_dag s, cdb_analysis_catalog c',
    '    WHERE c.node_id = ANY(s.input_nodes)',
    ')',
    'UPDATE cdb_analysis_catalog',
    'SET',
    '    used_at = NOW(),',
    '    hits = hits + 1,',
    '    last_used_from = \'{{=it._nodeId}}\'',
    'WHERE node_id IN (select node_id from search_dag)'
].join('\n'));

DatabaseService.prototype.trackAnalysis = function(analysis, callback) {
    var node = analysis.getRoot();

    // we don't want to track source nodes as high level nodes
    if (node.type === Source.TYPE) {
        return callback(null);
    }
    var sql = trackNodeQueryTemplate({ _nodeId: node.id() });
    debug('Tracking analysis with SQL: %s', sql);
    this.queryRunner.run(sql, QUERY_RUNNER_WRITE_OP, function(err, resultSet) {
        debug(err, resultSet);
        return callback(err);
    });
};
<|endoftext|>