index.js 4.04 KB
import Validator from 'swagger-model-validator';
import axios from 'axios';

/**
 * Handle authentication and send/receive with the backbone
 */
export class Adapter {
  constructor(protocol, config) {
    this.protocol = protocol;
    this.config = config;
    this.axios = axios;
    this.validator = new Validator(protocol.schema);
    this.auth();
  }

  /**
   * Test parsing the message based on the provided protocol schema
   * 
   * The message must be successfully json decoded into an object 
   * prior to validation
   * 
   * At present this returns the validation result which is an 
   * object containing a boolean valid field as well as details 
   * of any errors. 
   * @param {object} message 
   * @returns {object}
   */
  validate(message) {
    return this.validator.validate(
      message,
      this.protocol.schema.definitions.Message
    );
  }

  /**
   * Ensure the token in this.credentials is still valid
   * @returns {boolean}
   */
  tokenValid() {
    let valid = false;
    if (this.credentials) {
      const now = new Date().toISOString();
      valid = this.credentials.expiry > now;
    }
    return valid;
  }

  /**
   * Return an http headers object containing a bearer token authorisation header
   * @returns {object}
   */
  getAuthorizationHeader() {
    if (!this.tokenValid())
      return this.auth().then((response) => {
        return {
          Authorization: `Bearer ${this.credentials.token}`,
        };
      });
    else {
      return Promise.resolve({
        Authorization: `Bearer ${this.credentials.token}`,
      });
    }
  }

  /**
   * Do a client credentials grant and store in this.credentials
   * @returns {object}
   */
  auth() {
    let adapterConfig = this.config;
    console.error('config', adapterConfig);
    return this.axios
      .get(`${adapterConfig.api}/token`, {
        params: {
          client_id: adapterConfig.client_id,
          secret: adapterConfig.secret,
        },
      })
      .then((response) => {
        this.credentials = response.data;
        return response;
      });
  }

  /**
   * Call the GET /receive endpoint and process the messages with decode
   * 
   * Returns the response  
   * @returns {object}
   */
  poll() {
    let adapterConfig = this.config;
    return this.getAuthorizationHeader()
      .then((headers) => {
        return this.axios.get(`${adapterConfig.api}/receive`, {
          headers,
        });
      })
      .then((response) => {
        response.data.forEach((message) => {
          const parsed = JSON.parse(message.message);
          const validation = this.validate(parsed);
          if (validation.valid) {
            const type = this.protocol.getType(parsed);
            this.protocol.decode(type, parsed);
          } 
        });
        return response;
      });
  }

  /**
   * Publish a message to the backbone with the specified topic 
   * 
   * Messages should be passed through encode before sending
   * @param {string} topic 
   * @param {string} body 
   * @returns 
   */
  publish(topic, body) {
    let adapterConfig = this.config;
    return this.getAuthorizationHeader()
      .then((headers) => {
        return this.axios.post(
          `${adapterConfig.api}/send`,
          {
            topic,
            body,
          },
          {
            headers,
          }
        );
      })
      .then((response) => {
        return response;
      });
  }

  /**
   * Broadcast the message on the backbone
   * 
   * Broadcast messages bypass the normal publisher queues 
   * this means they can be used to deliver messages more 
   * quickly in an emergency scenario.
   * 
   * Messages should be passed through encode before sending
   * @param {*} body 
   * @returns 
   */
  broadcast(body) {
    let adapterConfig = this.config;
    return this.getAuthorizationHeader()
      .then((headers) => {
        return this.axios.post(
          `${adapterConfig.api}/notify`,
          {
            body,
          },
          {
            headers,
          }
        );
      })
      .then((response) => {
        return response;
      });
  }
}