repo_description: REST Client Extension for Visual Studio Code
repo_topics: graphql http http-client rest rest-api rest-client typescript visual-studio-code vscode vscode-extension
code: import { ExtensionContext, Range, TextDocument, ViewColumn, window } from 'vscode';
  import Logger from '../logger';
  import { IRestClientSettings, RequestSettings, RestClientSettings } from '../models/configurationSettings';
  import { HistoricalHttpRequest, HttpRequest } from '../models/httpRequest';
  import { RequestMetadata } from '../models/requestMetadata';
  import { RequestParserFactory } from '../models/requestParserFactory';
  import { trace } from "../utils/decorator";
  import { HttpClient } from '../utils/httpClient';
  import { RequestState, RequestStatusEntry } from '../utils/requestStatusBarEntry';
  import { RequestVariableCache } from "../utils/requestVariableCache";
  import { Selector } from '../utils/selector';
  import { UserDataManager } from '../utils/userDataManager';
  import { getCurrentTextDocument } from '../utils/workspaceUtility';
  import { HttpResponseTextDocumentView } from '../views/httpResponseTextDocumentView';
  import { HttpResponseWebview } from '../views/httpResponseWebview';
  
  export class RequestController {
      private _requestStatusEntry: RequestStatusEntry;
      private _httpClient: HttpClient;
      private _webview: HttpResponseWebview;
      private _textDocumentView: HttpResponseTextDocumentView;
      private _lastRequestSettingTuple: [HttpRequest, IRestClientSettings];
      private _lastPendingRequest?: HttpRequest;
  
      public constructor(context: ExtensionContext) {
          this._requestStatusEntry = new RequestStatusEntry();
          this._httpClient = new HttpClient();
          this._webview = new HttpResponseWebview(context);
          this._webview.onDidCloseAllWebviewPanels(() => this._requestStatusEntry.update({ state: RequestState.Closed }));
          this._textDocumentView = new HttpResponseTextDocumentView();
      }
  
      @trace('Request')
      public async run(range: Range) {
          const editor = window.activeTextEditor;
          const document = getCurrentTextDocument();
          if (!editor || !document) {
              return;
          }
  
          const selectedRequest = await Selector.getRequest(editor, range);
          if (!selectedRequest) {
              return;
          }
  
          const { text, metadatas } = selectedRequest;
          const name = metadatas.get(RequestMetadata.Name);
  
          if (metadatas.has(RequestMetadata.Note)) {
              const note = name ? `Are you sure you want to send the request "${name}"?` : 'Are you sure you want to send this request?';
              const userConfirmed = await window.showWarningMessage(note, 'Yes', 'No');
              if (userConfirmed !== 'Yes') {
                  return;
              }
          }
  
          const requestSettings = new RequestSettings(metadatas);
          const settings: IRestClientSettings = new RestClientSettings(requestSettings);
  
          // parse http request
          const httpRequest = await RequestParserFactory.createRequestParser(text, settings).parseHttpRequest(name);
  
          await this.runCore(httpRequest, settings, document);
      }
  
      @trace('Rerun Request')
      public async rerun() {
          if (!this._lastRequestSettingTuple) {
              return;
          }
  
          const [request, settings] = this._lastRequestSettingTuple;
  
          // TODO: recover from last request settings
          await this.runCore(request, settings);
      }
  
      @trace('Cancel Request')
      public async cancel() {
          this._lastPendingRequest?.cancel();
  
          this._requestStatusEntry.update({ state: RequestState.Cancelled });
      }
      public async clearCookies() {
          try {
              await this._httpClient.clearCookies();
          } catch (error) {
              window.showErrorMessage(`Error clearing cookies:${error?.message}`);
          }
      }
  
      private async runCore(httpRequest: HttpRequest, settings: IRestClientSettings, document?: TextDocument) {
          // clear status bar
          this._requestStatusEntry.update({ state: RequestState.Pending });
  
          // set last request and last pending request
          this._lastPendingRequest = httpRequest;
          this._lastRequestSettingTuple = [httpRequest, settings];
  
          // set http request
          try {
              const response = await this._httpClient.send(httpRequest, settings);
  
              // check cancel
              if (httpRequest.isCancelled) {
                  return;
              }
  
              this._requestStatusEntry.update({ state: RequestState.Received, response });
  
              if (httpRequest.name && document) {
                  RequestVariableCache.add(document, httpRequest.name, response);
              }
  
              try {
                  const activeColumn = window.activeTextEditor!.viewColumn;
                  const previewColumn = settings.previewColumn === ViewColumn.Active
                      ? activeColumn
                      : ((activeColumn as number) + 1) as ViewColumn;
                  if (settings.previewResponseInUntitledDocument) {
                      this._textDocumentView.render(response, previewColumn);
                  } else if (previewColumn) {
                      this._webview.render(response, previewColumn);
                  }
              } catch (reason) {
                  Logger.error('Unable to preview response:', reason);
                  window.showErrorMessage(reason);
              }
  
              // persist to history json file
              await UserDataManager.addToRequestHistory(HistoricalHttpRequest.convertFromHttpRequest(httpRequest));
          } catch (error) {
              // check cancel
              if (httpRequest.isCancelled) {
                  return;
              }
  
              if (error.code === 'ETIMEDOUT') {
                  error.message = `Request timed out. Double-check your network connection and/or raise the timeout duration (currently set to ${settings.timeoutInMilliseconds}ms) as needed: 'rest-client.timeoutinmilliseconds'. Details: ${error}.`;
              } else if (error.code === 'ECONNREFUSED') {
                  error.message = `The connection was rejected. Either the requested service isn’t running on the requested server/port, the proxy settings in vscode are misconfigured, or a firewall is blocking requests. Details: ${error}.`;
              } else if (error.code === 'ENETUNREACH') {
                  error.message = `You don't seem to be connected to a network. Details: ${error}`;
              }
              this._requestStatusEntry.update({ state: RequestState.Error });
              Logger.error('Failed to send request:', error);
              window.showErrorMessage(error.message);
          } finally {
              if (this._lastPendingRequest === httpRequest) {
                  this._lastPendingRequest = undefined;
              }
          }
      }
  
      public dispose() {
          this._requestStatusEntry.dispose();
          this._webview.dispose();
      }
  }