Home Reference Source

packages/skygear-core/lib/cloud/container.js

/**
 * Copyright 2015 Oursky Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* eslint camelcase: 0 */
import _ from 'lodash';

import {BaseContainer} from '../container';

import {CloudCodeAuthContainer} from './auth';
import {RelationContainer} from '../relation';
import {DatabaseContainer} from '../database';
import {PubsubContainer} from '../pubsub';
import {CloudCodePushContainer} from './push';
import {settings} from './settings';

const PUBSUB_OPEN_TIMEOUT_DURATION = 10000;
const PUBSUB_CLOSE_TIMEOUT_DURATION = 3000;

export default class CloudCodeContainer extends BaseContainer {

  constructor({ sendPluginRequest, asUserId } = {}) {
    super();

    this.asUserId = asUserId;
    this.sendPluginRequest = !!sendPluginRequest;

    this._auth = new CloudCodeAuthContainer(this);
    this._relation = new RelationContainer(this);
    this._db = new DatabaseContainer(this);
    this._pubsub = new PubsubContainer(this);
    this._push = new CloudCodePushContainer(this);
  }

  get auth() {
    return this._auth;
  }

  get relation() {
    return this._relation;
  }

  get publicDB() {
    return this._db.public;
  }

  get privateDB() {
    return this._db.private;
  }

  get pubsub() {
    return this._pubsub;
  }

  get push() {
    return this._push;
  }

  _prepareRequestObject(action, data) {
    let requestObject = super._prepareRequestObject(action, data);

    if (this.auth.accessToken) {
      requestObject = requestObject.set(
        'X-Skygear-Access-Token', this.auth.accessToken);
    }

    return requestObject;
  }

  _prepareRequestData(action, data) {
    let requestData = super._prepareRequestData(action, data);
    let extraData = {};

    if (this.auth.accessToken) {
      extraData.access_token = this.auth.accessToken;
    }

    if (this.asUserId) {
      extraData._user_id = this.asUserId;
    }

    if (this.sendPluginRequest) {
      extraData._from_plugin = true;
    }

    return _.assign(extraData, requestData);
  }

}

/**
 * Get a configured CloudCodeContainer.
 *
 * @param {String} [userId] - user ID of the user. If not specified, the default
 * is determined by the server.
 * @return {CloudCodeContainer} the cloud code container that acts as
 * the specified user.
 */
export function getContainer(userId) {
  const container = new CloudCodeContainer();
  container.apiKey = settings.masterKey;
  container.endPoint = settings.skygearEndpoint + '/';
  if (userId) {
    container.asUserId = userId;
  }
  return container;
}

/**
 * Publish multiple events to multiple channels.
 *
 * @param {String[]} [channels] - name of the channels
 * @param {Object[]} [eventsData] - events data to be published
 * @return {Promise<CloudCodeContainer>} promise with the cloud code container
 */
export async function publishEventsToChannels(channels, eventsData) {
  const container = new CloudCodeContainer();
  const pubsub = container.pubsub;

  // Try to open configure pubsub within the tiemout duration
  const pubsubOpened = await Promise.race([
    new Promise((resolve) => {
      pubsub.onOpen(() => {
        resolve(true);
      });
      return container.config({
        apiKey: settings.masterKey,
        endPoint: settings.skygearEndpoint + '/'
      }).then(() => {
        pubsub._pubsub.reconfigure();
        return;
      });
    }),
    new Promise((resolve) => {
      setTimeout(() => {
        resolve(false);
      }, PUBSUB_OPEN_TIMEOUT_DURATION);
    })
  ]);

  if (!pubsubOpened) {
    throw new Error(
      `Failed to open Skygear Pubsub in ${PUBSUB_OPEN_TIMEOUT_DURATION}ms`
    );
  }

  _.map(channels, (eachChannel) => {
    _.map(eventsData, (eachEventData) => {
      pubsub.publish(eachChannel, eachEventData);
    });
  });

  // Try to close pubsub within the tiemout duration
  const pubsubClosed = await Promise.race([
    new Promise((resolve) => {
      pubsub.onClose(() => {
        resolve(true);
      });
      pubsub._pubsub.close();
    }),
    new Promise((resolve) => {
      setTimeout(() => {
        resolve(false);
      }, PUBSUB_CLOSE_TIMEOUT_DURATION);
    })
  ]);

  if (!pubsubClosed) {
    console.warn(
      `Failed to close Skygear Pubsub in ${PUBSUB_CLOSE_TIMEOUT_DURATION}ms`
    );
  }

  return container;
}