<commit_before>/*
 * Kuzzle, a backend software, self-hostable and ready to use
 * to power modern apps
 *
 * Copyright 2015-2020 Kuzzle
 * mailto: support AT kuzzle.io
 * website: http://kuzzle.io
 *
 * 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
 *
 * https://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.
 */

'use strict';

const { difference } = require('lodash');
const Bluebird = require('bluebird');

const kerror = require('../../kerror');
const actionEnum = require('./actionEnum');
const { koncordeTest } = require('../../util/koncordeCompat');
const {
  DocumentNotification,
  ServerNotification,
  UserNotification,
} = require('./notification');

/**
 * Notification are meant to be dispatched on "channels" created when subscribing.
 * But some notification like TokenExpired don't have a specific channel to be received on,
 * so we need a constant channel name to send Kuzzle notification without having to subscribe.
 */
const KUZZLE_NOTIFICATION_CHANNEL = 'kuzzle:notification:channel';

/**
 * @typedef {Object} DocumentChanges
 * @property {string}  _id              of the document
 * @property {Object}  [_source]        of the document
 * @property {Object}  [_updatedFields] applied to the document (for updates)
 * @property {boolean} [created]        -- tells if this is a CREATE or REPLACE
 *                                      (for actionEnum.WRITE)
 */

/**
 * @class NotifierController
 */
class NotifierController {
  constructor(realtimeModule) {
    this.module = realtimeModule;
    this.ttl = global.kuzzle.config.limits.subscriptionDocumentTTL;
  }

  async init () {
    /**
     * Low-level document notification method, allocating a new Notification
     * message and dispatching it to a precomputed list of rooms.
     * @param {Array.<string>} rooms - list of koncorde rooms to notify
     * @param {DocumentNotification} notification
     * @param {Object} [opts]
     */
    global.kuzzle.onAsk(
      'core:realtime:document:dispatch',
      (rooms, notification, opts) => {
        return this._notifyDocument(rooms, notification, opts);
      });


    /**
     * Low-level user notification method, allocating a new Notification
     * message and dispatching it to the provided room
     * @param {string} room
     * @param {UserNotification}
     * @param {Object} [opts]
     */
    global.kuzzle.onAsk(
      'core:realtime:user:sendMessage',
      (room, notification, opts) => {
        return this._notifyUser(room, notification, opts);
      });

    /**
     * Notify about document creations, updates, replacements or deletions
     * @param {Request} request
     * @param {notifyActionEnum} action applied to documents
     * @param {Array.<DocumentChanges>} docs
     */
    global.kuzzle.onAsk(
      'core:realtime:document:mNotify',
      (request, action, docs) => this.notifyDocuments(request, action, docs));

    /**
     * Notify about document creations, updates, replacements or deletions
     * @param {Request} request
     * @param {notifyActionEnum} action applied to documents
     * @param {DocumentChanges} doc
     */
    global.kuzzle.onAsk(
      'core:realtime:document:notify',
      (request, action, doc) => this.notifyDocuments(request, action, [doc]));


    /**
     * Send a "token expired" notification to the target user
     * @param {string} connectionId
     */
    global.kuzzle.onAsk(
      'core:realtime:tokenExpired:notify',
      connectionId => this.notifyTokenExpired(connectionId));

    /**
     * Publish the provided request content to listening subscribers
     * @param {Request} request
     */
    global.kuzzle.onAsk('core:realtime:publish', request => this.publish(request));
  }

  /**
   * Broadcasts a notification about a document change or a
   * real-time message
   *
   * @param {Array} rooms - Subscribed rooms to notify
   * @param {Request} request - Request at the origin of the notification
   * @param {string} scope - 'in' or 'out'
   * @param {object} content - Document or message
   *
   * @returns {Promise}
   */
  async notifyDocument (rooms, request, scope, action, content) {
    if (rooms.length === 0) {
      return;
    }

    const notification = DocumentNotification.fromRequest(
      request,
      scope,
      action,
      content);

    global.kuzzle.emit('core:notify:document', {
      notification,
      rooms,
    });

    await this._notifyDocument(rooms, notification, {
      fromCluster: false
    });
  }

  /**
   * Broadcast a notification about a user entering or leaving
   * the provided room
   *
   * @param {string} room - Room entered or left
   * @param {Request} request - User (un)subscription request
   * @param {string} scope - 'in' or 'out'
   * @param {object} content - Notification additional informations
   *
   * @returns {Promise}
   */
  notifyUser (room, request, scope, content) {
    const notification = UserNotification.fromRequest(request, scope, content);

    global.kuzzle.emit('core:notify:user', {
      notification,
      room,
    });

    return this._notifyUser(room, notification, {
      fromCluster: false
    });
  }

