exchange.js

import BaseComponent from './basecomponent'
import BindingMessage from './messages/bindingmessage'
import AlternateMessage from './messages/alternatemessage'

class Exchange extends BaseComponent {
  /**
   * Exchange class represents a component which receives messages and route them to queues.
   *
   * @param {number} x - x position of the exchange
   * @param {number} y - y position of the exchange
   * @param {string} name - identifier
   * @param {string} type - exchange type
   * @param {Exchange} alternate - alternate exchange
   * @extends BaseComponent
   */
  constructor(x, y, name, type, alternate) {
    super(x, y)
    this.name = name
    this.type = type || 'direct'
    this.radius = 15
    this.bindings = []
    this.alternate = alternate || null
  }

  /**
   * Sets alternate exchange for exchange.
   *
   * @param {Exchange} alternate - alternate exchange
   */
  setAlternate(exchange) {
    this.alternate = exchange
  }

  /**
   * Removes a binding from the exchange.
   *
   * @param {Binding} binding - Binding object
   */
  removeBinding(binding) {
    const bindingIndex = this.bindings.findIndex((c) => c === binding)
    if (bindingIndex !== -1) {
      if (this.bindings.length === 1) {
        this.bindings = []
      } else {
        this.bindings.splice(bindingIndex, 1)
      }
    }
  }

  /**
   * Handler for arriving messages.
   *
   * @param {ExchangeMessage} msg
   */
  messageArrived(msg) {
    const { routingKey, fillStyle } = msg
    let sendMsg = false
    this.bindings.forEach((val) => {
      if (this.type === 'topic') {
        // TODO: more specific to handle * or #
        if (val.routingKey === routingKey || val.routingKey === '#') {
          new BindingMessage(this.x, this.y, val, fillStyle).addToScene(
            this.scene
          )
          sendMsg = true
        }
      } else if (this.type === 'direct') {
        if (val.destination.name === routingKey) {
          new BindingMessage(this.x, this.y, val, fillStyle).addToScene(
            this.scene
          )
          sendMsg = true
        }
        // fanout
      } else {
        new BindingMessage(this.x, this.y, val, fillStyle).addToScene(
          this.scene
        )
        sendMsg = true
      }
    })
    // no bindings, message is lost
    if (this.bindings.length === 0) {
      sendMsg = false
    }
    if (this.alternate && !sendMsg) {
      new AlternateMessage(
        this.x,
        this.y,
        this.alternate,
        routingKey,
        fillStyle
      ).addToScene(this.scene)
      sendMsg = true
    }
    // message not send, then it's lost
    if (!sendMsg) {
      this.scene.lostMessages += 1
    }
    this.scene.removeActor(msg)
  }

  render() {
    if (this.alternate) {
      this.ctx.beginPath()
      this.ctx.strokeStyle = '#000'
      this.ctx.setLineDash([3, 3])
      this.ctx.lineWidth = 1
      this.ctx.moveTo(this.x, this.y)
      this.ctx.lineTo(this.alternate.x, this.alternate.y)
      this.ctx.stroke()
    }
    // shadow
    this.ctx.globalAlpha = 0.4
    this.ctx.beginPath()
    this.ctx.fillStyle = '#000'
    this.ctx.arc(this.x + 2, this.y + 2, this.radius, 0, 2 * Math.PI)
    this.ctx.fill()

    this.ctx.globalAlpha = 1.0
    this.ctx.beginPath()
    // this.ctx.fillStyle = gradient;
    this.ctx.fillStyle = '#ccc'
    this.ctx.setLineDash([])
    this.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI)
    this.ctx.fill()

    if (this.dragged) {
      this.ctx.stroke()
    }

    if (this.hover) {
      this.ctx.stroke()
    }

    this.ctx.font = '10px Arial'
    this.ctx.fillStyle = '#000'
    this.ctx.fillText(
      this.name,
      this.x - this.radius,
      this.y + this.radius + 10
    )
    this.ctx.fillText(
      this.type,
      this.x - this.radius,
      this.y + this.radius + 22
    )
  }
}

export default Exchange