lib_messages.js

/**
 * @module brig/messages
 */
'use strict';


class Message {

  /**
   * Abstract message base class.
   *
   * @cosntructor
   */
  constructor() {}

  static get VoteRequestOptions() {
    return [
      'candidateId',
      'currentTerm',
      'currentLogLength',
      'currentLogLastTerm'
    ].sort();
  }

  static get VoteResponseOptions() {
    return [
      'voterId',
      'term',
      'granted'
    ].sort();
  }
 
  static get LogRequestOptions() {
    return [
      'leaderId',
      'term',
      'prefixLength',
      'prefixTerm',
      'leaderCommit',
      'suffix'
    ].sort();
  } 

  static get LogResponseOptions() {
    return [
      'followerId',
      'term',
      'ack',
      'success'
    ].sort();
  }

  static get BroadcastOptions() {
    return [
      'payload'
    ].sort();
  }

  static from(obj) {
    const keys = JSON.stringify(Object.keys(obj).sort());

    switch (keys) {
      case JSON.stringify(Message.VoteRequestOptions):
        return new VoteRequestMessage(obj);
      case JSON.stringify(Message.VoteResponseOptions):
        return new VoteResponseMessage(obj);
      case JSON.stringify(Message.LogRequestOptions):
        return new LogRequestMessage(obj);
      case JSON.stringify(Message.LogResponseOptions):
        return new LogResponseMessage(obj);
      case JSON.stringify(Message.BroadcastOptions):
        return new BroadcastMessage(obj.payload);
      default:
        throw new Error('Failed to detect message type from keys: ' + keys + '   ' + JSON.stringify(Message.VoteRequestOptions));
    }
  }

  serialize() {
    return JSON.stringify(this);
  }

}

module.exports.Message = Message;


class VoteRequestMessage extends Message {

  /**
   * @typedef {object} module:brig/messages~VoteRequestMessageOptions
   * @property {buffer} candidateId - Node ID to request vote for.
   * @property {number} currentTerm - The current election term.
   * @property {number} currentLogLength - The length of the candidates current log.
   * @property {number} currentLogLastTerm - The last term in the current log.
   */

  /**
   *
   * @constructor
   * @param {module:brig/messages~VoteRequestMessageOptions} options - Params 
   * to include in RV message.
   */
  constructor(params = {}) {
    super();
    this.candidateId = params.candidateId;
    this.currentTerm = params.currentTerm;
    this.currentLogLength = params.currentLogLength;
    this.currentLogLastTerm = params.currentLogLastTerm;
  }

  static get method() {
    return 'REQUEST_VOTE';
  }

}

module.exports.VoteRequestMessage = VoteRequestMessage;


class VoteResponseMessage extends Message {

  /**
   * @typedef {object} module:brig/messages~VoteResponseMessageOptions
   * @property {buffer} voterId - Node ID to request vote for.
   * @property {number} term - The current election term.
   * @property {boolean} granted - Indicates if the peer was voted for.
   */

  /**
   *
   * @constructor
   * @param {module:brig/messages~VoteResponseMessageOptions} result - Result 
   * to include in response.
   */
  constructor(params = {}) {
    super();
    this.voterId = params.voterId;
    this.term = params.term;
    this.granted = params.granted;
  }

  static get method() {
    return 'CAST_VOTE';
  }

}

module.exports.VoteResponseMessage = VoteResponseMessage;

class LogRequestMessage extends Message {

  /**
   * @typedef {object} module:brig/messages~LogRequestMessageOptions
   * @property {string} leaderId - The current leader.
   * @property {number} term - The current election term.
   * @property {number} prefixLength - Number of entries prior.
   * @property {number} leaderCommit - Latest commit from leader.
   * @property {Array.<module:brig/log~LogEntry>} suffix - Entries.
   */

  /**
   *
   * @constructor
   * @param {module:brig/messages~LogRequestMessageOptions} options - Params 
   * to include in AE message.
   */
  constructor(params = {}) {
    super();
    this.leaderId = params.leaderId;
    this.term = params.term;
    this.prefixLength = params.prefixLength;
    this.prefixTerm = params.prefixTerm;
    this.leaderCommit = params.leaderCommit;
    this.suffix = params.suffix || [];
  }

  static get method() {
    return 'APPEND_LOG';
  }

}

module.exports.LogRequestMessage = LogRequestMessage;


class LogResponseMessage extends Message {

  /**
   * @typedef {object} module:brig/messages~LogResponseMessageOptions
   * @property {string} followerId - Peer ID of the follower.
   * @property {number} term - Election term.
   * @property {number} ack - Total acks for the entry.
   * @property {boolean} success - True if peer  write the entry.
   */

  /**
   *
   * @constructor
   * @param {module:brig/messages~LogResponseMessageOptions} result - Result 
   * to include in response.
   */
  constructor(params = {}) {
    super();
    this.followerId = params.followerId;
    this.term = params.term;
    this.ack = params.ack;
    this.success = params.success;
  }

  static get method() {
    return 'ACK_APPEND';
  }

}

module.exports.LogResponseMessage = LogResponseMessage;


class BroadcastMessage extends Message {

  /**
   *
   * @constructor
   * @param {object} payload - Log entry data to replicate.
   */
  constructor(payload) {
    super();
    this.payload = payload;
  }

  static get method() {
    return 'REQUEST_LOG_RELAY';
  }

}

module.exports.BroadcastMessage = BroadcastMessage;