<commit_before>/**
 * core_connector.ts
 *
 * Handles all connection and communication with the uProxy core.
 */
/// <reference path='../../uproxy.ts'/>
/// <reference path='../../third_party/typings/es6-promise/es6-promise.d.ts' />

interface FullfillAndReject {
  fulfill :Function;
  reject :Function;
};

/**
 * This class hides all cross backend-ui communication wiring so that the
 * uProxy UI may speak through this connector as if talking directly to Core.
 *
 * Propagates these messages:
 *    Core --[ UPDATES  ]--> UI
 *    UI   --[ COMMANDS ]--> Core
 */
class CoreConnector implements uProxy.CoreAPI {

  // Global unique promise ID.
  private promiseId_ :number = 1;
  private mapPromiseIdToFulfillAndReject_ :{[id :number] : FullfillAndReject} =
      {};   

  constructor(private browserConnector_ :uProxy.CoreBrowserConnector) {
    this.browserConnector_.onUpdate(uProxy.Update.COMMAND_FULFILLED,
                                    this.handleRequestFulfilled_);
    this.browserConnector_.onUpdate(uProxy.Update.COMMAND_REJECTED,
                                    this.handleRequestRejected_);
  }

  public connected = () => {
    return this.browserConnector_.status.connected;
  }

  public onUpdate = (update :uProxy.Update, handler :Function) => {
    this.browserConnector_.onUpdate(update, handler);
  }

  /**
   * Send a Command from the UI to the Core, as a result of some user
   * interaction.
   */
  public sendCommand = (command :uProxy.Command, data ?:any) => {
    var payload :uProxy.Payload = {
      cmd: 'emit',
      type: command,
      data: data,
      promiseId: 0
    }
    console.log('UI sending Command ' + //uProxy.Command[command],
        JSON.stringify(payload));
    this.browserConnector_.send(payload);
  }

  /**
   * Send a Command from the UI to the Core, as a result of some user
   * interaction.  Command returns a promise that fulfills/rejects upon
   * an ack/reject from the backend.
   */
  public promiseCommand = (command :uProxy.Command, data ?:any)
      : Promise<any> => {
    var promiseId :number = ++(this.promiseId_);
    var payload :uProxy.Payload = {
      cmd: 'emit',
      type: command,
      data: data,
      promiseId: promiseId
    }
    console.log('UI sending Promise Command ' + uProxy.Command[command],
        JSON.stringify(payload));

    // Create a new promise and store its fulfill and reject functions.
    var fulfillFunc :Function;
    var rejectFunc :Function;
    var promise :Promise<any> = new Promise<any>((F, R) => {
      fulfillFunc = F;
      rejectFunc = R;
    });
    // TODO: we may want to periodically remove garbage from this table
    // e.g. if the app restarts, all promises should be removed or reject.
    // Also we may want to reject promises after some timeout.
    this.mapPromiseIdToFulfillAndReject_[promiseId] = {
      fulfill: fulfillFunc,
      reject: rejectFunc
    };

    // Send request to backend.
    this.browserConnector_.send(payload);

    return promise;
  }

  private handleRequestFulfilled_ = (data :any) => {
    var promiseId = data.promiseId;
    console.log('promise command fulfilled ' + promiseId);
    if (this.mapPromiseIdToFulfillAndReject_[promiseId]) {
      if (data.argsForCallback != undefined) {
        this.mapPromiseIdToFulfillAndReject_[promiseId]
            .fulfill(data.argsForCallback);
      }
      this.mapPromiseIdToFulfillAndReject_[promiseId].fulfill();
      delete this.mapPromiseIdToFulfillAndReject_[promiseId];
    } else {
      console.warn('fulfill not found ' + promiseId);
    }
  }

  private handleRequestRejected_ = (data :any) => {
    var promiseId = data.promiseId;
    console.log('promise command rejected ' + promiseId);
    if (this.mapPromiseIdToFulfillAndReject_[promiseId]) {
      if (data.errorForCallback != undefined) {
        this.mapPromiseIdToFulfillAndReject_[promiseId]
            .reject(data.errorForCallback);
      }
      this.mapPromiseIdToFulfillAndReject_[promiseId].reject();
      delete this.mapPromiseIdToFulfillAndReject_[promiseId];
    } else {
      console.warn('reject not found ' + promiseId);
    }
  }