  /**
   * Send a "token expired" notification to the target user
   *
   * @param {string} connectionId - User's connection identifier
   * @returns {Promise}
   */
  async notifyTokenExpired (connectionId) {
    await this._dispatch(
      'notify:server',
      [KUZZLE_NOTIFICATION_CHANNEL], // Sending notification on Kuzzle notification channel
      new ServerNotification('TokenExpired', 'Authentication Token Expired'),
      connectionId);
    
    await this.module.hotelClerk.removeUser(connectionId);
  }<commit_msg>change notification channel name<commit_after>/*
 * Kuzzle, a backend software, self-hostable and ready to use
 * to power modern apps
 *
 * Copyright 2015-2020 Kuzzle
 * mailto: support AT kuzzle.io
 * website: http://kuzzle.io
 *
 * 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
 *
 * https://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.
 */

'use strict';

const { difference } = require('lodash');
const Bluebird = require('bluebird');

const kerror = require('../../kerror');
const actionEnum = require('./actionEnum');
const { koncordeTest } = require('../../util/koncordeCompat');
const {
  DocumentNotification,
  ServerNotification,
  UserNotification,
} = require('./notification');

/**
 * Notification are meant to be dispatched on "channels" created when subscribing.
 * But some notification like TokenExpired don't have a specific channel to be received on,
 * so we need a constant channel name to send Kuzzle notification without having to subscribe.
 */
const KUZZLE_NOTIFICATION_CHANNEL = 'kuzzle:notification:server';

/**
 * @typedef {Object} DocumentChanges
 * @property {string}  _id              of the document
 * @property {Object}  [_source]        of the document
 * @property {Object}  [_updatedFields] applied to the document (for updates)
 * @property {boolean} [created]        -- tells if this is a CREATE or REPLACE
 *                                      (for actionEnum.WRITE)
 */

/**
 * @class NotifierController
 */
class NotifierController {
  constructor(realtimeModule) {
    this.module = realtimeModule;
    this.ttl = global.kuzzle.config.limits.subscriptionDocumentTTL;
  }

  async init () {
    /**
     * Low-level document notification method, allocating a new Notification
     * message and dispatching it to a precomputed list of rooms.
     * @param {Array.<string>} rooms - list of koncorde rooms to notify
     * @param {DocumentNotification} notification
     * @param {Object} [opts]
     */
    global.kuzzle.onAsk(
      'core:realtime:document:dispatch',
      (rooms, notification, opts) => {
        return this._notifyDocument(rooms, notification, opts);
      });


    /**
     * Low-level user notification method, allocating a new Notification
     * message and dispatching it to the provided room
     * @param {string} room
     * @param {UserNotification}
     * @param {Object} [opts]
     */
    global.kuzzle.onAsk(
      'core:realtime:user:sendMessage',
      (room, notification, opts) => {
        return this._notifyUser(room, notification, opts);
      });

    /**
     * Notify about document creations, updates, replacements or deletions
     * @param {Request} request
     * @param {notifyActionEnum} action applied to documents
     * @param {Array.<DocumentChanges>} docs
     */
    global.kuzzle.onAsk(
      'core:realtime:document:mNotify',
      (request, action, docs) => this.notifyDocuments(request, action, docs));

    /**
     * Notify about document creations, updates, replacements or deletions
     * @param {Request} request
     * @param {notifyActionEnum} action applied to documents
     * @param {DocumentChanges} doc
     */
    global.kuzzle.onAsk(
      'core:realtime:document:notify',
      (request, action, doc) => this.notifyDocuments(request, action, [doc]));


    /**
     * Send a "token expired" notification to the target user
     * @param {string} connectionId
     */
    global.kuzzle.onAsk(
      'core:realtime:tokenExpired:notify',
      connectionId => this.notifyTokenExpired(connectionId));

    /**
     * Publish the provided request content to listening subscribers
     * @param {Request} request
     */
    global.kuzzle.onAsk('core:realtime:publish', request => this.publish(request));
  }

  /**
   * Broadcasts a notification about a document change or a
   * real-time message
   *
   * @param {Array} rooms - Subscribed rooms to notify
   * @param {Request} request - Request at the origin of the notification
   * @param {string} scope - 'in' or 'out'
   * @param {object} content - Document or message
   *
   * @returns {Promise}
   */
  async notifyDocument (rooms, request, scope, action, content) {
    if (rooms.length === 0) {
      return;
    }

    const notification = DocumentNotification.fromRequest(
      request,
      scope,
      action,
      content);

    global.kuzzle.emit('core:notify:document', {
      notification,
      rooms,
    });

    await this._notifyDocument(rooms, notification, {
      fromCluster: false
    });
  }

  /**
   * Broadcast a notification about a user entering or leaving
   * the provided room
   *
   * @param {string} room - Room entered or left
   * @param {Request} request - User (un)subscription request
   * @param {string} scope - 'in' or 'out'
   * @param {object} content - Notification additional informations
   *
   * @returns {Promise}
   */
  notifyUser (room, request, scope, content) {
    const notification = UserNotification.fromRequest(request, scope, content);

    global.kuzzle.emit('core:notify:user', {
      notification,
      room,
    });

    return this._notifyUser(room, notification, {
      fromCluster: false
    });
  }

  /**
   * Send a "token expired" notification to the target user
   *
   * @param {string} connectionId - User's connection identifier
   * @returns {Promise}
   */
  async notifyTokenExpired (connectionId) {
    
    await this._dispatch(
      'notify:server',
      [KUZZLE_NOTIFICATION_CHANNEL], // Sending notification on Kuzzle notification channel
      new ServerNotification('TokenExpired', 'Authentication Token Expired'),
      connectionId);
    
    await this.module.hotelClerk.removeUser(connectionId);
  }
{'fraction_non_alphanumeric': 0.10884687781324168, 'fraction_numerical': 0.002127833701612243, 'mean_word_length': 3.1158639272482316, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 19, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7222561', 'n_tokens_mistral': 3442, 'n_tokens_neox': 3288, 'n_words': 1079}