Home Reference Source

src/client.js

/** * Imports ***/
import Collection from './cj';
import Request from './request';
import { PluginList } from './plugin';
import { PluginMetaList, UserCollabPluginMetaList } from './pluginmeta';
import { UserFavoritePluginMetaList } from './pluginmeta';
import { PluginStarList } from './pluginstar';
import { PluginCollaboratorList } from './plugincollab';
import { PipelineList } from './pipeline';
import User from './user';

/**
 * API client object.
 */
export default class Client {
  /**
   * Constructor
   *
   * @param {string} url - url of the ChRIS store service
   * @param {Object} [auth=null] - authentication object
   * @param {string} [auth.token] - authentication token
   */
  constructor(url, auth = null) {
    /** @type {string} */
    this.url = url;

    /** @type {Object} */
    this.auth = auth;

    /* Urls of the high level API resources */
    this.pluginMetasUrl = this.url;
    this.favoritePluginMetasUrl = '';
    this.collabPluginMetasUrl = '';
    this.pluginStarsUrl = '';
    this.pluginsUrl = '';
    this.pipelinesUrl = '';
    this.userUrl = '';
  }

  /**
   * Set the urls of the high level API resources.
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise
   */
  setUrls(timeout = 30000) {
    return this.getPluginMetas(null, timeout);
  }

  /**
   * Get a paginated list of plugin metas from the REST API given query search
   * parameters. If no search parameters then get the default first page.
   *
   * @param {Object} [searchParams=null] - search parameters object
   * @param {number} [searchParams.limit] - page limit
   * @param {number} [searchParams.offset] - page offset
   * @param {number} [searchParams.id] - match plugin meta id exactly with this number
   * @param {string} [searchParams.name] - match plugin meta name containing this string
   * @param {string} [searchParams.name_exact] - match plugin meta name exactly with this string
   * @param {string} [searchParams.title] - match plugin meta title containing this string
   * @param {string} [searchParams.type] - match plugin meta type exactly with this string
   * @param {string} [searchParams.category] - match plugin meta category exactly with this string
   * @param {string} [searchParams.authors] - match plugin meta authors containing this string
   * @param {number} [searchParams.min_creation_date] - match feed creation date gte this date
   * @param {number} [searchParams.max_creation_date] - match feed creation date lte this date
   * @param {string} [searchParams.name_title_category] - match plugin meta name, title or
   * category containing this string
   * @param {string} [searchParams.name_authors_category] - match plugin meta name, authors or
   * category containing this string
   * @param {string} [searchParams.owner_username] - match plugin meta owner's username exactly
   * with this string
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to a ``PluginMetaList`` object
   */
  getPluginMetas(searchParams = null, timeout = 30000) {
    const plgMetaList = new PluginMetaList(this.pluginMetasUrl, this.auth);

    return plgMetaList.get(searchParams, timeout).then(plgMetaList => {
      const coll = plgMetaList.collection;
      const getUrl = Collection.getLinkRelationUrls;

      if (!this.favoritePluginMetasUrl && this.auth) {
        this.favoritePluginMetasUrl = getUrl(coll, 'favorite_plugin_metas')[0];
      }
      if (!this.collabPluginMetasUrl && this.auth) {
        this.collabPluginMetasUrl = getUrl(coll, 'collab_plugin_metas')[0];
      }
      this.pluginStarsUrl = this.pluginStarsUrl || getUrl(coll, 'plugin_stars')[0];
      this.pluginsUrl = this.pluginsUrl || getUrl(coll, 'plugins')[0];
      this.pipelinesUrl = this.pipelinesUrl || getUrl(coll, 'pipelines')[0];
      this.userUrl = this.userUrl || getUrl(coll, 'user')[0];

      return plgMetaList;
    });
  }

  /**
   * Fetch a list of authenticated user's favorite plugin metas from the REST API.
   *
   * @param {Object} [params=null] - page parameters object
   * @param {number} [params.limit] - page limit
   * @param {number} [params.offset] - page offset
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to a ``UserFavoritePluginMetaList`` object
   */
  getFavoritePluginMetas(params = null, timeout = 30000) {
    return this._fetchRes('favoritePluginMetasUrl', UserFavoritePluginMetaList, params, timeout);
  }

  /**
   * Fetch a list of authenticated user's collaborated plugin metas from the REST API.
   *
   * @param {Object} [params=null] - page parameters object
   * @param {number} [params.limit] - page limit
   * @param {number} [params.offset] - page offset
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to a ``UserCollabPluginMetaList`` object
   */
  getCollabPluginMetas(params = null, timeout = 30000) {
    return this._fetchRes('collabPluginMetasUrl', UserCollabPluginMetaList, params, timeout);
  }

  /**
   * Get a plugin meta resource object given its id.
   *
   * @param {number} id - plugin meta id
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to a ``PluginMeta`` object
   */
  getPluginMeta(id, timeout = 30000) {
    return this.getPluginMetas({ id: id }, timeout).then(listRes => listRes.getItem(id));
  }

