<commit_before>/* global io */
/*jshint camelcase: false */
import DS from 'ember-data';
import Ember from 'ember';
import { requestResponseLogger } from './../initializers/socket-request-response-logger';

const {
  get,
  set,
  EnumerableUtils: { forEach },
  computed,
  Logger: { debug }
  } = Ember;

function printRequestStack(requestHash) {
  const e = new Error();
  if (!e.stack) {
    return; // phantomjs for
  }
  const stack = e.stack.replace(/^[^\(]+?[\n$]/gm, '')
    .replace(/^\s+at\s+/gm, '')
    .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@')
    .split('\n');
  stack.shift();
  stack.unshift('\n');
  debug(requestHash, stack.join('\n'));
}

export default DS.RESTAdapter.extend({
  socketAddress: 'http://api.collectrium.websocket:5000',
  socketHandshakeQuery: '',
  bulkOperationsSupport: {
    createRecord: false,
    updateRecord: false,
    deleteRecord: false
  },
  updateAsPatch: true,
  logRequests: true,
  collectRequestResponseLog: true,
  socketConnections: computed(function() {
    return Ember.Object.create();
  }),
  requestsPool: computed(function() {
    return [];
  }),

  /**
   * generate unique request id
   * @returns {string}
   */
  generateRequestId() {
    const S4 = function() {
      return Math.floor(Math.random() * 0x10000).toString(16); // 65536
    };

    return (
      S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4()
    );
  },
  /**
   *
   * @param type
   * @param options
   * @returns {Ember.get|*|Object}
   */
  getConnection(type, options) {
    /*jshint -W004 */
    var address = get(this, 'socketAddress') + '/';
    var requestsPool = get(this, 'requestsPool');
    type = type.modelName;
    var connections = get(this, 'socketConnections');
    var socketNS = type && get(connections, type);
    var onConnectFailed = this.onConnectFailed;
    var onError = this.onError;
    var adapter = this;
    const store = adapter.store;
    const collectRequestResponseLog = get(this, 'collectRequestResponseLog');

    if (arguments.length === 1) {
      options = {};
    }
    if (!type) {
      options = arguments[0];
    }
    if (socketNS) {
      // TODO: not sure that manually socket reconnecting is required
      if (socketNS.hasOwnProperty('socket') && !socketNS.socket.connected && !socketNS.socket.connecting) {
        socketNS.socket.connect();
      }
    } else {
      if (type) {
        address = `${address}${this.pathForType(type)}/`;
      }
      socketNS = io.connect(address, options);
      if (type) {
        socketNS.on('message', function(response) {
          if (response.hasOwnProperty('errors')) {
            if (response.request_id && requestsPool[response.request_id]) {
              var rejecter = requestsPool[response.request_id].reject;
              delete response.request_id;
              delete requestsPool[response.request_id];
              Ember.run(null, rejecter, response);
            }
          } else {
            if (response.request_id && requestsPool[response.request_id]) {
              var resolver = requestsPool[response.request_id].resolve;
              if (collectRequestResponseLog) {
                requestResponseLogger.logResponse(response);
              }
              delete response.request_id;
              delete requestsPool[response.request_id];
              Ember.run(null, resolver, response);
            } else {
              /**
               * Handling PUSH notifications
               * Operations can be only multiple
               */
              store.trigger('notification', response);
              // if response contains only ids array it means that we receive DELETE
              if (response.ids) {
                // remove all records from store without sending DELETE requests
                forEach(response.ids, function(id) {
                  const record = store.getById(type, id);
                  if (record) {
                    store.unloadRecord(record);
                  }
                });
              }
              // we receive CREATE or UPDATE, ember-data will manage data itself
              else {
                if (response.hasOwnProperty('payload')) {
                  store.pushPayload(type, response.payload);
                }
              }
            }
          }
        });
        set(connections, type, socketNS);
      } else {
        socketNS.on('connect_failed', function(response) {
          if (onConnectFailed) {
            onConnectFailed.call(adapter, response);
          }
        });
        socketNS.on('error', function(response) {
          if (onError) {
            onError.call(adapter, response);
          }
        });
      }
    }
    return socketNS;
  },

  /**
   *
   * @param type
   * @param requestType
   * @param hash
   * @returns {Ember.RSVP.Promise}
   */
  send: function(type, requestType, hash) {
    const connection = this.getConnection(type);
    const requestsPool = get(this, 'requestsPool');
    const requestId = this.generateRequestId();
    const modelName = type.modelName;
    const deffered = Ember.RSVP.defer('DS: SocketAdapter#emit ' + requestType + ' to ' + modelName);
    const logRequests = get(this, 'logRequests');
    const collectRequestResponseLog = get(this, 'collectRequestResponseLog');
    if (!(hash instanceof Object)) {
      hash = {};
    }
    deffered.requestType = requestType;
    hash.request_id = requestId;
    if (collectRequestResponseLog) {
      requestResponseLogger.logRequest({
        modelName,
        operation: requestType,
        hash
      });
    }
    requestsPool[requestId] = deffered;
    if (logRequests) {
      printRequestStack(hash);
    }
    connection.emit(requestType, hash);
    return deffered.promise;
  },

  /**
   * Fetching all resources of a given type from server, can't be called with params.
   * Returns resources without full relations.
   * @param store
   * @param type
   * @returns {Ember.RSVP.Promise}
   */
  findAll: function(store, type) {
    return this.send(type, 'READ_LIST');
  },

  /**
   *
   * @param store
   * @param type<commit_msg>Split send into separate methods<commit_after>/* global io */
/*jshint camelcase: false */
import DS from 'ember-data';
import Ember from 'ember';
import { requestResponseLogger } from './../initializers/socket-request-response-logger';

const {
  get,
  set,
  EnumerableUtils: { forEach },
  computed,
  Logger: { debug }
  } = Ember;

function printRequestStack(requestHash) {
  const e = new Error();
  if (!e.stack) {
    return; // phantomjs for
  }
  const stack = e.stack.replace(/^[^\(]+?[\n$]/gm, '')
    .replace(/^\s+at\s+/gm, '')
    .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@')
    .split('\n');
  stack.shift();
  stack.unshift('\n');
  debug(requestHash, stack.join('\n'));
}

export default DS.RESTAdapter.extend({
  socketAddress: 'http://api.collectrium.websocket:5000',
  socketHandshakeQuery: '',
  bulkOperationsSupport: {
    createRecord: false,
    updateRecord: false,
    deleteRecord: false
  },
  updateAsPatch: true,
  logRequests: true,
  collectRequestResponseLog: true,
  socketConnections: computed(function() {
    return Ember.Object.create();
  }),
  requestsPool: computed(function() {
    return [];
  }),

  /**
   * generate unique request id
   * @returns {string}
   */
  generateRequestId() {
    const S4 = function() {
      return Math.floor(Math.random() * 0x10000).toString(16); // 65536
    };

    return (
      S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4()
    );
  },
  /**
   *
   * @param type
   * @param options
   * @returns {Ember.get|*|Object}
   */
  getConnection(type, options) {
    /*jshint -W004 */
    var address = get(this, 'socketAddress') + '/';
    var requestsPool = get(this, 'requestsPool');
    type = type.modelName;
    var connections = get(this, 'socketConnections');
    var socketNS = type && get(connections, type);
    var onConnectFailed = this.onConnectFailed;
    var onError = this.onError;
    var adapter = this;
    const store = adapter.store;
    const collectRequestResponseLog = get(this, 'collectRequestResponseLog');

    if (arguments.length === 1) {
      options = {};
    }
    if (!type) {
      options = arguments[0];
    }
    if (socketNS) {
      // TODO: not sure that manually socket reconnecting is required
      if (socketNS.hasOwnProperty('socket') && !socketNS.socket.connected && !socketNS.socket.connecting) {
        socketNS.socket.connect();
      }
    } else {
      if (type) {
        address = `${address}${this.pathForType(type)}/`;
      }
      socketNS = io.connect(address, options);
      if (type) {
        socketNS.on('message', function(response) {
          if (response.hasOwnProperty('errors')) {
            if (response.request_id && requestsPool[response.request_id]) {
              var rejecter = requestsPool[response.request_id].reject;
              delete response.request_id;
              delete requestsPool[response.request_id];
              Ember.run(null, rejecter, response);
            }
          } else {
            if (response.request_id && requestsPool[response.request_id]) {
              var resolver = requestsPool[response.request_id].resolve;
              if (collectRequestResponseLog) {
                requestResponseLogger.logResponse(response);
              }
              delete response.request_id;
              delete requestsPool[response.request_id];
              Ember.run(null, resolver, response);
            } else {
              /**
               * Handling PUSH notifications
               * Operations can be only multiple
               */
              store.trigger('notification', response);
              // if response contains only ids array it means that we receive DELETE
              if (response.ids) {
                // remove all records from store without sending DELETE requests
                forEach(response.ids, function(id) {
                  const record = store.getById(type, id);
                  if (record) {
                    store.unloadRecord(record);
                  }
                });
              }
              // we receive CREATE or UPDATE, ember-data will manage data itself
              else {
                if (response.hasOwnProperty('payload')) {
                  store.pushPayload(type, response.payload);
                }
              }
            }
          }
        });
        set(connections, type, socketNS);
      } else {
        socketNS.on('connect_failed', function(response) {
          if (onConnectFailed) {
            onConnectFailed.call(adapter, response);
          }
        });
        socketNS.on('error', function(response) {
          if (onError) {
            onError.call(adapter, response);
          }
        });
      }
    }
    return socketNS;
  },

  /**
   *
   * @param type
   * @param requestType
   * @param hash
   * @returns {Ember.RSVP.Promise}
   */
  send: function(type, requestType, hash) {
    hash = this.buildRequest(type, requestType, hash);
    this.onBeforeSendHash(hash);
    const connection = this.getConnection(type);
    const requestsPool = get(this, 'requestsPool');
    const logRequests = get(this, 'logRequests');
    const modelName = type.modelName;
    const collectRequestResponseLog = get(this, 'collectRequestResponseLog');
    const deffered = Ember.RSVP.defer('DS: SocketAdapter#emit ' + requestType + ' to ' + modelName);
    deffered.requestType = requestType;
    if (collectRequestResponseLog) {
      requestResponseLogger.logRequest({
        modelName,
        operation: requestType,
        hash
      });
    }
    requestsPool[hash.request_id] = deffered;
    if (logRequests) {
      printRequestStack(hash);
    }
    connection.emit(requestType, hash);
    return deffered.promise;
  },

  buildRequest(type, requestType, hash) {
    const requestId = this.generateRequestId();
    if (!(hash instanceof Object)) {
      hash = {};
    }
    hash.request_id = requestId;
    return hash;
  },

  onBeforeSendHash(/*hash*/) {

  },

  /**
   * Fetching all resources of a given type from server, can't be called with params.
   * Returns resources without full relations.
   * @param store
   * @param type
   * @returns {Ember.RSVP.Promise}
   */
  findAll: function(store, type) {
    return this.send(type, 'READ_LIST');
  },

  /**
   *
   * @param store
   * @param type
{'fraction_non_alphanumeric': 0.13249640172717095, 'fraction_numerical': 0.004957620342235727, 'mean_word_length': 2.2209631728045327, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '480824', 'n_tokens_mistral': 3672, 'n_tokens_neox': 3584, 'n_words': 788}