  // --- CoreAPI interface requirements (sending COMMANDS) ---

  reset = () => {
    console.log('Resetting.');
    this.sendCommand(uProxy.Command.RESET, null);
  }

  // TODO: Reconnect this hook, which while we're testing, sends a new instance
  // message anytime we click on the user in the UI.
  sendInstance = (clientId) => {
    this.sendCommand(uProxy.Command.SEND_INSTANCE_HANDSHAKE_MESSAGE, clientId);
  }

  modifyConsent = (command:uProxy.ConsentCommand) => {
    console.log('Modifying consent.', command);
    this.sendCommand(uProxy.Command.MODIFY_CONSENT, command);
  }

  start = (path :InstancePath) : Promise<Net.Endpoint> => {
    var startPromiseId = this.promiseId_ + 1;
    console.log('Starting to proxy through ' + path);
    return this.promiseCommand(uProxy.Command.START_PROXYING, path)
        .then((endpoint:Net.Endpoint) => {
          return Promise.resolve(endpoint);
        });
  }

  stop = () => {
    console.log('Stopping proxy session.');
    this.sendCommand(uProxy.Command.STOP_PROXYING);
  }

  updateDescription = (description :string) => {
    // TODO: determine if novelty check is necessary.
    console.log('Updating description to ' + description);
    this.sendCommand(uProxy.Command.UPDATE_LOCAL_DEVICE_DESCRIPTION,
                     description);
  }

  // TODO: Implement this or remove it.
  // changeOption = (option) => {
  //   console.log('Changing option ' + option);
  //   this.sendCommand(uProxy.Command.CHANGE_OPTION, option);
  // }

  login = (network :string) : Promise<void> => {
    return this.promiseCommand(uProxy.Command.LOGIN, network);
  }

  logout = (network :string) => {
    this.sendCommand(uProxy.Command.LOGOUT, network);
  }
}  // class CoreConnector<commit_msg>Simplified core.start<commit_after>/**
 * core_connector.ts
 *
 * Handles all connection and communication with the uProxy core.
 */
/// <reference path='../../uproxy.ts'/>
/// <reference path='../../third_party/typings/es6-promise/es6-promise.d.ts' />

interface FullfillAndReject {
  fulfill :Function;
  reject :Function;
};

/**
 * This class hides all cross backend-ui communication wiring so that the
 * uProxy UI may speak through this connector as if talking directly to Core.
 *
 * Propagates these messages:
 *    Core --[ UPDATES  ]--> UI
 *    UI   --[ COMMANDS ]--> Core
 */
class CoreConnector implements uProxy.CoreAPI {

  // Global unique promise ID.
  private promiseId_ :number = 1;
  private mapPromiseIdToFulfillAndReject_ :{[id :number] : FullfillAndReject} =
      {};   

  constructor(private browserConnector_ :uProxy.CoreBrowserConnector) {
    this.browserConnector_.onUpdate(uProxy.Update.COMMAND_FULFILLED,
                                    this.handleRequestFulfilled_);
    this.browserConnector_.onUpdate(uProxy.Update.COMMAND_REJECTED,
                                    this.handleRequestRejected_);
  }

  public connected = () => {
    return this.browserConnector_.status.connected;
  }

  public onUpdate = (update :uProxy.Update, handler :Function) => {
    this.browserConnector_.onUpdate(update, handler);
  }

  /**
   * Send a Command from the UI to the Core, as a result of some user
   * interaction.
   */
  public sendCommand = (command :uProxy.Command, data ?:any) => {
    var payload :uProxy.Payload = {
      cmd: 'emit',
      type: command,
      data: data,
      promiseId: 0
    }
    console.log('UI sending Command ' + //uProxy.Command[command],
        JSON.stringify(payload));
    this.browserConnector_.send(payload);
  }