  /**
   * Get a paginated list of authenticated-user-specific plugin star data (descriptors)
   * given query search parameters. If no search parameters is given then get the default
   * first page.
   *
   * @param {Object} [searchParams=null] - search parameters
   * @param {number} [searchParams.limit] - page limit
   * @param {number} [searchParams.offset] - page offset
   * @param {number} [searchParams.id] - match plugin star id exactly with this number
   * @param {string} [searchParams.plugin_name] - match plugin name exactly with this string
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to a ``PluginStarList`` object
   */
  getPluginStars(searchParams = null, timeout = 30000) {
    return this._fetchRes('pluginStarsUrl', PluginStarList, searchParams, timeout);
  }

  /**
   * Get an authenticated-user-specific plugin star resource object given its id.
   *
   * @param {number} id - plugin star id
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to a ``PluginStar`` object
   */
  getPluginStar(id, timeout = 30000) {
    return this.getPluginStars({ id: id }, timeout).then(listRes => listRes.getItem(id));
  }

  /**
   * Create a new plugin star resource through the REST API.
   *
   * @param {Object} data - request JSON data object
   * @param {string} data.plugin_name - plugin's name
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to ``PluginStar`` object
   */
  createPluginStar(data, timeout = 30000) {
    const createRes = () => {
      const res = new PluginStarList(this.pluginStarsUrl, this.auth);
      return res.post(data, timeout).then(res => res.getItems()[0]);
    };
    return this.pluginStarsUrl ? createRes() : this.setUrls().then(() => createRes());
  }

  /**
   * Create a new plugin collaborator resource through the REST API.
   *
   * @param {number} pluginMetaId - plugin meta id
   * @param {Object} data - request JSON data object
   * @param {string} data.username - collaborator username
   * @param {string} data.role - collaborator role
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to ``PluginCollaborator`` object
   */
  createPluginCollaborator(pluginMetaId, data, timeout = 30000) {
    return this.getPluginMeta(pluginMetaId, timeout)
      .then(plgMeta => {
        const collaboratorsUrl = Collection.getLinkRelationUrls(plgMeta.collection.items[0], 'collaborators');
        const collabList = new PluginCollaboratorList(collaboratorsUrl[0], this.auth);
        return collabList.post(data, timeout);
      })
      .then(collabList => collabList.getItems()[0]);
  }

  /**
   * Get a paginated list of plugin data (descriptors) given query search
   * parameters. If no search parameters is given then get the default first
   * page.
   *
   * @param {Object} [searchParams=null] - search parameters
   * @param {number} [searchParams.limit] - page limit
   * @param {number} [searchParams.offset] - page offset
   * @param {number} [searchParams.id] - match plugin id exactly with this number
   * @param {string} [searchParams.name] - match plugin name containing this string
   * @param {string} [searchParams.name_latest] - match plugin name containing this string
   * and return only the latest version
   * @param {string} [searchParams.name_exact] - match plugin name exactly with this string
   * @param {string} [searchParams.name_exact_latest] - match plugin name exactly with this string
   * and return only the latest version
   * @param {string} [searchParams.dock_image] - match plugin docker image exactly with this string
   * @param {string} [searchParams.type] - match plugin type with this string
   * @param {string} [searchParams.category] - match plugin category containing this string
   * @param {string} [searchParams.owner_username] - match plugin username containing this string
   * @param {string} [searchParams.min_creation_date] - match plugin creation date after this date
   * @param {string} [searchParams.max_creation_date] - match plugin creation date before this date
   * @param {string} [searchParams.title] - match plugin title containing this string
   * @param {string} [searchParams.version] - match plugin version exactly with this string
   * @param {string} [searchParams.description] - match plugin description containing this string
   * @param {string} [searchParams.name_title_category] - match plugin name, title or category
   * containing this string
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to a ``PluginList`` object
   */
  getPlugins(searchParams = null, timeout = 30000) {
    return this._fetchRes('pluginsUrl', PluginList, searchParams, timeout);
  }

  /**
   * Get a plugin resource object given its id.
   *
   * @param {number} id - plugin id
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to a ``Plugin`` object
   */
  getPlugin(id, timeout = 30000) {
    return this.getPlugins({ id: id }, timeout).then(listRes => listRes.getItem(id));
  }

  /**
   * Create a new plugin resource through the REST API.
   *
   * @param {Object} data - request JSON data object
   * @param {string} data.name - plugin name
   * @param {string} data.dock_image - plugin docker image
   * @param {string} data.public_repo - plugin repo
   * @param {Object} uploadFileObj - custom file object
   * @param {Object} uploadFileObj.descriptor_file - file blob
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to ``Plugin`` object
   */
  createPlugin(data, uploadFileObj, timeout = 30000) {
    const createRes = () => {
      const res = new PluginList(this.pluginsUrl, this.auth);
      return res.post(data, uploadFileObj, timeout).then(res => res.getItems()[0]);
    };
    return this.pluginsUrl ? createRes() : this.setUrls().then(() => createRes());
  }

