index.vue 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023
  1. <template>
  2. <div class="page">
  3. <div class="content">
  4. <div class="page-columns inner-left">
  5. <AppSection class="blue" :label="$t('request')" ref="request" no-legend>
  6. <ul>
  7. <li class="shrink">
  8. <label for="method">{{ $t("method") }}</label>
  9. <span class="select-wrapper">
  10. <v-popover>
  11. <input
  12. id="method"
  13. class="method"
  14. v-model="method"
  15. :readonly="!customMethod"
  16. autofocus
  17. />
  18. <template slot="popover">
  19. <div
  20. v-for="(methodMenuItem, index) in methodMenuItems"
  21. :key="`method-${index}`"
  22. >
  23. <button
  24. class="icon"
  25. @click="
  26. customMethod = methodMenuItem == 'CUSTOM' ? true : false
  27. method = methodMenuItem
  28. "
  29. v-close-popover
  30. >
  31. {{ methodMenuItem }}
  32. </button>
  33. </div>
  34. </template>
  35. </v-popover>
  36. </span>
  37. </li>
  38. <li>
  39. <label for="url">{{ $t("url") }}</label>
  40. <input
  41. v-if="!this.$store.state.postwoman.settings.EXPERIMENTAL_URL_BAR_ENABLED"
  42. :class="{ error: !isValidURL }"
  43. class="border-dashed md:border-l border-brdColor"
  44. @keyup.enter="isValidURL ? sendRequest() : null"
  45. id="url"
  46. name="url"
  47. type="text"
  48. v-model="uri"
  49. spellcheck="false"
  50. @input="pathInputHandler"
  51. :placeholder="$t('url')"
  52. />
  53. <SmartUrlField v-model="uri" v-else />
  54. </li>
  55. <li class="shrink">
  56. <label class="hide-on-small-screen" for="send">&nbsp;</label>
  57. <button
  58. v-if="!runningRequest"
  59. :disabled="!isValidURL"
  60. @click="sendRequest"
  61. id="send"
  62. ref="sendButton"
  63. >
  64. {{ $t("send") }}
  65. <span>
  66. <i class="material-icons">send</i>
  67. </span>
  68. </button>
  69. <button v-else @click="cancelRequest" id="send" ref="sendButton">
  70. {{ $t("cancel") }}
  71. <span>
  72. <i class="material-icons">clear</i>
  73. </span>
  74. </button>
  75. </li>
  76. </ul>
  77. <ul>
  78. <li>
  79. <label for="name" class="text-sm">{{ $t("token_req_name") }}</label>
  80. <input id="name" name="name" type="text" v-model="name" class="text-sm" />
  81. </li>
  82. </ul>
  83. <div label="Request Body" v-if="['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)">
  84. <ul>
  85. <li>
  86. <label for="contentType" class="text-sm">{{ $t("content_type") }}</label>
  87. <SmartAutoComplete
  88. :source="validContentTypes"
  89. :spellcheck="false"
  90. v-model="contentType"
  91. styles="text-sm"
  92. />
  93. </li>
  94. </ul>
  95. <ul>
  96. <li>
  97. <div class="row-wrapper">
  98. <span>
  99. <SmartToggle
  100. v-if="canListParameters"
  101. :on="rawInput"
  102. @change="rawInput = $event"
  103. >
  104. {{ $t("raw_input") }}
  105. </SmartToggle>
  106. </span>
  107. </div>
  108. </li>
  109. </ul>
  110. <HttpBodyParameters
  111. v-if="!rawInput"
  112. :bodyParams="bodyParams"
  113. @clear-content="clearContent"
  114. @set-route-query-state="setRouteQueryState"
  115. @remove-request-body-param="removeRequestBodyParam"
  116. @add-request-body-param="addRequestBodyParam"
  117. />
  118. <HttpRawBody
  119. v-else
  120. :rawParams="rawParams"
  121. :contentType="contentType"
  122. :rawInput="rawInput"
  123. @clear-content="clearContent"
  124. @update-raw-body="updateRawBody"
  125. @update-raw-input="updateRawInput = (value) => (rawInput = value)"
  126. />
  127. </div>
  128. <div class="row-wrapper">
  129. <span>
  130. <button
  131. class="icon"
  132. @click="showCurlImportModal = !showCurlImportModal"
  133. v-tooltip.bottom="$t('import_curl')"
  134. >
  135. <i class="material-icons">import_export</i>
  136. </button>
  137. <button
  138. class="icon"
  139. @click="showCodegenModal = !showCodegenModal"
  140. :disabled="!isValidURL"
  141. v-tooltip.bottom="$t('show_code')"
  142. >
  143. <i class="material-icons">code</i>
  144. </button>
  145. </span>
  146. <span>
  147. <button
  148. class="icon"
  149. @click="copyRequest"
  150. ref="copyRequest"
  151. :disabled="!isValidURL"
  152. v-tooltip.bottom="$t('copy_request_link')"
  153. >
  154. <i v-if="navigatorShare" class="material-icons">share</i>
  155. <i v-else class="material-icons">content_copy</i>
  156. </button>
  157. <button
  158. class="icon"
  159. @click="saveRequest"
  160. ref="saveRequest"
  161. :disabled="!isValidURL"
  162. v-tooltip.bottom="$t('save_to_collections')"
  163. >
  164. <i class="material-icons">create_new_folder</i>
  165. </button>
  166. <button
  167. class="icon"
  168. @click="clearContent('', $event)"
  169. v-tooltip.bottom="$t('clear_all')"
  170. ref="clearAll"
  171. >
  172. <i class="material-icons">clear_all</i>
  173. </button>
  174. </span>
  175. </div>
  176. </AppSection>
  177. <section id="options">
  178. <SmartTabs>
  179. <SmartTab
  180. :id="'params'"
  181. :label="
  182. $t('parameters') + `${params.length !== 0 ? ' \xA0 • \xA0 ' + params.length : ''}`
  183. "
  184. :selected="true"
  185. >
  186. <HttpParameters
  187. :params="params"
  188. @clear-content="clearContent"
  189. @remove-request-param="removeRequestParam"
  190. @add-request-param="addRequestParam"
  191. />
  192. </SmartTab>
  193. <SmartTab
  194. :id="'headers'"
  195. :label="
  196. $t('headers') + `${headers.length !== 0 ? ' \xA0 • \xA0 ' + headers.length : ''}`
  197. "
  198. >
  199. <HttpHeaders
  200. :headers="headers"
  201. @clear-content="clearContent"
  202. @set-route-query-state="setRouteQueryState"
  203. @remove-request-header="removeRequestHeader"
  204. @add-request-header="addRequestHeader"
  205. />
  206. </SmartTab>
  207. <SmartTab :id="'authentication'" :label="$t('authentication')">
  208. <AppSection class="teal" :label="$t('authentication')" ref="authentication" no-legend>
  209. <ul>
  210. <li>
  211. <div class="row-wrapper">
  212. <label for="auth">{{ $t("authentication") }}</label>
  213. <div>
  214. <button
  215. class="icon"
  216. @click="clearContent('auth', $event)"
  217. v-tooltip.bottom="$t('clear')"
  218. >
  219. <i class="material-icons">clear_all</i>
  220. </button>
  221. </div>
  222. </div>
  223. <span class="select-wrapper">
  224. <select id="auth" v-model="auth">
  225. <option>None</option>
  226. <option>Basic Auth</option>
  227. <option>Bearer Token</option>
  228. <option>OAuth 2.0</option>
  229. </select>
  230. </span>
  231. </li>
  232. </ul>
  233. <ul v-if="auth === 'Basic Auth'">
  234. <li>
  235. <input placeholder="User" name="http_basic_user" v-model="httpUser" />
  236. </li>
  237. <li>
  238. <input
  239. placeholder="Password"
  240. name="http_basic_passwd"
  241. :type="passwordFieldType"
  242. v-model="httpPassword"
  243. />
  244. </li>
  245. <div>
  246. <li>
  247. <button class="icon" ref="switchVisibility" @click="switchVisibility">
  248. <i class="material-icons" v-if="passwordFieldType === 'text'">visibility</i>
  249. <i class="material-icons" v-if="passwordFieldType !== 'text'"
  250. >visibility_off</i
  251. >
  252. </button>
  253. </li>
  254. </div>
  255. </ul>
  256. <ul v-if="auth === 'Bearer Token' || auth === 'OAuth 2.0'">
  257. <li>
  258. <div class="row-wrapper">
  259. <input placeholder="Token" name="bearer_token" v-model="bearerToken" />
  260. <button
  261. v-if="auth === 'OAuth 2.0'"
  262. class="icon"
  263. @click="showTokenListModal = !showTokenListModal"
  264. v-tooltip.bottom="$t('use_token')"
  265. >
  266. <i class="material-icons">open_in_new</i>
  267. </button>
  268. <button
  269. v-if="auth === 'OAuth 2.0'"
  270. class="icon"
  271. @click="showTokenRequest = !showTokenRequest"
  272. v-tooltip.bottom="$t('get_token')"
  273. >
  274. <i class="material-icons">vpn_key</i>
  275. </button>
  276. </div>
  277. </li>
  278. </ul>
  279. <div class="row-wrapper">
  280. <SmartToggle :on="!urlExcludes.auth" @change="setExclude('auth', !$event)">
  281. {{ $t("include_in_url") }}
  282. </SmartToggle>
  283. </div>
  284. </AppSection>
  285. <AppSection
  286. v-if="showTokenRequest"
  287. class="red"
  288. label="Access Token Request"
  289. ref="accessTokenRequest"
  290. >
  291. <ul>
  292. <li>
  293. <div class="row-wrapper">
  294. <label for="token-name">{{ $t("token_name") }}</label>
  295. <div>
  296. <button
  297. class="icon"
  298. @click="showTokenRequestList = true"
  299. v-tooltip.bottom="$t('manage_token_req')"
  300. >
  301. <i class="material-icons">library_add</i>
  302. </button>
  303. <button
  304. class="icon"
  305. @click="clearContent('access_token', $event)"
  306. v-tooltip.bottom="$t('clear')"
  307. >
  308. <i class="material-icons">clear_all</i>
  309. </button>
  310. <button
  311. class="icon"
  312. @click="showTokenRequest = false"
  313. v-tooltip.bottom="$t('close')"
  314. >
  315. <i class="material-icons">close</i>
  316. </button>
  317. </div>
  318. </div>
  319. <input
  320. id="token-name"
  321. :placeholder="$t('optional')"
  322. name="token_name"
  323. v-model="accessTokenName"
  324. type="text"
  325. />
  326. </li>
  327. </ul>
  328. <ul>
  329. <li>
  330. <label for="oidc-discovery-url">
  331. {{ $t("oidc_discovery_url") }}
  332. </label>
  333. <input
  334. :disabled="this.authUrl !== '' || this.accessTokenUrl !== ''"
  335. id="oidc-discovery-url"
  336. name="oidc_discovery_url"
  337. type="url"
  338. v-model="oidcDiscoveryUrl"
  339. placeholder="https://example.com/.well-known/openid-configuration"
  340. />
  341. </li>
  342. </ul>
  343. <ul>
  344. <li>
  345. <label for="auth-url">{{ $t("auth_url") }}</label>
  346. <input
  347. :disabled="this.oidcDiscoveryUrl !== ''"
  348. id="auth-url"
  349. name="auth_url"
  350. type="url"
  351. v-model="authUrl"
  352. placeholder="https://example.com/login/oauth/authorize"
  353. />
  354. </li>
  355. </ul>
  356. <ul>
  357. <li>
  358. <label for="access-token-url">
  359. {{ $t("access_token_url") }}
  360. </label>
  361. <input
  362. :disabled="this.oidcDiscoveryUrl !== ''"
  363. id="access-token-url"
  364. name="access_token_url"
  365. type="url"
  366. v-model="accessTokenUrl"
  367. placeholder="https://example.com/login/oauth/access_token"
  368. />
  369. </li>
  370. </ul>
  371. <ul>
  372. <li>
  373. <label for="client-id">{{ $t("client_id") }}</label>
  374. <input
  375. id="client-id"
  376. name="client_id"
  377. type="text"
  378. v-model="clientId"
  379. placeholder="Client ID"
  380. />
  381. </li>
  382. </ul>
  383. <ul>
  384. <li>
  385. <label for="scope">{{ $t("scope") }}</label>
  386. <input
  387. id="scope"
  388. name="scope"
  389. type="text"
  390. v-model="scope"
  391. placeholder="e.g. read:org"
  392. />
  393. </li>
  394. </ul>
  395. <ul>
  396. <li>
  397. <button class="icon" @click="handleAccessTokenRequest">
  398. <i class="material-icons">vpn_key</i>
  399. <span>{{ $t("request_token") }}</span>
  400. </button>
  401. </li>
  402. </ul>
  403. </AppSection>
  404. </SmartTab>
  405. <SmartTab :id="'pre_request_script'" :label="$t('pre_request_script')">
  406. <AppSection
  407. v-if="showPreRequestScript"
  408. class="orange"
  409. :label="$t('pre_request_script')"
  410. ref="preRequest"
  411. no-legend
  412. >
  413. <ul>
  414. <li>
  415. <div class="row-wrapper">
  416. <label>{{ $t("javascript_code") }}</label>
  417. <div>
  418. <a
  419. href="https://github.com/hoppscotch/hoppscotch/wiki/Pre-Request-Scripts"
  420. target="_blank"
  421. rel="noopener"
  422. >
  423. <button class="icon" v-tooltip="$t('wiki')">
  424. <i class="material-icons">help_outline</i>
  425. </button>
  426. </a>
  427. </div>
  428. </div>
  429. <SmartJsEditor
  430. v-model="preRequestScript"
  431. :options="{
  432. maxLines: '16',
  433. minLines: '8',
  434. fontSize: '16px',
  435. autoScrollEditorIntoView: true,
  436. showPrintMargin: false,
  437. useWorker: false,
  438. }"
  439. styles="rounded-b-lg"
  440. completeMode="pre"
  441. />
  442. </li>
  443. </ul>
  444. </AppSection>
  445. </SmartTab>
  446. <SmartTab :id="'tests'" :label="$t('tests')">
  447. <AppSection
  448. v-if="testsEnabled"
  449. class="orange"
  450. :label="$t('tests')"
  451. ref="postRequestTests"
  452. no-legend
  453. >
  454. <ul>
  455. <li>
  456. <div class="row-wrapper">
  457. <label>{{ $t("javascript_code") }}</label>
  458. <div>
  459. <a
  460. href="https://github.com/hoppscotch/hoppscotch/wiki/Post-Request-Tests"
  461. target="_blank"
  462. rel="noopener"
  463. >
  464. <button class="icon" v-tooltip="$t('wiki')">
  465. <i class="material-icons">help_outline</i>
  466. </button>
  467. </a>
  468. </div>
  469. </div>
  470. <SmartJsEditor
  471. v-model="testScript"
  472. :options="{
  473. maxLines: '16',
  474. minLines: '8',
  475. fontSize: '16px',
  476. autoScrollEditorIntoView: true,
  477. showPrintMargin: false,
  478. useWorker: false,
  479. }"
  480. styles="rounded-b-lg"
  481. completeMode="test"
  482. />
  483. <div v-if="testReports.length !== 0">
  484. <div class="row-wrapper">
  485. <label>Test Reports</label>
  486. <div>
  487. <button
  488. class="icon"
  489. @click="clearContent('tests', $event)"
  490. v-tooltip.bottom="$t('clear')"
  491. >
  492. <i class="material-icons">clear_all</i>
  493. </button>
  494. </div>
  495. </div>
  496. <div v-for="(testReport, index) in testReports" :key="index">
  497. <div v-if="testReport.startBlock" class="info">
  498. <hr />
  499. <h4>{{ testReport.startBlock }}</h4>
  500. </div>
  501. <p v-else-if="testReport.result" class="row-wrapper info">
  502. <span :class="testReport.styles.class">
  503. <i class="material-icons">
  504. {{ testReport.styles.icon }}
  505. </i>
  506. <span>&nbsp; {{ testReport.result }}</span>
  507. <span v-if="testReport.message">
  508. <label>&nbsp; • &nbsp; {{ testReport.message }}</label>
  509. </span>
  510. </span>
  511. </p>
  512. <div v-else-if="testReport.endBlock"><hr /></div>
  513. </div>
  514. </div>
  515. </li>
  516. </ul>
  517. </AppSection>
  518. </SmartTab>
  519. </SmartTabs>
  520. </section>
  521. <HttpResponse :response="response" :active="runningRequest" ref="response" />
  522. </div>
  523. <aside v-if="activeSidebar" class="sticky-inner inner-right lg:max-w-md">
  524. <section>
  525. <SmartTabs>
  526. <SmartTab :id="'history'" :label="$t('history')" :selected="true">
  527. <History :page="'rest'" @useHistory="handleUseHistory" ref="historyComponent" />
  528. </SmartTab>
  529. <SmartTab :id="'collections'" :label="$t('collections')">
  530. <Collections />
  531. </SmartTab>
  532. <SmartTab :id="'env'" :label="$t('environments')">
  533. <Environments @use-environment="useSelectedEnvironment($event)" />
  534. </SmartTab>
  535. <SmartTab :id="'notes'" :label="$t('notes')">
  536. <HttpNotes />
  537. </SmartTab>
  538. </SmartTabs>
  539. </section>
  540. </aside>
  541. </div>
  542. <CollectionsSaveRequest
  543. :show="showSaveRequestModal"
  544. @hide-modal="hideRequestModal"
  545. :editing-request="editRequest"
  546. />
  547. <HttpImportCurl
  548. :show="showCurlImportModal"
  549. @hide-modal="showCurlImportModal = false"
  550. @handle-import="handleImport"
  551. />
  552. <HttpCodegenModal
  553. :show="showCodegenModal"
  554. :requestTypeProp="requestType"
  555. :requestCode="requestCode"
  556. @hide-modal="showCodegenModal = false"
  557. @set-request-type="setRequestType"
  558. />
  559. <HttpTokenList
  560. :show="showTokenListModal"
  561. :tokens="tokens"
  562. @clear-content="clearContent"
  563. @use-oauth-token="useOAuthToken"
  564. @remove-oauth-token="removeOAuthToken"
  565. @hide-modal="showTokenListModal = false"
  566. />
  567. <SmartModal v-if="showTokenRequestList" @close="showTokenRequestList = false">
  568. <div slot="header">
  569. <div class="row-wrapper">
  570. <h3 class="title">{{ $t("manage_token_req") }}</h3>
  571. <div>
  572. <button class="icon" @click="showTokenRequestList = false">
  573. <i class="material-icons">close</i>
  574. </button>
  575. </div>
  576. </div>
  577. </div>
  578. <div slot="body" class="flex flex-col">
  579. <div class="row-wrapper">
  580. <label for="token-req-list">{{ $t("token_req_list") }}</label>
  581. <div>
  582. <button
  583. :disabled="this.tokenReqs.length === 0"
  584. class="icon"
  585. @click="showTokenRequestList = false"
  586. v-tooltip.bottom="$t('use_token_req')"
  587. >
  588. <i class="material-icons">input</i>
  589. </button>
  590. <button
  591. :disabled="this.tokenReqs.length === 0"
  592. class="icon"
  593. @click="removeOAuthTokenReq"
  594. v-tooltip.bottom="$t('delete')"
  595. >
  596. <i class="material-icons">delete</i>
  597. </button>
  598. </div>
  599. </div>
  600. <ul>
  601. <li>
  602. <span class="select-wrapper">
  603. <select
  604. id="token-req-list"
  605. v-model="tokenReqSelect"
  606. :disabled="this.tokenReqs.length === 0"
  607. @change="tokenReqChange($event)"
  608. >
  609. <option v-for="(req, index) in tokenReqs" :key="index" :value="req.name">
  610. {{ req.name }}
  611. </option>
  612. </select>
  613. </span>
  614. </li>
  615. </ul>
  616. <label for="token-req-name">{{ $t("token_req_name") }}</label>
  617. <input v-model="tokenReqName" />
  618. <label for="token-req-details">
  619. {{ $t("token_req_details") }}
  620. </label>
  621. <textarea id="token-req-details" readonly rows="7" v-model="tokenReqDetails"></textarea>
  622. </div>
  623. <div slot="footer">
  624. <div class="row-wrapper">
  625. <span></span>
  626. <span>
  627. <button class="icon primary" @click="addOAuthTokenReq">
  628. {{ $t("save_token_req") }}
  629. </button>
  630. </span>
  631. </div>
  632. </div>
  633. </SmartModal>
  634. </div>
  635. </template>
  636. <script>
  637. import url from "url"
  638. import querystring from "querystring"
  639. import parseCurlCommand from "~/helpers/curlparser"
  640. import getEnvironmentVariablesFromScript from "~/helpers/preRequest"
  641. import runTestScriptWithVariables from "~/helpers/postwomanTesting"
  642. import parseTemplateString from "~/helpers/templating"
  643. import { tokenRequest, oauthRedirect } from "~/helpers/oauth"
  644. import { cancelRunningRequest, sendNetworkRequest } from "~/helpers/network"
  645. import { fb } from "~/helpers/fb"
  646. import { hasPathParams, addPathParamsToVariables, getQueryParams } from "~/helpers/requestParams"
  647. import { parseUrlAndPath } from "~/helpers/utils/uri"
  648. import { httpValid } from "~/helpers/utils/valid"
  649. import { knownContentTypes, isJSONContentType } from "~/helpers/utils/contenttypes"
  650. import { generateCodeWithGenerator } from "~/helpers/codegen/codegen"
  651. export default {
  652. data() {
  653. return {
  654. showCurlImportModal: false,
  655. showPreRequestScript: true,
  656. testsEnabled: true,
  657. testScript: "// pw.expect('variable').toBe('value');",
  658. preRequestScript: "// pw.env.set('variable', 'value');",
  659. testReports: [],
  660. copyButton: '<i class="material-icons">content_copy</i>',
  661. downloadButton: '<i class="material-icons">save_alt</i>',
  662. doneButton: '<i class="material-icons">done</i>',
  663. showCodegenModal: false,
  664. response: {
  665. status: "",
  666. headers: "",
  667. body: "",
  668. duration: 0,
  669. size: 0,
  670. },
  671. validContentTypes: knownContentTypes,
  672. paramsWatchEnabled: true,
  673. showTokenListModal: false,
  674. showTokenRequest: false,
  675. showTokenRequestList: false,
  676. showSaveRequestModal: false,
  677. editRequest: {},
  678. urlExcludes: {},
  679. activeSidebar: true,
  680. fb,
  681. customMethod: false,
  682. files: [],
  683. filenames: "",
  684. navigatorShare: navigator.share,
  685. runningRequest: false,
  686. settings: {
  687. SCROLL_INTO_ENABLED:
  688. typeof this.$store.state.postwoman.settings.SCROLL_INTO_ENABLED !== "undefined"
  689. ? this.$store.state.postwoman.settings.SCROLL_INTO_ENABLED
  690. : true,
  691. },
  692. currentMethodIndex: 0,
  693. methodMenuItems: [
  694. "GET",
  695. "HEAD",
  696. "POST",
  697. "PUT",
  698. "DELETE",
  699. "CONNECT",
  700. "OPTIONS",
  701. "TRACE",
  702. "PATCH",
  703. "CUSTOM",
  704. ],
  705. }
  706. },
  707. watch: {
  708. urlExcludes: {
  709. deep: true,
  710. handler() {
  711. this.$store.commit("postwoman/applySetting", [
  712. "URL_EXCLUDES",
  713. Object.assign({}, this.urlExcludes),
  714. ])
  715. },
  716. },
  717. canListParameters: {
  718. immediate: true,
  719. handler(canListParameters) {
  720. if (canListParameters) {
  721. this.$nextTick(() => {
  722. this.rawInput = Boolean(this.rawParams && this.rawParams !== "{}")
  723. })
  724. } else {
  725. this.rawInput = true
  726. }
  727. },
  728. },
  729. contentType(contentType, oldContentType) {
  730. const getDefaultParams = (contentType) => {
  731. if (isJSONContentType(contentType)) return "{}"
  732. switch (contentType) {
  733. case "application/xml":
  734. return "<?xml version='1.0' encoding='utf-8'?>"
  735. case "text/html":
  736. return "<!doctype html>"
  737. }
  738. return ""
  739. }
  740. if (!this.rawParams || this.rawParams === getDefaultParams(oldContentType)) {
  741. this.rawParams = getDefaultParams(contentType)
  742. }
  743. this.setRouteQueryState()
  744. },
  745. params: {
  746. handler(newValue) {
  747. if (!this.paramsWatchEnabled) {
  748. this.paramsWatchEnabled = true
  749. return
  750. }
  751. let path = this.path
  752. let queryString = getQueryParams(newValue)
  753. .map(({ key, value }) => `${key}=${value}`)
  754. .join("&")
  755. queryString = queryString === "" ? "" : `?${queryString}`
  756. if (path.includes("?")) {
  757. path = path.slice(0, path.indexOf("?")) + queryString
  758. } else {
  759. path = path + queryString
  760. }
  761. this.path = path
  762. this.setRouteQueryState()
  763. },
  764. deep: true,
  765. },
  766. selectedRequest(newValue, oldValue) {
  767. // @TODO: Convert all variables to single request variable
  768. if (!newValue) return
  769. this.uri = newValue.url + newValue.path
  770. this.url = newValue.url
  771. this.path = newValue.path
  772. this.method = newValue.method
  773. this.auth = newValue.auth
  774. this.httpUser = newValue.httpUser
  775. this.httpPassword = newValue.httpPassword
  776. this.passwordFieldType = newValue.passwordFieldType
  777. this.bearerToken = newValue.bearerToken
  778. this.headers = newValue.headers
  779. this.params = newValue.params
  780. this.bodyParams = newValue.bodyParams
  781. this.rawParams = newValue.rawParams
  782. this.rawInput = newValue.rawInput
  783. this.contentType = newValue.contentType
  784. this.requestType = newValue.requestType
  785. if (newValue.preRequestScript) {
  786. this.showPreRequestScript = true
  787. this.preRequestScript = newValue.preRequestScript
  788. }
  789. if (newValue.testScript) {
  790. this.testsEnabled = true
  791. this.testScript = newValue.testScript
  792. }
  793. this.name = newValue.name
  794. },
  795. editingRequest(newValue) {
  796. this.editRequest = newValue
  797. this.showSaveRequestModal = true
  798. },
  799. method() {
  800. this.contentType = ["POST", "PUT", "PATCH", "DELETE"].includes(this.method)
  801. ? "application/json"
  802. : ""
  803. },
  804. preRequestScript(val, oldVal) {
  805. this.uri = this.uri
  806. },
  807. },
  808. computed: {
  809. /**
  810. * Check content types that can be automatically
  811. * serialized by postwoman.
  812. */
  813. canListParameters() {
  814. return (
  815. this.contentType === "application/x-www-form-urlencoded" ||
  816. this.contentType === "multipart/form-data" ||
  817. isJSONContentType(this.contentType)
  818. )
  819. },
  820. uri: {
  821. get() {
  822. return this.$store.state.request.uri ? this.$store.state.request.uri : this.url + this.path
  823. },
  824. set(value) {
  825. this.$store.commit("setState", { value, attribute: "uri" })
  826. let url = value
  827. if ((this.preRequestScript && this.showPreRequestScript) || hasPathParams(this.params)) {
  828. let environmentVariables = getEnvironmentVariablesFromScript(this.preRequestScript)
  829. environmentVariables = addPathParamsToVariables(this.params, environmentVariables)
  830. url = parseTemplateString(value, environmentVariables)
  831. }
  832. let result = parseUrlAndPath(url)
  833. this.url = result.url
  834. this.path = result.path
  835. },
  836. },
  837. url: {
  838. get() {
  839. return this.$store.state.request.url
  840. },
  841. set(value) {
  842. this.$store.commit("setState", { value, attribute: "url" })
  843. },
  844. },
  845. method: {
  846. get() {
  847. return this.$store.state.request.method
  848. },
  849. set(value) {
  850. this.$store.commit("setState", { value, attribute: "method" })
  851. },
  852. },
  853. path: {
  854. get() {
  855. return this.$store.state.request.path
  856. },
  857. set(value) {
  858. this.$store.commit("setState", { value, attribute: "path" })
  859. },
  860. },
  861. name: {
  862. get() {
  863. return this.$store.state.request.name
  864. },
  865. set(value) {
  866. this.$store.commit("setState", { value, attribute: "name" })
  867. },
  868. },
  869. auth: {
  870. get() {
  871. return this.$store.state.request.auth
  872. },
  873. set(value) {
  874. this.$store.commit("setState", { value, attribute: "auth" })
  875. },
  876. },
  877. httpUser: {
  878. get() {
  879. return this.$store.state.request.httpUser
  880. },
  881. set(value) {
  882. this.$store.commit("setState", { value, attribute: "httpUser" })
  883. },
  884. },
  885. httpPassword: {
  886. get() {
  887. return this.$store.state.request.httpPassword
  888. },
  889. set(value) {
  890. this.$store.commit("setState", { value, attribute: "httpPassword" })
  891. },
  892. },
  893. bearerToken: {
  894. get() {
  895. return this.$store.state.request.bearerToken
  896. },
  897. set(value) {
  898. this.$store.commit("setState", { value, attribute: "bearerToken" })
  899. },
  900. },
  901. tokens: {
  902. get() {
  903. return this.$store.state.oauth2.tokens
  904. },
  905. set(value) {
  906. this.$store.commit("setOAuth2", { value, attribute: "tokens" })
  907. },
  908. },
  909. tokenReqs: {
  910. get() {
  911. return this.$store.state.oauth2.tokenReqs
  912. },
  913. set(value) {
  914. this.$store.commit("setOAuth2", { value, attribute: "tokenReqs" })
  915. },
  916. },
  917. tokenReqSelect: {
  918. get() {
  919. return this.$store.state.oauth2.tokenReqSelect
  920. },
  921. set(value) {
  922. this.$store.commit("setOAuth2", { value, attribute: "tokenReqSelect" })
  923. },
  924. },
  925. tokenReqName: {
  926. get() {
  927. return this.$store.state.oauth2.tokenReqName
  928. },
  929. set(value) {
  930. this.$store.commit("setOAuth2", { value, attribute: "tokenReqName" })
  931. },
  932. },
  933. accessTokenName: {
  934. get() {
  935. return this.$store.state.oauth2.accessTokenName
  936. },
  937. set(value) {
  938. this.$store.commit("setOAuth2", {
  939. value,
  940. attribute: "accessTokenName",
  941. })
  942. },
  943. },
  944. oidcDiscoveryUrl: {
  945. get() {
  946. return this.$store.state.oauth2.oidcDiscoveryUrl
  947. },
  948. set(value) {
  949. this.$store.commit("setOAuth2", {
  950. value,
  951. attribute: "oidcDiscoveryUrl",
  952. })
  953. },
  954. },
  955. authUrl: {
  956. get() {
  957. return this.$store.state.oauth2.authUrl
  958. },
  959. set(value) {
  960. this.$store.commit("setOAuth2", { value, attribute: "authUrl" })
  961. },
  962. },
  963. accessTokenUrl: {
  964. get() {
  965. return this.$store.state.oauth2.accessTokenUrl
  966. },
  967. set(value) {
  968. this.$store.commit("setOAuth2", { value, attribute: "accessTokenUrl" })
  969. },
  970. },
  971. clientId: {
  972. get() {
  973. return this.$store.state.oauth2.clientId
  974. },
  975. set(value) {
  976. this.$store.commit("setOAuth2", { value, attribute: "clientId" })
  977. },
  978. },
  979. scope: {
  980. get() {
  981. return this.$store.state.oauth2.scope
  982. },
  983. set(value) {
  984. this.$store.commit("setOAuth2", { value, attribute: "scope" })
  985. },
  986. },
  987. state: {
  988. get() {
  989. return this.$store.state.oauth2.state
  990. },
  991. set(value) {
  992. this.$store.commit("setOAuth2", { value, attribute: "state" })
  993. },
  994. },
  995. headers: {
  996. get() {
  997. return this.$store.state.request.headers
  998. },
  999. set(value) {
  1000. this.$store.commit("setState", { value, attribute: "headers" })
  1001. },
  1002. },
  1003. params: {
  1004. get() {
  1005. return this.$store.state.request.params
  1006. },
  1007. set(value) {
  1008. this.$store.commit("setState", { value, attribute: "params" })
  1009. },
  1010. },
  1011. bodyParams: {
  1012. get() {
  1013. return this.$store.state.request.bodyParams
  1014. },
  1015. set(value) {
  1016. this.$store.commit("setState", { value, attribute: "bodyParams" })
  1017. },
  1018. },
  1019. rawParams: {
  1020. get() {
  1021. return this.$store.state.request.rawParams
  1022. },
  1023. set(value) {
  1024. this.$store.commit("setState", { value, attribute: "rawParams" })
  1025. },
  1026. },
  1027. rawInput: {
  1028. get() {
  1029. return this.$store.state.request.rawInput
  1030. },
  1031. set(value) {
  1032. this.$store.commit("setState", { value, attribute: "rawInput" })
  1033. },
  1034. },
  1035. requestType: {
  1036. get() {
  1037. return this.$store.state.request.requestType
  1038. },
  1039. set(value) {
  1040. this.$store.commit("setState", { value, attribute: "requestType" })
  1041. },
  1042. },
  1043. contentType: {
  1044. get() {
  1045. return this.$store.state.request.contentType
  1046. },
  1047. set(value) {
  1048. this.$store.commit("setState", { value, attribute: "contentType" })
  1049. },
  1050. },
  1051. passwordFieldType: {
  1052. get() {
  1053. return this.$store.state.request.passwordFieldType
  1054. },
  1055. set(value) {
  1056. this.$store.commit("setState", {
  1057. value,
  1058. attribute: "passwordFieldType",
  1059. })
  1060. },
  1061. },
  1062. selectedRequest() {
  1063. return this.$store.state.postwoman.selectedRequest
  1064. },
  1065. editingRequest() {
  1066. return this.$store.state.postwoman.editingRequest
  1067. },
  1068. requestName() {
  1069. return this.name
  1070. },
  1071. isValidURL() {
  1072. // if showPreRequestScript, we cannot determine if a URL is valid because the full string is not known ahead of time
  1073. return this.showPreRequestScript || httpValid(this.url)
  1074. },
  1075. hasRequestBody() {
  1076. return ["POST", "PUT", "PATCH", "DELETE"].includes(this.method)
  1077. },
  1078. pathName() {
  1079. return this.path.match(/^([^?]*)\??/)[1]
  1080. },
  1081. rawRequestBody() {
  1082. const { bodyParams, contentType } = this
  1083. if (isJSONContentType(contentType)) {
  1084. try {
  1085. const obj = JSON.parse(
  1086. `{${bodyParams
  1087. .filter((item) => (item.hasOwnProperty("active") ? item.active == true : true))
  1088. .filter(({ key }) => !!key)
  1089. .map(({ key, value }) => `"${key}": "${value}"`)
  1090. .join()}}`
  1091. )
  1092. return JSON.stringify(obj, null, 2)
  1093. } catch (ex) {
  1094. console.log(ex)
  1095. this.$toast.clear()
  1096. this.$toast.error(
  1097. "Parameter value must be a string, switch to Raw input for other formats",
  1098. {
  1099. icon: "error",
  1100. }
  1101. )
  1102. return "invalid"
  1103. }
  1104. } else {
  1105. return bodyParams
  1106. .filter((item) => (item.hasOwnProperty("active") ? item.active == true : true))
  1107. .filter(({ key }) => !!key)
  1108. .map(({ key, value }) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
  1109. .join("&")
  1110. }
  1111. },
  1112. queryString() {
  1113. const result = getQueryParams(this.params)
  1114. .map(({ key, value }) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
  1115. .join("&")
  1116. return result === "" ? "" : `?${result}`
  1117. },
  1118. requestCode() {
  1119. let headers = []
  1120. if (this.preRequestScript || hasPathParams(this.params)) {
  1121. let environmentVariables = getEnvironmentVariablesFromScript(this.preRequestScript)
  1122. environmentVariables = addPathParamsToVariables(this.params, environmentVariables)
  1123. for (let k of this.headers.filter((item) =>
  1124. item.hasOwnProperty("active") ? item.active == true : true
  1125. )) {
  1126. const kParsed = parseTemplateString(k.key, environmentVariables)
  1127. const valParsed = parseTemplateString(k.value, environmentVariables)
  1128. headers.push({ key: kParsed, value: valParsed })
  1129. }
  1130. }
  1131. return generateCodeWithGenerator(this.requestType, {
  1132. auth: this.auth,
  1133. method: this.method,
  1134. url: this.url,
  1135. pathName: this.pathName,
  1136. queryString: this.queryString,
  1137. httpUser: this.httpUser,
  1138. httpPassword: this.httpPassword,
  1139. bearerToken: this.bearerToken,
  1140. headers,
  1141. rawInput: this.rawInput,
  1142. rawParams: this.rawParams,
  1143. rawRequestBody: this.rawRequestBody,
  1144. contentType: this.contentType,
  1145. })
  1146. },
  1147. tokenReqDetails() {
  1148. const details = {
  1149. oidcDiscoveryUrl: this.oidcDiscoveryUrl,
  1150. authUrl: this.authUrl,
  1151. accessTokenUrl: this.accessTokenUrl,
  1152. clientId: this.clientId,
  1153. scope: this.scope,
  1154. }
  1155. return JSON.stringify(details, null, 2)
  1156. },
  1157. },
  1158. methods: {
  1159. useSelectedEnvironment(args) {
  1160. let environment = args.environment
  1161. let environments = args.environments
  1162. let preRequestScriptString = ""
  1163. for (let variable of environment.variables) {
  1164. preRequestScriptString += `pw.env.set('${variable.key}', '${variable.value}');\n`
  1165. }
  1166. for (let env of environments) {
  1167. if (env.name === environment.name) {
  1168. continue
  1169. }
  1170. if (env.name === "Globals" || env.name === "globals") {
  1171. preRequestScriptString += this.useSelectedEnvironment({
  1172. environment: env,
  1173. environments,
  1174. })
  1175. }
  1176. }
  1177. this.preRequestScript = preRequestScriptString
  1178. this.showPreRequestScript = true
  1179. return preRequestScriptString
  1180. },
  1181. checkCollections() {
  1182. const checkCollectionAvailability =
  1183. this.$store.state.postwoman.collections &&
  1184. this.$store.state.postwoman.collections.length > 0
  1185. return checkCollectionAvailability
  1186. },
  1187. scrollInto(view) {
  1188. this.$refs[view].$el.scrollIntoView({
  1189. behavior: "smooth",
  1190. })
  1191. },
  1192. handleUseHistory(entry) {
  1193. this.name = entry.name
  1194. this.method = entry.method
  1195. this.uri = entry.url + entry.path
  1196. this.url = entry.url
  1197. this.path = entry.path
  1198. this.showPreRequestScript = entry.usesPreScripts
  1199. this.preRequestScript = entry.preRequestScript
  1200. this.auth = entry.auth
  1201. this.httpUser = entry.httpUser
  1202. this.httpPassword = entry.httpPassword
  1203. this.bearerToken = entry.bearerToken
  1204. this.headers = entry.headers
  1205. this.params = entry.params
  1206. this.bodyParams = entry.bodyParams
  1207. this.rawParams = entry.rawParams
  1208. this.rawInput = entry.rawInput
  1209. this.contentType = entry.contentType
  1210. this.requestType = entry.requestType
  1211. this.testScript = entry.testScript
  1212. this.testsEnabled = entry.usesPostScripts
  1213. if (this.settings.SCROLL_INTO_ENABLED) this.scrollInto("request")
  1214. },
  1215. async makeRequest(auth, headers, requestBody, preRequestScript) {
  1216. const requestOptions = {
  1217. method: this.method,
  1218. url: this.url + this.pathName + this.queryString,
  1219. auth,
  1220. headers,
  1221. data: requestBody,
  1222. credentials: true,
  1223. }
  1224. if (preRequestScript || hasPathParams(this.params)) {
  1225. let environmentVariables = getEnvironmentVariablesFromScript(preRequestScript)
  1226. environmentVariables = addPathParamsToVariables(this.params, environmentVariables)
  1227. requestOptions.url = parseTemplateString(requestOptions.url, environmentVariables)
  1228. if (!(requestOptions.data instanceof FormData)) {
  1229. // TODO: Parse env variables for form data too
  1230. requestOptions.data = parseTemplateString(requestOptions.data, environmentVariables)
  1231. }
  1232. for (let k in requestOptions.headers) {
  1233. const kParsed = parseTemplateString(k, environmentVariables)
  1234. const valParsed = parseTemplateString(requestOptions.headers[k], environmentVariables)
  1235. delete requestOptions.headers[k]
  1236. requestOptions.headers[kParsed] = valParsed
  1237. }
  1238. }
  1239. if (typeof requestOptions.data === "string") {
  1240. requestOptions.data = parseTemplateString(requestOptions.data)
  1241. }
  1242. return await sendNetworkRequest(requestOptions, this.$store)
  1243. },
  1244. cancelRequest() {
  1245. cancelRunningRequest(this.$store)
  1246. },
  1247. async sendRequest() {
  1248. this.$toast.clear()
  1249. if (this.settings.SCROLL_INTO_ENABLED) this.scrollInto("response")
  1250. if (!this.isValidURL) {
  1251. this.$toast.error(this.$t("url_invalid_format"), {
  1252. icon: "error",
  1253. })
  1254. return
  1255. }
  1256. // Start showing the loading bar as soon as possible.
  1257. // The nuxt axios module will hide it when the request is made.
  1258. this.$nuxt.$loading.start()
  1259. this.response = {
  1260. duration: 0,
  1261. size: 0,
  1262. status: this.$t("fetching"),
  1263. body: this.$t("loading"),
  1264. }
  1265. const auth =
  1266. this.auth === "Basic Auth"
  1267. ? {
  1268. username: this.httpUser,
  1269. password: this.httpPassword,
  1270. }
  1271. : null
  1272. let headers = {}
  1273. let headersObject = {}
  1274. Object.keys(headers).forEach((id) => {
  1275. if (headers[id].key) headersObject[headers[id].key] = headers[id].value
  1276. })
  1277. headers = headersObject
  1278. // If the request has a body, we want to ensure Content-Type is sent.
  1279. let requestBody
  1280. if (this.hasRequestBody) {
  1281. requestBody = this.rawInput ? this.rawParams : this.rawRequestBody
  1282. Object.assign(headers, {
  1283. "Content-Type": `${this.contentType}; charset=utf-8`,
  1284. })
  1285. }
  1286. requestBody = requestBody ? requestBody.toString() : null
  1287. if (this.contentType === "multipart/form-data") {
  1288. const formData = new FormData()
  1289. for (const bodyParam of this.bodyParams.filter((item) =>
  1290. item.hasOwnProperty("active") ? item.active == true : true
  1291. )) {
  1292. if (bodyParam?.value?.[0] instanceof File) {
  1293. for (const file of bodyParam.value) {
  1294. formData.append(bodyParam.key, file)
  1295. }
  1296. } else {
  1297. formData.append(bodyParam.key, bodyParam.value)
  1298. }
  1299. }
  1300. requestBody = formData
  1301. }
  1302. // If the request uses a token for auth, we want to make sure it's sent here.
  1303. if (this.auth === "Bearer Token" || this.auth === "OAuth 2.0")
  1304. headers["Authorization"] = `Bearer ${this.bearerToken}`
  1305. headers = Object.assign(
  1306. // Clone the app headers object first, we don't want to
  1307. // mutate it with the request headers added by default.
  1308. Object.assign(
  1309. {},
  1310. this.headers.filter((item) =>
  1311. item.hasOwnProperty("active") ? item.active == true : true
  1312. )
  1313. )
  1314. // We make our temporary headers object the source so
  1315. // that you can override the added headers if you
  1316. // specify them.
  1317. // headers
  1318. )
  1319. Object.keys(headers).forEach((id) => {
  1320. if (headers[id].key) headersObject[headers[id].key] = headers[id].value
  1321. })
  1322. headers = headersObject
  1323. const startTime = new Date().getTime()
  1324. try {
  1325. this.runningRequest = true
  1326. const payload = await this.makeRequest(
  1327. auth,
  1328. headers,
  1329. requestBody,
  1330. this.showPreRequestScript && this.preRequestScript
  1331. )
  1332. this.runningRequest = false
  1333. const duration = new Date().getTime() - startTime
  1334. this.response.duration = duration
  1335. this.response.size = payload.headers["content-length"]
  1336. ;(() => {
  1337. this.response.status = payload.status
  1338. this.response.headers = payload.headers
  1339. // We don't need to bother parsing JSON, axios already handles it for us!
  1340. this.response.body = payload.data
  1341. // Addition of an entry to the history component.
  1342. const entry = {
  1343. name: this.requestName,
  1344. status: this.response.status,
  1345. date: new Date().toLocaleDateString(),
  1346. time: new Date().toLocaleTimeString(),
  1347. updatedOn: new Date(),
  1348. method: this.method,
  1349. url: this.url,
  1350. path: this.path,
  1351. usesPreScripts: this.showPreRequestScript,
  1352. preRequestScript: this.preRequestScript,
  1353. duration,
  1354. star: false,
  1355. auth: this.auth,
  1356. httpUser: this.httpUser,
  1357. httpPassword: this.httpPassword,
  1358. bearerToken: this.bearerToken,
  1359. headers: this.headers,
  1360. params: this.params,
  1361. bodyParams: this.bodyParams,
  1362. rawParams: this.rawParams,
  1363. rawInput: this.rawInput,
  1364. contentType: this.contentType,
  1365. requestType: this.requestType,
  1366. testScript: this.testScript,
  1367. usesPostScripts: this.testsEnabled,
  1368. }
  1369. if ((this.preRequestScript && this.showPreRequestScript) || hasPathParams(this.params)) {
  1370. let environmentVariables = getEnvironmentVariablesFromScript(this.preRequestScript)
  1371. environmentVariables = addPathParamsToVariables(this.params, environmentVariables)
  1372. entry.path = parseTemplateString(entry.path, environmentVariables)
  1373. entry.url = parseTemplateString(entry.url, environmentVariables)
  1374. }
  1375. this.$refs.historyComponent.addEntry(entry)
  1376. if (fb.currentUser !== null && fb.currentSettings[2]) {
  1377. if (fb.currentSettings[2].value) {
  1378. fb.writeHistory(entry)
  1379. }
  1380. }
  1381. })()
  1382. } catch (error) {
  1383. this.runningRequest = false
  1384. // If the error is caused by cancellation, do nothing
  1385. if (error === "cancellation") {
  1386. this.response.status = this.$t("cancelled")
  1387. this.response.body = this.$t("cancelled")
  1388. } else {
  1389. console.log(error)
  1390. const duration = new Date().getTime() - startTime
  1391. this.response.duration = duration
  1392. this.response.size = Buffer.byteLength(JSON.stringify(error))
  1393. if (error.response) {
  1394. this.response.headers = error.response.headers
  1395. this.response.status = error.response.status
  1396. this.response.body = error.response.data
  1397. // Addition of an entry to the history component.
  1398. const entry = {
  1399. name: this.requestName,
  1400. status: this.response.status,
  1401. date: new Date().toLocaleDateString(),
  1402. time: new Date().toLocaleTimeString(),
  1403. updatedOn: new Date(),
  1404. method: this.method,
  1405. url: this.url,
  1406. path: this.path,
  1407. usesPreScripts: this.showPreRequestScript,
  1408. preRequestScript: this.preRequestScript,
  1409. star: false,
  1410. auth: this.auth,
  1411. httpUser: this.httpUser,
  1412. httpPassword: this.httpPassword,
  1413. bearerToken: this.bearerToken,
  1414. headers: this.headers,
  1415. params: this.params,
  1416. bodyParams: this.bodyParams,
  1417. rawParams: this.rawParams,
  1418. rawInput: this.rawInput,
  1419. contentType: this.contentType,
  1420. requestType: this.requestType,
  1421. testScript: this.testScript,
  1422. usesPostScripts: this.testsEnabled,
  1423. }
  1424. if (
  1425. (this.preRequestScript && this.showPreRequestScript) ||
  1426. hasPathParams(this.params)
  1427. ) {
  1428. let environmentVariables = getEnvironmentVariablesFromScript(this.preRequestScript)
  1429. environmentVariables = addPathParamsToVariables(this.params, environmentVariables)
  1430. entry.path = parseTemplateString(entry.path, environmentVariables)
  1431. entry.url = parseTemplateString(entry.url, environmentVariables)
  1432. }
  1433. this.$refs.historyComponent.addEntry(entry)
  1434. if (fb.currentUser !== null && fb.currentSettings[2]) {
  1435. if (fb.currentSettings[2].value) {
  1436. fb.writeHistory(entry)
  1437. }
  1438. }
  1439. return
  1440. } else {
  1441. this.response.status = error.message
  1442. this.response.body = `${error}. ${this.$t("check_console_details")}`
  1443. this.$toast.error(`${error} ${this.$t("f12_details")}`, {
  1444. icon: "error",
  1445. })
  1446. if (!this.$store.state.postwoman.settings.PROXY_ENABLED) {
  1447. this.$toast.info(this.$t("enable_proxy"), {
  1448. icon: "help",
  1449. duration: 8000,
  1450. action: {
  1451. text: this.$t("yes"),
  1452. onClick: (e, toastObject) => {
  1453. this.$router.push({ path: "/settings" })
  1454. },
  1455. },
  1456. })
  1457. }
  1458. }
  1459. }
  1460. }
  1461. // tests
  1462. const syntheticResponse = {
  1463. status: this.response.status,
  1464. body: this.response.body,
  1465. headers: this.response.headers,
  1466. }
  1467. // Parse JSON body
  1468. if (
  1469. syntheticResponse.headers["content-type"] &&
  1470. isJSONContentType(syntheticResponse.headers["content-type"])
  1471. ) {
  1472. try {
  1473. syntheticResponse.body = JSON.parse(
  1474. new TextDecoder("utf-8").decode(new Uint8Array(syntheticResponse.body))
  1475. )
  1476. } catch (_e) {}
  1477. }
  1478. const { testResults } = runTestScriptWithVariables(this.testScript, {
  1479. response: syntheticResponse,
  1480. })
  1481. this.testReports = testResults
  1482. },
  1483. getQueryStringFromPath() {
  1484. const pathParsed = url.parse(this.uri)
  1485. return pathParsed.query ? pathParsed.query : ""
  1486. },
  1487. queryStringToArray(queryString) {
  1488. const queryParsed = querystring.parse(queryString)
  1489. return Object.keys(queryParsed).map((key) => ({
  1490. key,
  1491. value: queryParsed[key],
  1492. active: true,
  1493. }))
  1494. },
  1495. pathInputHandler() {
  1496. if (this.uri.includes("?")) {
  1497. const queryString = this.getQueryStringFromPath()
  1498. const params = this.queryStringToArray(queryString)
  1499. this.paramsWatchEnabled = false
  1500. this.params = params
  1501. }
  1502. },
  1503. addRequestHeader() {
  1504. this.$store.commit("addHeaders", {
  1505. key: "",
  1506. value: "",
  1507. active: true,
  1508. })
  1509. return false
  1510. },
  1511. removeRequestHeader(index) {
  1512. // .slice() gives us an entirely new array rather than giving us just the reference
  1513. const oldHeaders = this.headers.slice()
  1514. this.$store.commit("removeHeaders", index)
  1515. this.$toast.error(this.$t("deleted"), {
  1516. icon: "delete",
  1517. action: {
  1518. text: this.$t("undo"),
  1519. onClick: (e, toastObject) => {
  1520. this.headers = oldHeaders
  1521. toastObject.remove()
  1522. },
  1523. },
  1524. })
  1525. },
  1526. addRequestParam() {
  1527. this.$store.commit("addParams", { key: "", value: "", type: "query", active: true })
  1528. return false
  1529. },
  1530. removeRequestParam(index) {
  1531. // .slice() gives us an entirely new array rather than giving us just the reference
  1532. const oldParams = this.params.slice()
  1533. this.$store.commit("removeParams", index)
  1534. this.$toast.error(this.$t("deleted"), {
  1535. icon: "delete",
  1536. action: {
  1537. text: this.$t("undo"),
  1538. onClick: (e, toastObject) => {
  1539. this.params = oldParams
  1540. toastObject.remove()
  1541. },
  1542. },
  1543. })
  1544. },
  1545. addRequestBodyParam() {
  1546. this.$store.commit("addBodyParams", { key: "", value: "", active: true })
  1547. return false
  1548. },
  1549. removeRequestBodyParam(index) {
  1550. // .slice() gives us an entirely new array rather than giving us just the reference
  1551. const oldBodyParams = this.bodyParams.slice()
  1552. this.$store.commit("removeBodyParams", index)
  1553. this.$toast.error(this.$t("deleted"), {
  1554. icon: "delete",
  1555. action: {
  1556. text: this.$t("undo"),
  1557. onClick: (e, toastObject) => {
  1558. this.bodyParams = oldBodyParams
  1559. toastObject.remove()
  1560. },
  1561. },
  1562. })
  1563. },
  1564. copyRequest() {
  1565. if (navigator.share) {
  1566. const time = new Date().toLocaleTimeString()
  1567. const date = new Date().toLocaleDateString()
  1568. navigator
  1569. .share({
  1570. title: "Hoppscotch",
  1571. text: `Hoppscotch • Open source API development ecosystem at ${time} on ${date}`,
  1572. url: window.location.href,
  1573. })
  1574. .then(() => {})
  1575. .catch(() => {})
  1576. } else {
  1577. const dummy = document.createElement("input")
  1578. document.body.appendChild(dummy)
  1579. dummy.value = window.location.href
  1580. dummy.select()
  1581. document.execCommand("copy")
  1582. document.body.removeChild(dummy)
  1583. this.$refs.copyRequest.innerHTML = this.doneButton
  1584. this.$toast.info(this.$t("copied_to_clipboard"), {
  1585. icon: "done",
  1586. })
  1587. setTimeout(() => (this.$refs.copyRequest.innerHTML = this.copyButton), 1000)
  1588. }
  1589. },
  1590. setRouteQueryState() {
  1591. const flat = (key) => (this[key] !== "" ? `${key}=${this[key]}&` : "")
  1592. const deep = (key) => {
  1593. const haveItems = [...this[key]].length
  1594. if (haveItems && this[key]["value"] !== "") {
  1595. // Exclude files fro query params
  1596. const filesRemoved = this[key].filter((item) => !(item?.value?.[0] instanceof File))
  1597. return `${key}=${JSON.stringify(filesRemoved)}&`
  1598. }
  1599. return ""
  1600. }
  1601. let flats = [
  1602. "method",
  1603. "url",
  1604. "path",
  1605. !this.urlExcludes.auth ? "auth" : null,
  1606. !this.urlExcludes.httpUser ? "httpUser" : null,
  1607. !this.urlExcludes.httpPassword ? "httpPassword" : null,
  1608. !this.urlExcludes.bearerToken ? "bearerToken" : null,
  1609. "contentType",
  1610. ]
  1611. .filter((item) => item !== null)
  1612. .map((item) => flat(item))
  1613. const deeps = ["headers", "params"].map((item) => deep(item))
  1614. const bodyParams = this.rawInput ? [flat("rawParams")] : [deep("bodyParams")]
  1615. history.replaceState(
  1616. window.location.href,
  1617. "",
  1618. `/?${encodeURI(flats.concat(deeps, bodyParams).join("").slice(0, -1))}`
  1619. )
  1620. },
  1621. setRouteQueries(queries) {
  1622. if (typeof queries !== "object") throw new Error("Route query parameters must be a Object")
  1623. for (const key in queries) {
  1624. if (["headers", "params", "bodyParams"].includes(key))
  1625. this[key] = JSON.parse(decodeURI(encodeURI(queries[key])))
  1626. if (key === "rawParams") {
  1627. this.rawInput = true
  1628. this.rawParams = queries["rawParams"]
  1629. } else if (typeof this[key] === "string") {
  1630. this[key] = queries[key]
  1631. }
  1632. }
  1633. },
  1634. // observeRequestButton() {
  1635. // const requestElement = this.$refs.request
  1636. // const sendButtonElement = this.$refs.sendButton
  1637. // const observer = new IntersectionObserver(
  1638. // (entries, observer) => {
  1639. // entries.forEach(({ isIntersecting }) => {
  1640. // if (isIntersecting) sendButtonElement.classList.remove("show")
  1641. // // The button should float when it is no longer visible on screen.
  1642. // // This is done by adding the show class to the button.
  1643. // else sendButtonElement.classList.add("show")
  1644. // })
  1645. // },
  1646. // {
  1647. // rootMargin: "0px",
  1648. // threshold: [0],
  1649. // }
  1650. // )
  1651. // observer.observe(requestElement)
  1652. // },
  1653. handleImport() {
  1654. const { value: text } = document.getElementById("import-curl")
  1655. try {
  1656. const parsedCurl = parseCurlCommand(text)
  1657. const { origin, pathname } = new URL(parsedCurl.url.replace(/"/g, "").replace(/'/g, ""))
  1658. this.url = origin
  1659. this.path = pathname
  1660. this.uri = this.url + this.path
  1661. this.headers = []
  1662. if (parsedCurl.headers) {
  1663. for (const key of Object.keys(parsedCurl.headers)) {
  1664. this.$store.commit("addHeaders", {
  1665. key,
  1666. value: parsedCurl.headers[key],
  1667. })
  1668. }
  1669. }
  1670. this.method = parsedCurl.method.toUpperCase()
  1671. if (parsedCurl["data"]) {
  1672. this.rawInput = true
  1673. this.rawParams = parsedCurl["data"]
  1674. }
  1675. this.showCurlImportModal = false
  1676. } catch (error) {
  1677. this.showCurlImportModal = false
  1678. this.$toast.error(this.$t("curl_invalid_format"), {
  1679. icon: "error",
  1680. })
  1681. }
  1682. },
  1683. switchVisibility() {
  1684. this.passwordFieldType = this.passwordFieldType === "password" ? "text" : "password"
  1685. },
  1686. clearContent(name, { target }) {
  1687. switch (name) {
  1688. case "bodyParams":
  1689. this.bodyParams = []
  1690. this.files = []
  1691. break
  1692. case "rawParams":
  1693. this.rawParams = "{}"
  1694. break
  1695. case "parameters":
  1696. this.params = []
  1697. break
  1698. case "auth":
  1699. this.auth = "None"
  1700. this.httpUser = ""
  1701. this.httpPassword = ""
  1702. this.bearerToken = ""
  1703. this.showTokenRequest = false
  1704. this.tokens = []
  1705. this.tokenReqs = []
  1706. break
  1707. case "access_token":
  1708. this.accessTokenName = ""
  1709. this.oidcDiscoveryUrl = ""
  1710. this.authUrl = ""
  1711. this.accessTokenUrl = ""
  1712. this.clientId = ""
  1713. this.scope = ""
  1714. break
  1715. case "headers":
  1716. this.headers = []
  1717. break
  1718. case "tests":
  1719. this.testReports = []
  1720. break
  1721. case "tokens":
  1722. this.tokens = []
  1723. break
  1724. default:
  1725. this.method = "GET"
  1726. this.url = "https://httpbin.org"
  1727. this.path = "/get"
  1728. this.uri = this.url + this.path
  1729. this.name = "Untitled request"
  1730. this.bodyParams = []
  1731. this.rawParams = "{}"
  1732. this.files = []
  1733. this.params = []
  1734. this.auth = "None"
  1735. this.httpUser = ""
  1736. this.httpPassword = ""
  1737. this.bearerToken = ""
  1738. this.showTokenRequest = false
  1739. this.tokens = []
  1740. this.tokenReqs = []
  1741. this.accessTokenName = ""
  1742. this.oidcDiscoveryUrl = ""
  1743. this.authUrl = ""
  1744. this.accessTokenUrl = ""
  1745. this.clientId = ""
  1746. this.scope = ""
  1747. this.headers = []
  1748. this.testReports = []
  1749. }
  1750. target.innerHTML = this.doneButton
  1751. this.$toast.info(this.$t("cleared"), {
  1752. icon: "clear_all",
  1753. })
  1754. setTimeout(() => (target.innerHTML = '<i class="material-icons">clear_all</i>'), 1000)
  1755. },
  1756. saveRequest() {
  1757. if (!this.checkCollections()) {
  1758. this.$toast.error(this.$t("create_collection"), {
  1759. icon: "error",
  1760. })
  1761. return
  1762. }
  1763. let urlAndPath = parseUrlAndPath(this.uri)
  1764. this.editRequest = {
  1765. url: decodeURI(urlAndPath.url),
  1766. path: decodeURI(urlAndPath.path),
  1767. method: this.method,
  1768. auth: this.auth,
  1769. httpUser: this.httpUser,
  1770. httpPassword: this.httpPassword,
  1771. passwordFieldType: this.passwordFieldType,
  1772. bearerToken: this.bearerToken,
  1773. headers: this.headers,
  1774. params: this.params,
  1775. bodyParams: this.bodyParams,
  1776. rawParams: this.rawParams,
  1777. rawInput: this.rawInput,
  1778. contentType: this.contentType,
  1779. requestType: this.requestType,
  1780. preRequestScript: this.showPreRequestScript == true ? this.preRequestScript : null,
  1781. testScript: this.testsEnabled == true ? this.testScript : null,
  1782. name: this.requestName,
  1783. }
  1784. if (this.selectedRequest.url) {
  1785. this.editRequest = Object.assign({}, this.selectedRequest, this.editRequest)
  1786. }
  1787. this.showSaveRequestModal = true
  1788. },
  1789. hideRequestModal() {
  1790. this.showSaveRequestModal = false
  1791. this.editRequest = {}
  1792. },
  1793. setExclude(excludedField, excluded) {
  1794. if (excludedField === "auth") {
  1795. this.urlExcludes.auth = excluded
  1796. this.urlExcludes.httpUser = excluded
  1797. this.urlExcludes.httpPassword = excluded
  1798. this.urlExcludes.bearerToken = excluded
  1799. } else {
  1800. this.urlExcludes[excludedField] = excluded
  1801. }
  1802. this.setRouteQueryState()
  1803. },
  1804. updateRawBody(rawParams) {
  1805. this.rawParams = rawParams
  1806. },
  1807. async handleAccessTokenRequest() {
  1808. if (this.oidcDiscoveryUrl === "" && (this.authUrl === "" || this.accessTokenUrl === "")) {
  1809. this.$toast.error(this.$t("complete_config_urls"), {
  1810. icon: "error",
  1811. })
  1812. return
  1813. }
  1814. try {
  1815. const tokenReqParams = {
  1816. grantType: "code",
  1817. oidcDiscoveryUrl: this.oidcDiscoveryUrl,
  1818. authUrl: this.authUrl,
  1819. accessTokenUrl: this.accessTokenUrl,
  1820. clientId: this.clientId,
  1821. scope: this.scope,
  1822. }
  1823. await tokenRequest(tokenReqParams)
  1824. } catch (e) {
  1825. this.$toast.error(e, {
  1826. icon: "code",
  1827. })
  1828. }
  1829. },
  1830. async oauthRedirectReq() {
  1831. const tokenInfo = await oauthRedirect()
  1832. if (Object.prototype.hasOwnProperty.call(tokenInfo, "access_token")) {
  1833. this.bearerToken = tokenInfo.access_token
  1834. this.addOAuthToken({
  1835. name: this.accessTokenName,
  1836. value: tokenInfo.access_token,
  1837. })
  1838. }
  1839. },
  1840. addOAuthToken({ name, value }) {
  1841. this.$store.commit("addOAuthToken", {
  1842. name,
  1843. value,
  1844. })
  1845. return false
  1846. },
  1847. removeOAuthToken(index) {
  1848. const oldTokens = this.tokens.slice()
  1849. this.$store.commit("removeOAuthToken", index)
  1850. this.$toast.error(this.$t("deleted"), {
  1851. icon: "delete",
  1852. action: {
  1853. text: this.$t("undo"),
  1854. onClick: (e, toastObject) => {
  1855. this.tokens = oldTokens
  1856. toastObject.remove()
  1857. },
  1858. },
  1859. })
  1860. },
  1861. useOAuthToken(value) {
  1862. this.bearerToken = value
  1863. this.showTokenListModal = false
  1864. },
  1865. addOAuthTokenReq() {
  1866. try {
  1867. const name = this.tokenReqName
  1868. const details = JSON.parse(this.tokenReqDetails)
  1869. this.$store.commit("addOAuthTokenReq", {
  1870. name,
  1871. details,
  1872. })
  1873. this.$toast.info(this.$t("token_request_saved"))
  1874. this.showTokenRequestList = false
  1875. } catch (e) {
  1876. this.$toast.error(e, {
  1877. icon: "code",
  1878. })
  1879. }
  1880. },
  1881. removeOAuthTokenReq(index) {
  1882. const oldTokenReqs = this.tokenReqs.slice()
  1883. const targetReqIndex = this.tokenReqs.findIndex(({ name }) => name === this.tokenReqName)
  1884. if (targetReqIndex < 0) return
  1885. this.$store.commit("removeOAuthTokenReq", targetReqIndex)
  1886. this.$toast.error(this.$t("deleted"), {
  1887. icon: "delete",
  1888. action: {
  1889. text: this.$t("undo"),
  1890. onClick: (e, toastObject) => {
  1891. this.tokenReqs = oldTokenReqs
  1892. toastObject.remove()
  1893. },
  1894. },
  1895. })
  1896. },
  1897. tokenReqChange({ target }) {
  1898. const { details, name } = this.tokenReqs.find(({ name }) => name === target.value)
  1899. const { oidcDiscoveryUrl, authUrl, accessTokenUrl, clientId, scope } = details
  1900. this.tokenReqName = name
  1901. this.oidcDiscoveryUrl = oidcDiscoveryUrl
  1902. this.authUrl = authUrl
  1903. this.accessTokenUrl = accessTokenUrl
  1904. this.clientId = clientId
  1905. this.scope = scope
  1906. },
  1907. setRequestType(val) {
  1908. this.requestType = val
  1909. },
  1910. },
  1911. async mounted() {
  1912. // this.observeRequestButton()
  1913. this._keyListener = function (e) {
  1914. if (e.key === "g" && (e.ctrlKey || e.metaKey)) {
  1915. e.preventDefault()
  1916. if (!this.runningRequest) {
  1917. this.sendRequest()
  1918. } else {
  1919. this.cancelRequest()
  1920. }
  1921. }
  1922. if (e.key === "s" && (e.ctrlKey || e.metaKey)) {
  1923. e.preventDefault()
  1924. this.saveRequest()
  1925. }
  1926. if (e.key === "k" && (e.ctrlKey || e.metaKey)) {
  1927. e.preventDefault()
  1928. this.copyRequest()
  1929. }
  1930. if (e.key === "i" && (e.ctrlKey || e.metaKey)) {
  1931. e.preventDefault()
  1932. this.$refs.clearAll.click()
  1933. }
  1934. if (e.key === "Escape") {
  1935. e.preventDefault()
  1936. this.showCurlImportModal = this.showTokenListModal = this.showTokenRequestList = this.showSaveRequestModal = this.showCodegenModal = false
  1937. }
  1938. if ((e.key === "g" || e.key === "G") && e.altKey) {
  1939. this.method = "GET"
  1940. }
  1941. if ((e.key === "h" || e.key === "H") && e.altKey) {
  1942. this.method = "HEAD"
  1943. }
  1944. if ((e.key === "p" || e.key === "P") && e.altKey) {
  1945. this.method = "POST"
  1946. }
  1947. if ((e.key === "u" || e.key === "U") && e.altKey) {
  1948. this.method = "PUT"
  1949. }
  1950. if ((e.key === "x" || e.key === "X") && e.altKey) {
  1951. this.method = "DELETE"
  1952. }
  1953. if (e.key == "ArrowUp" && e.altKey && this.currentMethodIndex > 0) {
  1954. this.method = this.methodMenuItems[--this.currentMethodIndex % this.methodMenuItems.length]
  1955. } else if (e.key == "ArrowDown" && e.altKey && this.currentMethodIndex < 9) {
  1956. this.method = this.methodMenuItems[++this.currentMethodIndex % this.methodMenuItems.length]
  1957. }
  1958. }
  1959. document.addEventListener("keydown", this._keyListener.bind(this))
  1960. await this.oauthRedirectReq()
  1961. },
  1962. created() {
  1963. this.urlExcludes = this.$store.state.postwoman.settings.URL_EXCLUDES || {
  1964. // Exclude authentication by default for security reasons.
  1965. auth: true,
  1966. httpUser: true,
  1967. httpPassword: true,
  1968. bearerToken: true,
  1969. }
  1970. if (Object.keys(this.$route.query).length) this.setRouteQueries(this.$route.query)
  1971. this.$watch(
  1972. (vm) => [
  1973. vm.name,
  1974. vm.method,
  1975. vm.url,
  1976. vm.auth,
  1977. vm.path,
  1978. vm.httpUser,
  1979. vm.httpPassword,
  1980. vm.bearerToken,
  1981. vm.headers,
  1982. vm.params,
  1983. vm.bodyParams,
  1984. vm.contentType,
  1985. vm.rawParams,
  1986. ],
  1987. (val) => {
  1988. this.setRouteQueryState()
  1989. }
  1990. )
  1991. },
  1992. beforeDestroy() {
  1993. document.removeEventListener("keydown", this._keyListener)
  1994. },
  1995. }
  1996. </script>