  /**
   * Send a Command from the UI to the Core, as a result of some user
   * interaction.  Command returns a promise that fulfills/rejects upon
   * an ack/reject from the backend.
   */
  public promiseCommand = (command :uProxy.Command, data ?:any)
      : Promise<any> => {
    var promiseId :number = ++(this.promiseId_);
    var payload :uProxy.Payload = {
      cmd: 'emit',
      type: command,
      data: data,
      promiseId: promiseId
    }
    console.log('UI sending Promise Command ' + uProxy.Command[command],
        JSON.stringify(payload));

    // Create a new promise and store its fulfill and reject functions.
    var fulfillFunc :Function;
    var rejectFunc :Function;
    var promise :Promise<any> = new Promise<any>((F, R) => {
      fulfillFunc = F;
      rejectFunc = R;
    });
    // TODO: we may want to periodically remove garbage from this table
    // e.g. if the app restarts, all promises should be removed or reject.
    // Also we may want to reject promises after some timeout.
    this.mapPromiseIdToFulfillAndReject_[promiseId] = {
      fulfill: fulfillFunc,
      reject: rejectFunc
    };

    // Send request to backend.
    this.browserConnector_.send(payload);

    return promise;
  }

  private handleRequestFulfilled_ = (data :any) => {
    var promiseId = data.promiseId;
    console.log('promise command fulfilled ' + promiseId);
    if (this.mapPromiseIdToFulfillAndReject_[promiseId]) {
      if (data.argsForCallback != undefined) {
        this.mapPromiseIdToFulfillAndReject_[promiseId]
            .fulfill(data.argsForCallback);
      }
      this.mapPromiseIdToFulfillAndReject_[promiseId].fulfill();
      delete this.mapPromiseIdToFulfillAndReject_[promiseId];
    } else {
      console.warn('fulfill not found ' + promiseId);
    }
  }

  private handleRequestRejected_ = (data :any) => {
    var promiseId = data.promiseId;
    console.log('promise command rejected ' + promiseId);
    if (this.mapPromiseIdToFulfillAndReject_[promiseId]) {
      if (data.errorForCallback != undefined) {
        this.mapPromiseIdToFulfillAndReject_[promiseId]
            .reject(data.errorForCallback);
      }
      this.mapPromiseIdToFulfillAndReject_[promiseId].reject();
      delete this.mapPromiseIdToFulfillAndReject_[promiseId];
    } else {
      console.warn('reject not found ' + promiseId);
    }
  }

  // --- CoreAPI interface requirements (sending COMMANDS) ---

  reset = () => {
    console.log('Resetting.');
    this.sendCommand(uProxy.Command.RESET, null);
  }

  // TODO: Reconnect this hook, which while we're testing, sends a new instance
  // message anytime we click on the user in the UI.
  sendInstance = (clientId) => {
    this.sendCommand(uProxy.Command.SEND_INSTANCE_HANDSHAKE_MESSAGE, clientId);
  }

  modifyConsent = (command:uProxy.ConsentCommand) => {
    console.log('Modifying consent.', command);
    this.sendCommand(uProxy.Command.MODIFY_CONSENT, command);
  }

  start = (path :InstancePath) : Promise<Net.Endpoint> => {
    console.log('Starting to proxy through ' + path);
    return this.promiseCommand(uProxy.Command.START_PROXYING, path);
  }

  stop = () => {
    console.log('Stopping proxy session.');
    this.sendCommand(uProxy.Command.STOP_PROXYING);
  }

  updateDescription = (description :string) => {
    // TODO: determine if novelty check is necessary.
    console.log('Updating description to ' + description);
    this.sendCommand(uProxy.Command.UPDATE_LOCAL_DEVICE_DESCRIPTION,
                     description);
  }

  // TODO: Implement this or remove it.
  // changeOption = (option) => {
  //   console.log('Changing option ' + option);
  //   this.sendCommand(uProxy.Command.CHANGE_OPTION, option);
  // }

  login = (network :string) : Promise<void> => {
    return this.promiseCommand(uProxy.Command.LOGIN, network);
  }

  logout = (network :string) => {
    this.sendCommand(uProxy.Command.LOGOUT, network);
  }
}  // class CoreConnector
{'fraction_non_alphanumeric': 0.11750558323312146, 'fraction_numerical': 0.0007730630475863253, 'mean_word_length': 2.9996564754379937, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 54, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '3617653', 'n_tokens_mistral': 3528, 'n_tokens_neox': 3488, 'n_words': 829}