  /**
   * Get a paginated list of pipeline data (descriptors) given query search parameters.
   * If no search parameters is given then get the default first page.
   *
   * @param {Object} [searchParams=null] - search parameters
   * @param {number} [searchParams.limit] - page limit
   * @param {number} [searchParams.offset] - page offset
   * @param {number} [searchParams.id] - match pipeline id exactly with this number
   * @param {string} [searchParams.name] - match pipeline name containing this string
   * @param {string} [searchParams.category] - match pipeline category containing this string
   * @param {string} [searchParams.owner_username] - match pipeline's owner username exactly with this string
   * @param {string} [searchParams.description] - match pipeline description containing this string
   * @param {string} [searchParams.authors] - match pipeline authors containing this string
   * @param {string} [searchParams.min_creation_date] - match pipeline creation date after this date
   * @param {string} [searchParams.max_creation_date] - match pipeline creation date before this date
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to a ``PipelineList`` object
   */
  getPipelines(searchParams = null, timeout = 30000) {
    return this._fetchRes('pipelinesUrl', PipelineList, searchParams, timeout);
  }

  /**
   * Get a pipeline resource object given its id.
   *
   * @param {number} id - pipeline id
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to a ``Pipeline`` object
   */
  getPipeline(id, timeout = 30000) {
    return this.getPipelines({ id: id }, timeout).then(listRes => listRes.getItem(id));
  }

  /**
   * Create a new pipeline resource through the REST API.
   *
   * @param {Object} data - request JSON data object
   * @param {string} data.name - pipeline name
   * @param {string} data.plugin_tree - JSON string containing a plugin tree list
   * @param {string} [data.authors] - pipeline authors
   * @param {string} [data.category] - pipeline category
   * @param {string} [data.description] - pipeline description
   * @param {boolean} [data.locked=true] - pipeline status
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to ``Pipeline`` object
   */
  createPipeline(data, timeout = 30000) {
    const createRes = () => {
      const res = new PipelineList(this.pipelinessUrl, this.auth);
      return res.post(data, timeout).then(res => res.getItems()[0]);
    };
    return this.pipelinesUrl ? createRes() : this.setUrls().then(() => createRes());
  }

  /**
   * Get a user resource object for the currently authenticated user.
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to a ``User`` object
   */
  getUser(timeout = 30000) {
    return this._fetchRes('userUrl', User, null, timeout);
  }

  /**
   * Create a new user account.
   *
   * @param {string} usersUrl - url of the user accounts service
   * @param {string} username - username
   * @param {string} password - password
   * @param {string} email - user email
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to a ``User`` object
   */
  static createUser(usersUrl, username, password, email, timeout = 30000) {
    const req = new Request(undefined, 'application/vnd.collection+json', timeout);
    const userData = {
      template: {
        data: [
          { name: 'username', value: username },
          { name: 'password', value: password },
          { name: 'email', value: email },
        ],
      },
    };
    return req.post(usersUrl, userData).then(resp => {
      const coll = resp.data.collection;
      const userUrl = coll.items[0].href;
      const auth = { username: username, password: password };
      const user = new User(userUrl, auth);
      user.collection = coll;
      return user;
    });
  }

  /**
   * Fetch a user's login authorization token from the REST API.
   * @param {string} authUrl - url of the authorization service
   * @param {string} username - username
   * @param {string} password - password
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise, resolves to a ``string`` value
   */
  static getAuthToken(authUrl, username, password, timeout = 30000) {
    const req = new Request(undefined, 'application/json', timeout);
    const authData = {
      username: username,
      password: password,
    };
    return req.post(authUrl, authData).then(resp => resp.data.token);
  }

  /**
   * Helper method to run an asynchronous task defined by a task generator function.
   *
   * @param {function*()} taskGenerator - generator function
   */
  static runAsyncTask(taskGenerator) {
    Request.runAsyncTask(taskGenerator);
  }

  /**
   * Internal method to fetch a high level resource through the REST API.
   *
   * @param {string} resUrlProp -  property of the `this` object containing the url of the resource
   * @param {string} ResClass - resource class
   * @param {Object} [searchParams=null] - search parameters object
   * @param {number} [timeout=30000] - request timeout
   *
   * @return {Object} - JS Promise
   */
  _fetchRes(resUrlProp, ResClass, searchParams = null, timeout = 30000) {
    const getRes = () => {
      const res = new ResClass(this[resUrlProp], this.auth);
      return searchParams ? res.get(searchParams, timeout) : res.get(timeout);
    };
    return this[resUrlProp] ? getRes() : this.setUrls().then(() => getRes());
  }
}