1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023 |
- <template>
- <div class="page">
- <div class="content">
- <div class="page-columns inner-left">
- <AppSection class="blue" :label="$t('request')" ref="request" no-legend>
- <ul>
- <li class="shrink">
- <label for="method">{{ $t("method") }}</label>
- <span class="select-wrapper">
- <v-popover>
- <input
- id="method"
- class="method"
- v-model="method"
- :readonly="!customMethod"
- autofocus
- />
- <template slot="popover">
- <div
- v-for="(methodMenuItem, index) in methodMenuItems"
- :key="`method-${index}`"
- >
- <button
- class="icon"
- @click="
- customMethod = methodMenuItem == 'CUSTOM' ? true : false
- method = methodMenuItem
- "
- v-close-popover
- >
- {{ methodMenuItem }}
- </button>
- </div>
- </template>
- </v-popover>
- </span>
- </li>
- <li>
- <label for="url">{{ $t("url") }}</label>
- <input
- v-if="!this.$store.state.postwoman.settings.EXPERIMENTAL_URL_BAR_ENABLED"
- :class="{ error: !isValidURL }"
- class="border-dashed md:border-l border-brdColor"
- @keyup.enter="isValidURL ? sendRequest() : null"
- id="url"
- name="url"
- type="text"
- v-model="uri"
- spellcheck="false"
- @input="pathInputHandler"
- :placeholder="$t('url')"
- />
- <SmartUrlField v-model="uri" v-else />
- </li>
- <li class="shrink">
- <label class="hide-on-small-screen" for="send"> </label>
- <button
- v-if="!runningRequest"
- :disabled="!isValidURL"
- @click="sendRequest"
- id="send"
- ref="sendButton"
- >
- {{ $t("send") }}
- <span>
- <i class="material-icons">send</i>
- </span>
- </button>
- <button v-else @click="cancelRequest" id="send" ref="sendButton">
- {{ $t("cancel") }}
- <span>
- <i class="material-icons">clear</i>
- </span>
- </button>
- </li>
- </ul>
- <ul>
- <li>
- <label for="name" class="text-sm">{{ $t("token_req_name") }}</label>
- <input id="name" name="name" type="text" v-model="name" class="text-sm" />
- </li>
- </ul>
- <div label="Request Body" v-if="['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)">
- <ul>
- <li>
- <label for="contentType" class="text-sm">{{ $t("content_type") }}</label>
- <SmartAutoComplete
- :source="validContentTypes"
- :spellcheck="false"
- v-model="contentType"
- styles="text-sm"
- />
- </li>
- </ul>
- <ul>
- <li>
- <div class="row-wrapper">
- <span>
- <SmartToggle
- v-if="canListParameters"
- :on="rawInput"
- @change="rawInput = $event"
- >
- {{ $t("raw_input") }}
- </SmartToggle>
- </span>
- </div>
- </li>
- </ul>
- <HttpBodyParameters
- v-if="!rawInput"
- :bodyParams="bodyParams"
- @clear-content="clearContent"
- @set-route-query-state="setRouteQueryState"
- @remove-request-body-param="removeRequestBodyParam"
- @add-request-body-param="addRequestBodyParam"
- />
- <HttpRawBody
- v-else
- :rawParams="rawParams"
- :contentType="contentType"
- :rawInput="rawInput"
- @clear-content="clearContent"
- @update-raw-body="updateRawBody"
- @update-raw-input="updateRawInput = (value) => (rawInput = value)"
- />
- </div>
- <div class="row-wrapper">
- <span>
- <button
- class="icon"
- @click="showCurlImportModal = !showCurlImportModal"
- v-tooltip.bottom="$t('import_curl')"
- >
- <i class="material-icons">import_export</i>
- </button>
- <button
- class="icon"
- @click="showCodegenModal = !showCodegenModal"
- :disabled="!isValidURL"
- v-tooltip.bottom="$t('show_code')"
- >
- <i class="material-icons">code</i>
- </button>
- </span>
- <span>
- <button
- class="icon"
- @click="copyRequest"
- ref="copyRequest"
- :disabled="!isValidURL"
- v-tooltip.bottom="$t('copy_request_link')"
- >
- <i v-if="navigatorShare" class="material-icons">share</i>
- <i v-else class="material-icons">content_copy</i>
- </button>
- <button
- class="icon"
- @click="saveRequest"
- ref="saveRequest"
- :disabled="!isValidURL"
- v-tooltip.bottom="$t('save_to_collections')"
- >
- <i class="material-icons">create_new_folder</i>
- </button>
- <button
- class="icon"
- @click="clearContent('', $event)"
- v-tooltip.bottom="$t('clear_all')"
- ref="clearAll"
- >
- <i class="material-icons">clear_all</i>
- </button>
- </span>
- </div>
- </AppSection>
- <section id="options">
- <SmartTabs>
- <SmartTab
- :id="'params'"
- :label="
- $t('parameters') + `${params.length !== 0 ? ' \xA0 • \xA0 ' + params.length : ''}`
- "
- :selected="true"
- >
- <HttpParameters
- :params="params"
- @clear-content="clearContent"
- @remove-request-param="removeRequestParam"
- @add-request-param="addRequestParam"
- />
- </SmartTab>
- <SmartTab
- :id="'headers'"
- :label="
- $t('headers') + `${headers.length !== 0 ? ' \xA0 • \xA0 ' + headers.length : ''}`
- "
- >
- <HttpHeaders
- :headers="headers"
- @clear-content="clearContent"
- @set-route-query-state="setRouteQueryState"
- @remove-request-header="removeRequestHeader"
- @add-request-header="addRequestHeader"
- />
- </SmartTab>
- <SmartTab :id="'authentication'" :label="$t('authentication')">
- <AppSection class="teal" :label="$t('authentication')" ref="authentication" no-legend>
- <ul>
- <li>
- <div class="row-wrapper">
- <label for="auth">{{ $t("authentication") }}</label>
- <div>
- <button
- class="icon"
- @click="clearContent('auth', $event)"
- v-tooltip.bottom="$t('clear')"
- >
- <i class="material-icons">clear_all</i>
- </button>
- </div>
- </div>
- <span class="select-wrapper">
- <select id="auth" v-model="auth">
- <option>None</option>
- <option>Basic Auth</option>
- <option>Bearer Token</option>
- <option>OAuth 2.0</option>
- </select>
- </span>
- </li>
- </ul>
- <ul v-if="auth === 'Basic Auth'">
- <li>
- <input placeholder="User" name="http_basic_user" v-model="httpUser" />
- </li>
- <li>
- <input
- placeholder="Password"
- name="http_basic_passwd"
- :type="passwordFieldType"
- v-model="httpPassword"
- />
- </li>
- <div>
- <li>
- <button class="icon" ref="switchVisibility" @click="switchVisibility">
- <i class="material-icons" v-if="passwordFieldType === 'text'">visibility</i>
- <i class="material-icons" v-if="passwordFieldType !== 'text'"
- >visibility_off</i
- >
- </button>
- </li>
- </div>
- </ul>
- <ul v-if="auth === 'Bearer Token' || auth === 'OAuth 2.0'">
- <li>
- <div class="row-wrapper">
- <input placeholder="Token" name="bearer_token" v-model="bearerToken" />
- <button
- v-if="auth === 'OAuth 2.0'"
- class="icon"
- @click="showTokenListModal = !showTokenListModal"
- v-tooltip.bottom="$t('use_token')"
- >
- <i class="material-icons">open_in_new</i>
- </button>
- <button
- v-if="auth === 'OAuth 2.0'"
- class="icon"
- @click="showTokenRequest = !showTokenRequest"
- v-tooltip.bottom="$t('get_token')"
- >
- <i class="material-icons">vpn_key</i>
- </button>
- </div>
- </li>
- </ul>
- <div class="row-wrapper">
- <SmartToggle :on="!urlExcludes.auth" @change="setExclude('auth', !$event)">
- {{ $t("include_in_url") }}
- </SmartToggle>
- </div>
- </AppSection>
- <AppSection
- v-if="showTokenRequest"
- class="red"
- label="Access Token Request"
- ref="accessTokenRequest"
- >
- <ul>
- <li>
- <div class="row-wrapper">
- <label for="token-name">{{ $t("token_name") }}</label>
- <div>
- <button
- class="icon"
- @click="showTokenRequestList = true"
- v-tooltip.bottom="$t('manage_token_req')"
- >
- <i class="material-icons">library_add</i>
- </button>
- <button
- class="icon"
- @click="clearContent('access_token', $event)"
- v-tooltip.bottom="$t('clear')"
- >
- <i class="material-icons">clear_all</i>
- </button>
- <button
- class="icon"
- @click="showTokenRequest = false"
- v-tooltip.bottom="$t('close')"
- >
- <i class="material-icons">close</i>
- </button>
- </div>
- </div>
- <input
- id="token-name"
- :placeholder="$t('optional')"
- name="token_name"
- v-model="accessTokenName"
- type="text"
- />
- </li>
- </ul>
- <ul>
- <li>
- <label for="oidc-discovery-url">
- {{ $t("oidc_discovery_url") }}
- </label>
- <input
- :disabled="this.authUrl !== '' || this.accessTokenUrl !== ''"
- id="oidc-discovery-url"
- name="oidc_discovery_url"
- type="url"
- v-model="oidcDiscoveryUrl"
- placeholder="https://example.com/.well-known/openid-configuration"
- />
- </li>
- </ul>
- <ul>
- <li>
- <label for="auth-url">{{ $t("auth_url") }}</label>
- <input
- :disabled="this.oidcDiscoveryUrl !== ''"
- id="auth-url"
- name="auth_url"
- type="url"
- v-model="authUrl"
- placeholder="https://example.com/login/oauth/authorize"
- />
- </li>
- </ul>
- <ul>
- <li>
- <label for="access-token-url">
- {{ $t("access_token_url") }}
- </label>
- <input
- :disabled="this.oidcDiscoveryUrl !== ''"
- id="access-token-url"
- name="access_token_url"
- type="url"
- v-model="accessTokenUrl"
- placeholder="https://example.com/login/oauth/access_token"
- />
- </li>
- </ul>
- <ul>
- <li>
- <label for="client-id">{{ $t("client_id") }}</label>
- <input
- id="client-id"
- name="client_id"
- type="text"
- v-model="clientId"
- placeholder="Client ID"
- />
- </li>
- </ul>
- <ul>
- <li>
- <label for="scope">{{ $t("scope") }}</label>
- <input
- id="scope"
- name="scope"
- type="text"
- v-model="scope"
- placeholder="e.g. read:org"
- />
- </li>
- </ul>
- <ul>
- <li>
- <button class="icon" @click="handleAccessTokenRequest">
- <i class="material-icons">vpn_key</i>
- <span>{{ $t("request_token") }}</span>
- </button>
- </li>
- </ul>
- </AppSection>
- </SmartTab>
- <SmartTab :id="'pre_request_script'" :label="$t('pre_request_script')">
- <AppSection
- v-if="showPreRequestScript"
- class="orange"
- :label="$t('pre_request_script')"
- ref="preRequest"
- no-legend
- >
- <ul>
- <li>
- <div class="row-wrapper">
- <label>{{ $t("javascript_code") }}</label>
- <div>
- <a
- href="https://github.com/hoppscotch/hoppscotch/wiki/Pre-Request-Scripts"
- target="_blank"
- rel="noopener"
- >
- <button class="icon" v-tooltip="$t('wiki')">
- <i class="material-icons">help_outline</i>
- </button>
- </a>
- </div>
- </div>
- <SmartJsEditor
- v-model="preRequestScript"
- :options="{
- maxLines: '16',
- minLines: '8',
- fontSize: '16px',
- autoScrollEditorIntoView: true,
- showPrintMargin: false,
- useWorker: false,
- }"
- styles="rounded-b-lg"
- completeMode="pre"
- />
- </li>
- </ul>
- </AppSection>
- </SmartTab>
- <SmartTab :id="'tests'" :label="$t('tests')">
- <AppSection
- v-if="testsEnabled"
- class="orange"
- :label="$t('tests')"
- ref="postRequestTests"
- no-legend
- >
- <ul>
- <li>
- <div class="row-wrapper">
- <label>{{ $t("javascript_code") }}</label>
- <div>
- <a
- href="https://github.com/hoppscotch/hoppscotch/wiki/Post-Request-Tests"
- target="_blank"
- rel="noopener"
- >
- <button class="icon" v-tooltip="$t('wiki')">
- <i class="material-icons">help_outline</i>
- </button>
- </a>
- </div>
- </div>
- <SmartJsEditor
- v-model="testScript"
- :options="{
- maxLines: '16',
- minLines: '8',
- fontSize: '16px',
- autoScrollEditorIntoView: true,
- showPrintMargin: false,
- useWorker: false,
- }"
- styles="rounded-b-lg"
- completeMode="test"
- />
- <div v-if="testReports.length !== 0">
- <div class="row-wrapper">
- <label>Test Reports</label>
- <div>
- <button
- class="icon"
- @click="clearContent('tests', $event)"
- v-tooltip.bottom="$t('clear')"
- >
- <i class="material-icons">clear_all</i>
- </button>
- </div>
- </div>
- <div v-for="(testReport, index) in testReports" :key="index">
- <div v-if="testReport.startBlock" class="info">
- <hr />
- <h4>{{ testReport.startBlock }}</h4>
- </div>
- <p v-else-if="testReport.result" class="row-wrapper info">
- <span :class="testReport.styles.class">
- <i class="material-icons">
- {{ testReport.styles.icon }}
- </i>
- <span> {{ testReport.result }}</span>
- <span v-if="testReport.message">
- <label> • {{ testReport.message }}</label>
- </span>
- </span>
- </p>
- <div v-else-if="testReport.endBlock"><hr /></div>
- </div>
- </div>
- </li>
- </ul>
- </AppSection>
- </SmartTab>
- </SmartTabs>
- </section>
- <HttpResponse :response="response" :active="runningRequest" ref="response" />
- </div>
- <aside v-if="activeSidebar" class="sticky-inner inner-right lg:max-w-md">
- <section>
- <SmartTabs>
- <SmartTab :id="'history'" :label="$t('history')" :selected="true">
- <History :page="'rest'" @useHistory="handleUseHistory" ref="historyComponent" />
- </SmartTab>
- <SmartTab :id="'collections'" :label="$t('collections')">
- <Collections />
- </SmartTab>
- <SmartTab :id="'env'" :label="$t('environments')">
- <Environments @use-environment="useSelectedEnvironment($event)" />
- </SmartTab>
- <SmartTab :id="'notes'" :label="$t('notes')">
- <HttpNotes />
- </SmartTab>
- </SmartTabs>
- </section>
- </aside>
- </div>
- <CollectionsSaveRequest
- :show="showSaveRequestModal"
- @hide-modal="hideRequestModal"
- :editing-request="editRequest"
- />
- <HttpImportCurl
- :show="showCurlImportModal"
- @hide-modal="showCurlImportModal = false"
- @handle-import="handleImport"
- />
- <HttpCodegenModal
- :show="showCodegenModal"
- :requestTypeProp="requestType"
- :requestCode="requestCode"
- @hide-modal="showCodegenModal = false"
- @set-request-type="setRequestType"
- />
- <HttpTokenList
- :show="showTokenListModal"
- :tokens="tokens"
- @clear-content="clearContent"
- @use-oauth-token="useOAuthToken"
- @remove-oauth-token="removeOAuthToken"
- @hide-modal="showTokenListModal = false"
- />
- <SmartModal v-if="showTokenRequestList" @close="showTokenRequestList = false">
- <div slot="header">
- <div class="row-wrapper">
- <h3 class="title">{{ $t("manage_token_req") }}</h3>
- <div>
- <button class="icon" @click="showTokenRequestList = false">
- <i class="material-icons">close</i>
- </button>
- </div>
- </div>
- </div>
- <div slot="body" class="flex flex-col">
- <div class="row-wrapper">
- <label for="token-req-list">{{ $t("token_req_list") }}</label>
- <div>
- <button
- :disabled="this.tokenReqs.length === 0"
- class="icon"
- @click="showTokenRequestList = false"
- v-tooltip.bottom="$t('use_token_req')"
- >
- <i class="material-icons">input</i>
- </button>
- <button
- :disabled="this.tokenReqs.length === 0"
- class="icon"
- @click="removeOAuthTokenReq"
- v-tooltip.bottom="$t('delete')"
- >
- <i class="material-icons">delete</i>
- </button>
- </div>
- </div>
- <ul>
- <li>
- <span class="select-wrapper">
- <select
- id="token-req-list"
- v-model="tokenReqSelect"
- :disabled="this.tokenReqs.length === 0"
- @change="tokenReqChange($event)"
- >
- <option v-for="(req, index) in tokenReqs" :key="index" :value="req.name">
- {{ req.name }}
- </option>
- </select>
- </span>
- </li>
- </ul>
- <label for="token-req-name">{{ $t("token_req_name") }}</label>
- <input v-model="tokenReqName" />
- <label for="token-req-details">
- {{ $t("token_req_details") }}
- </label>
- <textarea id="token-req-details" readonly rows="7" v-model="tokenReqDetails"></textarea>
- </div>
- <div slot="footer">
- <div class="row-wrapper">
- <span></span>
- <span>
- <button class="icon primary" @click="addOAuthTokenReq">
- {{ $t("save_token_req") }}
- </button>
- </span>
- </div>
- </div>
- </SmartModal>
- </div>
- </template>
- <script>
- import url from "url"
- import querystring from "querystring"
- import parseCurlCommand from "~/helpers/curlparser"
- import getEnvironmentVariablesFromScript from "~/helpers/preRequest"
- import runTestScriptWithVariables from "~/helpers/postwomanTesting"
- import parseTemplateString from "~/helpers/templating"
- import { tokenRequest, oauthRedirect } from "~/helpers/oauth"
- import { cancelRunningRequest, sendNetworkRequest } from "~/helpers/network"
- import { fb } from "~/helpers/fb"
- import { hasPathParams, addPathParamsToVariables, getQueryParams } from "~/helpers/requestParams"
- import { parseUrlAndPath } from "~/helpers/utils/uri"
- import { httpValid } from "~/helpers/utils/valid"
- import { knownContentTypes, isJSONContentType } from "~/helpers/utils/contenttypes"
- import { generateCodeWithGenerator } from "~/helpers/codegen/codegen"
- export default {
- data() {
- return {
- showCurlImportModal: false,
- showPreRequestScript: true,
- testsEnabled: true,
- testScript: "// pw.expect('variable').toBe('value');",
- preRequestScript: "// pw.env.set('variable', 'value');",
- testReports: [],
- copyButton: '<i class="material-icons">content_copy</i>',
- downloadButton: '<i class="material-icons">save_alt</i>',
- doneButton: '<i class="material-icons">done</i>',
- showCodegenModal: false,
- response: {
- status: "",
- headers: "",
- body: "",
- duration: 0,
- size: 0,
- },
- validContentTypes: knownContentTypes,
- paramsWatchEnabled: true,
- showTokenListModal: false,
- showTokenRequest: false,
- showTokenRequestList: false,
- showSaveRequestModal: false,
- editRequest: {},
- urlExcludes: {},
- activeSidebar: true,
- fb,
- customMethod: false,
- files: [],
- filenames: "",
- navigatorShare: navigator.share,
- runningRequest: false,
- settings: {
- SCROLL_INTO_ENABLED:
- typeof this.$store.state.postwoman.settings.SCROLL_INTO_ENABLED !== "undefined"
- ? this.$store.state.postwoman.settings.SCROLL_INTO_ENABLED
- : true,
- },
- currentMethodIndex: 0,
- methodMenuItems: [
- "GET",
- "HEAD",
- "POST",
- "PUT",
- "DELETE",
- "CONNECT",
- "OPTIONS",
- "TRACE",
- "PATCH",
- "CUSTOM",
- ],
- }
- },
- watch: {
- urlExcludes: {
- deep: true,
- handler() {
- this.$store.commit("postwoman/applySetting", [
- "URL_EXCLUDES",
- Object.assign({}, this.urlExcludes),
- ])
- },
- },
- canListParameters: {
- immediate: true,
- handler(canListParameters) {
- if (canListParameters) {
- this.$nextTick(() => {
- this.rawInput = Boolean(this.rawParams && this.rawParams !== "{}")
- })
- } else {
- this.rawInput = true
- }
- },
- },
- contentType(contentType, oldContentType) {
- const getDefaultParams = (contentType) => {
- if (isJSONContentType(contentType)) return "{}"
- switch (contentType) {
- case "application/xml":
- return "<?xml version='1.0' encoding='utf-8'?>"
- case "text/html":
- return "<!doctype html>"
- }
- return ""
- }
- if (!this.rawParams || this.rawParams === getDefaultParams(oldContentType)) {
- this.rawParams = getDefaultParams(contentType)
- }
- this.setRouteQueryState()
- },
- params: {
- handler(newValue) {
- if (!this.paramsWatchEnabled) {
- this.paramsWatchEnabled = true
- return
- }
- let path = this.path
- let queryString = getQueryParams(newValue)
- .map(({ key, value }) => `${key}=${value}`)
- .join("&")
- queryString = queryString === "" ? "" : `?${queryString}`
- if (path.includes("?")) {
- path = path.slice(0, path.indexOf("?")) + queryString
- } else {
- path = path + queryString
- }
- this.path = path
- this.setRouteQueryState()
- },
- deep: true,
- },
- selectedRequest(newValue, oldValue) {
- // @TODO: Convert all variables to single request variable
- if (!newValue) return
- this.uri = newValue.url + newValue.path
- this.url = newValue.url
- this.path = newValue.path
- this.method = newValue.method
- this.auth = newValue.auth
- this.httpUser = newValue.httpUser
- this.httpPassword = newValue.httpPassword
- this.passwordFieldType = newValue.passwordFieldType
- this.bearerToken = newValue.bearerToken
- this.headers = newValue.headers
- this.params = newValue.params
- this.bodyParams = newValue.bodyParams
- this.rawParams = newValue.rawParams
- this.rawInput = newValue.rawInput
- this.contentType = newValue.contentType
- this.requestType = newValue.requestType
- if (newValue.preRequestScript) {
- this.showPreRequestScript = true
- this.preRequestScript = newValue.preRequestScript
- }
- if (newValue.testScript) {
- this.testsEnabled = true
- this.testScript = newValue.testScript
- }
- this.name = newValue.name
- },
- editingRequest(newValue) {
- this.editRequest = newValue
- this.showSaveRequestModal = true
- },
- method() {
- this.contentType = ["POST", "PUT", "PATCH", "DELETE"].includes(this.method)
- ? "application/json"
- : ""
- },
- preRequestScript(val, oldVal) {
- this.uri = this.uri
- },
- },
- computed: {
- /**
- * Check content types that can be automatically
- * serialized by postwoman.
- */
- canListParameters() {
- return (
- this.contentType === "application/x-www-form-urlencoded" ||
- this.contentType === "multipart/form-data" ||
- isJSONContentType(this.contentType)
- )
- },
- uri: {
- get() {
- return this.$store.state.request.uri ? this.$store.state.request.uri : this.url + this.path
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "uri" })
- let url = value
- if ((this.preRequestScript && this.showPreRequestScript) || hasPathParams(this.params)) {
- let environmentVariables = getEnvironmentVariablesFromScript(this.preRequestScript)
- environmentVariables = addPathParamsToVariables(this.params, environmentVariables)
- url = parseTemplateString(value, environmentVariables)
- }
- let result = parseUrlAndPath(url)
- this.url = result.url
- this.path = result.path
- },
- },
- url: {
- get() {
- return this.$store.state.request.url
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "url" })
- },
- },
- method: {
- get() {
- return this.$store.state.request.method
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "method" })
- },
- },
- path: {
- get() {
- return this.$store.state.request.path
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "path" })
- },
- },
- name: {
- get() {
- return this.$store.state.request.name
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "name" })
- },
- },
- auth: {
- get() {
- return this.$store.state.request.auth
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "auth" })
- },
- },
- httpUser: {
- get() {
- return this.$store.state.request.httpUser
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "httpUser" })
- },
- },
- httpPassword: {
- get() {
- return this.$store.state.request.httpPassword
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "httpPassword" })
- },
- },
- bearerToken: {
- get() {
- return this.$store.state.request.bearerToken
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "bearerToken" })
- },
- },
- tokens: {
- get() {
- return this.$store.state.oauth2.tokens
- },
- set(value) {
- this.$store.commit("setOAuth2", { value, attribute: "tokens" })
- },
- },
- tokenReqs: {
- get() {
- return this.$store.state.oauth2.tokenReqs
- },
- set(value) {
- this.$store.commit("setOAuth2", { value, attribute: "tokenReqs" })
- },
- },
- tokenReqSelect: {
- get() {
- return this.$store.state.oauth2.tokenReqSelect
- },
- set(value) {
- this.$store.commit("setOAuth2", { value, attribute: "tokenReqSelect" })
- },
- },
- tokenReqName: {
- get() {
- return this.$store.state.oauth2.tokenReqName
- },
- set(value) {
- this.$store.commit("setOAuth2", { value, attribute: "tokenReqName" })
- },
- },
- accessTokenName: {
- get() {
- return this.$store.state.oauth2.accessTokenName
- },
- set(value) {
- this.$store.commit("setOAuth2", {
- value,
- attribute: "accessTokenName",
- })
- },
- },
- oidcDiscoveryUrl: {
- get() {
- return this.$store.state.oauth2.oidcDiscoveryUrl
- },
- set(value) {
- this.$store.commit("setOAuth2", {
- value,
- attribute: "oidcDiscoveryUrl",
- })
- },
- },
- authUrl: {
- get() {
- return this.$store.state.oauth2.authUrl
- },
- set(value) {
- this.$store.commit("setOAuth2", { value, attribute: "authUrl" })
- },
- },
- accessTokenUrl: {
- get() {
- return this.$store.state.oauth2.accessTokenUrl
- },
- set(value) {
- this.$store.commit("setOAuth2", { value, attribute: "accessTokenUrl" })
- },
- },
- clientId: {
- get() {
- return this.$store.state.oauth2.clientId
- },
- set(value) {
- this.$store.commit("setOAuth2", { value, attribute: "clientId" })
- },
- },
- scope: {
- get() {
- return this.$store.state.oauth2.scope
- },
- set(value) {
- this.$store.commit("setOAuth2", { value, attribute: "scope" })
- },
- },
- state: {
- get() {
- return this.$store.state.oauth2.state
- },
- set(value) {
- this.$store.commit("setOAuth2", { value, attribute: "state" })
- },
- },
- headers: {
- get() {
- return this.$store.state.request.headers
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "headers" })
- },
- },
- params: {
- get() {
- return this.$store.state.request.params
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "params" })
- },
- },
- bodyParams: {
- get() {
- return this.$store.state.request.bodyParams
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "bodyParams" })
- },
- },
- rawParams: {
- get() {
- return this.$store.state.request.rawParams
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "rawParams" })
- },
- },
- rawInput: {
- get() {
- return this.$store.state.request.rawInput
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "rawInput" })
- },
- },
- requestType: {
- get() {
- return this.$store.state.request.requestType
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "requestType" })
- },
- },
- contentType: {
- get() {
- return this.$store.state.request.contentType
- },
- set(value) {
- this.$store.commit("setState", { value, attribute: "contentType" })
- },
- },
- passwordFieldType: {
- get() {
- return this.$store.state.request.passwordFieldType
- },
- set(value) {
- this.$store.commit("setState", {
- value,
- attribute: "passwordFieldType",
- })
- },
- },
- selectedRequest() {
- return this.$store.state.postwoman.selectedRequest
- },
- editingRequest() {
- return this.$store.state.postwoman.editingRequest
- },
- requestName() {
- return this.name
- },
- isValidURL() {
- // if showPreRequestScript, we cannot determine if a URL is valid because the full string is not known ahead of time
- return this.showPreRequestScript || httpValid(this.url)
- },
- hasRequestBody() {
- return ["POST", "PUT", "PATCH", "DELETE"].includes(this.method)
- },
- pathName() {
- return this.path.match(/^([^?]*)\??/)[1]
- },
- rawRequestBody() {
- const { bodyParams, contentType } = this
- if (isJSONContentType(contentType)) {
- try {
- const obj = JSON.parse(
- `{${bodyParams
- .filter((item) => (item.hasOwnProperty("active") ? item.active == true : true))
- .filter(({ key }) => !!key)
- .map(({ key, value }) => `"${key}": "${value}"`)
- .join()}}`
- )
- return JSON.stringify(obj, null, 2)
- } catch (ex) {
- console.log(ex)
- this.$toast.clear()
- this.$toast.error(
- "Parameter value must be a string, switch to Raw input for other formats",
- {
- icon: "error",
- }
- )
- return "invalid"
- }
- } else {
- return bodyParams
- .filter((item) => (item.hasOwnProperty("active") ? item.active == true : true))
- .filter(({ key }) => !!key)
- .map(({ key, value }) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
- .join("&")
- }
- },
- queryString() {
- const result = getQueryParams(this.params)
- .map(({ key, value }) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
- .join("&")
- return result === "" ? "" : `?${result}`
- },
- requestCode() {
- let headers = []
- if (this.preRequestScript || hasPathParams(this.params)) {
- let environmentVariables = getEnvironmentVariablesFromScript(this.preRequestScript)
- environmentVariables = addPathParamsToVariables(this.params, environmentVariables)
- for (let k of this.headers.filter((item) =>
- item.hasOwnProperty("active") ? item.active == true : true
- )) {
- const kParsed = parseTemplateString(k.key, environmentVariables)
- const valParsed = parseTemplateString(k.value, environmentVariables)
- headers.push({ key: kParsed, value: valParsed })
- }
- }
- return generateCodeWithGenerator(this.requestType, {
- auth: this.auth,
- method: this.method,
- url: this.url,
- pathName: this.pathName,
- queryString: this.queryString,
- httpUser: this.httpUser,
- httpPassword: this.httpPassword,
- bearerToken: this.bearerToken,
- headers,
- rawInput: this.rawInput,
- rawParams: this.rawParams,
- rawRequestBody: this.rawRequestBody,
- contentType: this.contentType,
- })
- },
- tokenReqDetails() {
- const details = {
- oidcDiscoveryUrl: this.oidcDiscoveryUrl,
- authUrl: this.authUrl,
- accessTokenUrl: this.accessTokenUrl,
- clientId: this.clientId,
- scope: this.scope,
- }
- return JSON.stringify(details, null, 2)
- },
- },
- methods: {
- useSelectedEnvironment(args) {
- let environment = args.environment
- let environments = args.environments
- let preRequestScriptString = ""
- for (let variable of environment.variables) {
- preRequestScriptString += `pw.env.set('${variable.key}', '${variable.value}');\n`
- }
- for (let env of environments) {
- if (env.name === environment.name) {
- continue
- }
- if (env.name === "Globals" || env.name === "globals") {
- preRequestScriptString += this.useSelectedEnvironment({
- environment: env,
- environments,
- })
- }
- }
- this.preRequestScript = preRequestScriptString
- this.showPreRequestScript = true
- return preRequestScriptString
- },
- checkCollections() {
- const checkCollectionAvailability =
- this.$store.state.postwoman.collections &&
- this.$store.state.postwoman.collections.length > 0
- return checkCollectionAvailability
- },
- scrollInto(view) {
- this.$refs[view].$el.scrollIntoView({
- behavior: "smooth",
- })
- },
- handleUseHistory(entry) {
- this.name = entry.name
- this.method = entry.method
- this.uri = entry.url + entry.path
- this.url = entry.url
- this.path = entry.path
- this.showPreRequestScript = entry.usesPreScripts
- this.preRequestScript = entry.preRequestScript
- this.auth = entry.auth
- this.httpUser = entry.httpUser
- this.httpPassword = entry.httpPassword
- this.bearerToken = entry.bearerToken
- this.headers = entry.headers
- this.params = entry.params
- this.bodyParams = entry.bodyParams
- this.rawParams = entry.rawParams
- this.rawInput = entry.rawInput
- this.contentType = entry.contentType
- this.requestType = entry.requestType
- this.testScript = entry.testScript
- this.testsEnabled = entry.usesPostScripts
- if (this.settings.SCROLL_INTO_ENABLED) this.scrollInto("request")
- },
- async makeRequest(auth, headers, requestBody, preRequestScript) {
- const requestOptions = {
- method: this.method,
- url: this.url + this.pathName + this.queryString,
- auth,
- headers,
- data: requestBody,
- credentials: true,
- }
- if (preRequestScript || hasPathParams(this.params)) {
- let environmentVariables = getEnvironmentVariablesFromScript(preRequestScript)
- environmentVariables = addPathParamsToVariables(this.params, environmentVariables)
- requestOptions.url = parseTemplateString(requestOptions.url, environmentVariables)
- if (!(requestOptions.data instanceof FormData)) {
- // TODO: Parse env variables for form data too
- requestOptions.data = parseTemplateString(requestOptions.data, environmentVariables)
- }
- for (let k in requestOptions.headers) {
- const kParsed = parseTemplateString(k, environmentVariables)
- const valParsed = parseTemplateString(requestOptions.headers[k], environmentVariables)
- delete requestOptions.headers[k]
- requestOptions.headers[kParsed] = valParsed
- }
- }
- if (typeof requestOptions.data === "string") {
- requestOptions.data = parseTemplateString(requestOptions.data)
- }
- return await sendNetworkRequest(requestOptions, this.$store)
- },
- cancelRequest() {
- cancelRunningRequest(this.$store)
- },
- async sendRequest() {
- this.$toast.clear()
- if (this.settings.SCROLL_INTO_ENABLED) this.scrollInto("response")
- if (!this.isValidURL) {
- this.$toast.error(this.$t("url_invalid_format"), {
- icon: "error",
- })
- return
- }
- // Start showing the loading bar as soon as possible.
- // The nuxt axios module will hide it when the request is made.
- this.$nuxt.$loading.start()
- this.response = {
- duration: 0,
- size: 0,
- status: this.$t("fetching"),
- body: this.$t("loading"),
- }
- const auth =
- this.auth === "Basic Auth"
- ? {
- username: this.httpUser,
- password: this.httpPassword,
- }
- : null
- let headers = {}
- let headersObject = {}
- Object.keys(headers).forEach((id) => {
- if (headers[id].key) headersObject[headers[id].key] = headers[id].value
- })
- headers = headersObject
- // If the request has a body, we want to ensure Content-Type is sent.
- let requestBody
- if (this.hasRequestBody) {
- requestBody = this.rawInput ? this.rawParams : this.rawRequestBody
- Object.assign(headers, {
- "Content-Type": `${this.contentType}; charset=utf-8`,
- })
- }
- requestBody = requestBody ? requestBody.toString() : null
- if (this.contentType === "multipart/form-data") {
- const formData = new FormData()
- for (const bodyParam of this.bodyParams.filter((item) =>
- item.hasOwnProperty("active") ? item.active == true : true
- )) {
- if (bodyParam?.value?.[0] instanceof File) {
- for (const file of bodyParam.value) {
- formData.append(bodyParam.key, file)
- }
- } else {
- formData.append(bodyParam.key, bodyParam.value)
- }
- }
- requestBody = formData
- }
- // If the request uses a token for auth, we want to make sure it's sent here.
- if (this.auth === "Bearer Token" || this.auth === "OAuth 2.0")
- headers["Authorization"] = `Bearer ${this.bearerToken}`
- headers = Object.assign(
- // Clone the app headers object first, we don't want to
- // mutate it with the request headers added by default.
- Object.assign(
- {},
- this.headers.filter((item) =>
- item.hasOwnProperty("active") ? item.active == true : true
- )
- )
- // We make our temporary headers object the source so
- // that you can override the added headers if you
- // specify them.
- // headers
- )
- Object.keys(headers).forEach((id) => {
- if (headers[id].key) headersObject[headers[id].key] = headers[id].value
- })
- headers = headersObject
- const startTime = new Date().getTime()
- try {
- this.runningRequest = true
- const payload = await this.makeRequest(
- auth,
- headers,
- requestBody,
- this.showPreRequestScript && this.preRequestScript
- )
- this.runningRequest = false
- const duration = new Date().getTime() - startTime
- this.response.duration = duration
- this.response.size = payload.headers["content-length"]
- ;(() => {
- this.response.status = payload.status
- this.response.headers = payload.headers
- // We don't need to bother parsing JSON, axios already handles it for us!
- this.response.body = payload.data
- // Addition of an entry to the history component.
- const entry = {
- name: this.requestName,
- status: this.response.status,
- date: new Date().toLocaleDateString(),
- time: new Date().toLocaleTimeString(),
- updatedOn: new Date(),
- method: this.method,
- url: this.url,
- path: this.path,
- usesPreScripts: this.showPreRequestScript,
- preRequestScript: this.preRequestScript,
- duration,
- star: false,
- auth: this.auth,
- httpUser: this.httpUser,
- httpPassword: this.httpPassword,
- bearerToken: this.bearerToken,
- headers: this.headers,
- params: this.params,
- bodyParams: this.bodyParams,
- rawParams: this.rawParams,
- rawInput: this.rawInput,
- contentType: this.contentType,
- requestType: this.requestType,
- testScript: this.testScript,
- usesPostScripts: this.testsEnabled,
- }
- if ((this.preRequestScript && this.showPreRequestScript) || hasPathParams(this.params)) {
- let environmentVariables = getEnvironmentVariablesFromScript(this.preRequestScript)
- environmentVariables = addPathParamsToVariables(this.params, environmentVariables)
- entry.path = parseTemplateString(entry.path, environmentVariables)
- entry.url = parseTemplateString(entry.url, environmentVariables)
- }
- this.$refs.historyComponent.addEntry(entry)
- if (fb.currentUser !== null && fb.currentSettings[2]) {
- if (fb.currentSettings[2].value) {
- fb.writeHistory(entry)
- }
- }
- })()
- } catch (error) {
- this.runningRequest = false
- // If the error is caused by cancellation, do nothing
- if (error === "cancellation") {
- this.response.status = this.$t("cancelled")
- this.response.body = this.$t("cancelled")
- } else {
- console.log(error)
- const duration = new Date().getTime() - startTime
- this.response.duration = duration
- this.response.size = Buffer.byteLength(JSON.stringify(error))
- if (error.response) {
- this.response.headers = error.response.headers
- this.response.status = error.response.status
- this.response.body = error.response.data
- // Addition of an entry to the history component.
- const entry = {
- name: this.requestName,
- status: this.response.status,
- date: new Date().toLocaleDateString(),
- time: new Date().toLocaleTimeString(),
- updatedOn: new Date(),
- method: this.method,
- url: this.url,
- path: this.path,
- usesPreScripts: this.showPreRequestScript,
- preRequestScript: this.preRequestScript,
- star: false,
- auth: this.auth,
- httpUser: this.httpUser,
- httpPassword: this.httpPassword,
- bearerToken: this.bearerToken,
- headers: this.headers,
- params: this.params,
- bodyParams: this.bodyParams,
- rawParams: this.rawParams,
- rawInput: this.rawInput,
- contentType: this.contentType,
- requestType: this.requestType,
- testScript: this.testScript,
- usesPostScripts: this.testsEnabled,
- }
- if (
- (this.preRequestScript && this.showPreRequestScript) ||
- hasPathParams(this.params)
- ) {
- let environmentVariables = getEnvironmentVariablesFromScript(this.preRequestScript)
- environmentVariables = addPathParamsToVariables(this.params, environmentVariables)
- entry.path = parseTemplateString(entry.path, environmentVariables)
- entry.url = parseTemplateString(entry.url, environmentVariables)
- }
- this.$refs.historyComponent.addEntry(entry)
- if (fb.currentUser !== null && fb.currentSettings[2]) {
- if (fb.currentSettings[2].value) {
- fb.writeHistory(entry)
- }
- }
- return
- } else {
- this.response.status = error.message
- this.response.body = `${error}. ${this.$t("check_console_details")}`
- this.$toast.error(`${error} ${this.$t("f12_details")}`, {
- icon: "error",
- })
- if (!this.$store.state.postwoman.settings.PROXY_ENABLED) {
- this.$toast.info(this.$t("enable_proxy"), {
- icon: "help",
- duration: 8000,
- action: {
- text: this.$t("yes"),
- onClick: (e, toastObject) => {
- this.$router.push({ path: "/settings" })
- },
- },
- })
- }
- }
- }
- }
- // tests
- const syntheticResponse = {
- status: this.response.status,
- body: this.response.body,
- headers: this.response.headers,
- }
- // Parse JSON body
- if (
- syntheticResponse.headers["content-type"] &&
- isJSONContentType(syntheticResponse.headers["content-type"])
- ) {
- try {
- syntheticResponse.body = JSON.parse(
- new TextDecoder("utf-8").decode(new Uint8Array(syntheticResponse.body))
- )
- } catch (_e) {}
- }
- const { testResults } = runTestScriptWithVariables(this.testScript, {
- response: syntheticResponse,
- })
- this.testReports = testResults
- },
- getQueryStringFromPath() {
- const pathParsed = url.parse(this.uri)
- return pathParsed.query ? pathParsed.query : ""
- },
- queryStringToArray(queryString) {
- const queryParsed = querystring.parse(queryString)
- return Object.keys(queryParsed).map((key) => ({
- key,
- value: queryParsed[key],
- active: true,
- }))
- },
- pathInputHandler() {
- if (this.uri.includes("?")) {
- const queryString = this.getQueryStringFromPath()
- const params = this.queryStringToArray(queryString)
- this.paramsWatchEnabled = false
- this.params = params
- }
- },
- addRequestHeader() {
- this.$store.commit("addHeaders", {
- key: "",
- value: "",
- active: true,
- })
- return false
- },
- removeRequestHeader(index) {
- // .slice() gives us an entirely new array rather than giving us just the reference
- const oldHeaders = this.headers.slice()
- this.$store.commit("removeHeaders", index)
- this.$toast.error(this.$t("deleted"), {
- icon: "delete",
- action: {
- text: this.$t("undo"),
- onClick: (e, toastObject) => {
- this.headers = oldHeaders
- toastObject.remove()
- },
- },
- })
- },
- addRequestParam() {
- this.$store.commit("addParams", { key: "", value: "", type: "query", active: true })
- return false
- },
- removeRequestParam(index) {
- // .slice() gives us an entirely new array rather than giving us just the reference
- const oldParams = this.params.slice()
- this.$store.commit("removeParams", index)
- this.$toast.error(this.$t("deleted"), {
- icon: "delete",
- action: {
- text: this.$t("undo"),
- onClick: (e, toastObject) => {
- this.params = oldParams
- toastObject.remove()
- },
- },
- })
- },
- addRequestBodyParam() {
- this.$store.commit("addBodyParams", { key: "", value: "", active: true })
- return false
- },
- removeRequestBodyParam(index) {
- // .slice() gives us an entirely new array rather than giving us just the reference
- const oldBodyParams = this.bodyParams.slice()
- this.$store.commit("removeBodyParams", index)
- this.$toast.error(this.$t("deleted"), {
- icon: "delete",
- action: {
- text: this.$t("undo"),
- onClick: (e, toastObject) => {
- this.bodyParams = oldBodyParams
- toastObject.remove()
- },
- },
- })
- },
- copyRequest() {
- if (navigator.share) {
- const time = new Date().toLocaleTimeString()
- const date = new Date().toLocaleDateString()
- navigator
- .share({
- title: "Hoppscotch",
- text: `Hoppscotch • Open source API development ecosystem at ${time} on ${date}`,
- url: window.location.href,
- })
- .then(() => {})
- .catch(() => {})
- } else {
- const dummy = document.createElement("input")
- document.body.appendChild(dummy)
- dummy.value = window.location.href
- dummy.select()
- document.execCommand("copy")
- document.body.removeChild(dummy)
- this.$refs.copyRequest.innerHTML = this.doneButton
- this.$toast.info(this.$t("copied_to_clipboard"), {
- icon: "done",
- })
- setTimeout(() => (this.$refs.copyRequest.innerHTML = this.copyButton), 1000)
- }
- },
- setRouteQueryState() {
- const flat = (key) => (this[key] !== "" ? `${key}=${this[key]}&` : "")
- const deep = (key) => {
- const haveItems = [...this[key]].length
- if (haveItems && this[key]["value"] !== "") {
- // Exclude files fro query params
- const filesRemoved = this[key].filter((item) => !(item?.value?.[0] instanceof File))
- return `${key}=${JSON.stringify(filesRemoved)}&`
- }
- return ""
- }
- let flats = [
- "method",
- "url",
- "path",
- !this.urlExcludes.auth ? "auth" : null,
- !this.urlExcludes.httpUser ? "httpUser" : null,
- !this.urlExcludes.httpPassword ? "httpPassword" : null,
- !this.urlExcludes.bearerToken ? "bearerToken" : null,
- "contentType",
- ]
- .filter((item) => item !== null)
- .map((item) => flat(item))
- const deeps = ["headers", "params"].map((item) => deep(item))
- const bodyParams = this.rawInput ? [flat("rawParams")] : [deep("bodyParams")]
- history.replaceState(
- window.location.href,
- "",
- `/?${encodeURI(flats.concat(deeps, bodyParams).join("").slice(0, -1))}`
- )
- },
- setRouteQueries(queries) {
- if (typeof queries !== "object") throw new Error("Route query parameters must be a Object")
- for (const key in queries) {
- if (["headers", "params", "bodyParams"].includes(key))
- this[key] = JSON.parse(decodeURI(encodeURI(queries[key])))
- if (key === "rawParams") {
- this.rawInput = true
- this.rawParams = queries["rawParams"]
- } else if (typeof this[key] === "string") {
- this[key] = queries[key]
- }
- }
- },
- // observeRequestButton() {
- // const requestElement = this.$refs.request
- // const sendButtonElement = this.$refs.sendButton
- // const observer = new IntersectionObserver(
- // (entries, observer) => {
- // entries.forEach(({ isIntersecting }) => {
- // if (isIntersecting) sendButtonElement.classList.remove("show")
- // // The button should float when it is no longer visible on screen.
- // // This is done by adding the show class to the button.
- // else sendButtonElement.classList.add("show")
- // })
- // },
- // {
- // rootMargin: "0px",
- // threshold: [0],
- // }
- // )
- // observer.observe(requestElement)
- // },
- handleImport() {
- const { value: text } = document.getElementById("import-curl")
- try {
- const parsedCurl = parseCurlCommand(text)
- const { origin, pathname } = new URL(parsedCurl.url.replace(/"/g, "").replace(/'/g, ""))
- this.url = origin
- this.path = pathname
- this.uri = this.url + this.path
- this.headers = []
- if (parsedCurl.headers) {
- for (const key of Object.keys(parsedCurl.headers)) {
- this.$store.commit("addHeaders", {
- key,
- value: parsedCurl.headers[key],
- })
- }
- }
- this.method = parsedCurl.method.toUpperCase()
- if (parsedCurl["data"]) {
- this.rawInput = true
- this.rawParams = parsedCurl["data"]
- }
- this.showCurlImportModal = false
- } catch (error) {
- this.showCurlImportModal = false
- this.$toast.error(this.$t("curl_invalid_format"), {
- icon: "error",
- })
- }
- },
- switchVisibility() {
- this.passwordFieldType = this.passwordFieldType === "password" ? "text" : "password"
- },
- clearContent(name, { target }) {
- switch (name) {
- case "bodyParams":
- this.bodyParams = []
- this.files = []
- break
- case "rawParams":
- this.rawParams = "{}"
- break
- case "parameters":
- this.params = []
- break
- case "auth":
- this.auth = "None"
- this.httpUser = ""
- this.httpPassword = ""
- this.bearerToken = ""
- this.showTokenRequest = false
- this.tokens = []
- this.tokenReqs = []
- break
- case "access_token":
- this.accessTokenName = ""
- this.oidcDiscoveryUrl = ""
- this.authUrl = ""
- this.accessTokenUrl = ""
- this.clientId = ""
- this.scope = ""
- break
- case "headers":
- this.headers = []
- break
- case "tests":
- this.testReports = []
- break
- case "tokens":
- this.tokens = []
- break
- default:
- this.method = "GET"
- this.url = "https://httpbin.org"
- this.path = "/get"
- this.uri = this.url + this.path
- this.name = "Untitled request"
- this.bodyParams = []
- this.rawParams = "{}"
- this.files = []
- this.params = []
- this.auth = "None"
- this.httpUser = ""
- this.httpPassword = ""
- this.bearerToken = ""
- this.showTokenRequest = false
- this.tokens = []
- this.tokenReqs = []
- this.accessTokenName = ""
- this.oidcDiscoveryUrl = ""
- this.authUrl = ""
- this.accessTokenUrl = ""
- this.clientId = ""
- this.scope = ""
- this.headers = []
- this.testReports = []
- }
- target.innerHTML = this.doneButton
- this.$toast.info(this.$t("cleared"), {
- icon: "clear_all",
- })
- setTimeout(() => (target.innerHTML = '<i class="material-icons">clear_all</i>'), 1000)
- },
- saveRequest() {
- if (!this.checkCollections()) {
- this.$toast.error(this.$t("create_collection"), {
- icon: "error",
- })
- return
- }
- let urlAndPath = parseUrlAndPath(this.uri)
- this.editRequest = {
- url: decodeURI(urlAndPath.url),
- path: decodeURI(urlAndPath.path),
- method: this.method,
- auth: this.auth,
- httpUser: this.httpUser,
- httpPassword: this.httpPassword,
- passwordFieldType: this.passwordFieldType,
- bearerToken: this.bearerToken,
- headers: this.headers,
- params: this.params,
- bodyParams: this.bodyParams,
- rawParams: this.rawParams,
- rawInput: this.rawInput,
- contentType: this.contentType,
- requestType: this.requestType,
- preRequestScript: this.showPreRequestScript == true ? this.preRequestScript : null,
- testScript: this.testsEnabled == true ? this.testScript : null,
- name: this.requestName,
- }
- if (this.selectedRequest.url) {
- this.editRequest = Object.assign({}, this.selectedRequest, this.editRequest)
- }
- this.showSaveRequestModal = true
- },
- hideRequestModal() {
- this.showSaveRequestModal = false
- this.editRequest = {}
- },
- setExclude(excludedField, excluded) {
- if (excludedField === "auth") {
- this.urlExcludes.auth = excluded
- this.urlExcludes.httpUser = excluded
- this.urlExcludes.httpPassword = excluded
- this.urlExcludes.bearerToken = excluded
- } else {
- this.urlExcludes[excludedField] = excluded
- }
- this.setRouteQueryState()
- },
- updateRawBody(rawParams) {
- this.rawParams = rawParams
- },
- async handleAccessTokenRequest() {
- if (this.oidcDiscoveryUrl === "" && (this.authUrl === "" || this.accessTokenUrl === "")) {
- this.$toast.error(this.$t("complete_config_urls"), {
- icon: "error",
- })
- return
- }
- try {
- const tokenReqParams = {
- grantType: "code",
- oidcDiscoveryUrl: this.oidcDiscoveryUrl,
- authUrl: this.authUrl,
- accessTokenUrl: this.accessTokenUrl,
- clientId: this.clientId,
- scope: this.scope,
- }
- await tokenRequest(tokenReqParams)
- } catch (e) {
- this.$toast.error(e, {
- icon: "code",
- })
- }
- },
- async oauthRedirectReq() {
- const tokenInfo = await oauthRedirect()
- if (Object.prototype.hasOwnProperty.call(tokenInfo, "access_token")) {
- this.bearerToken = tokenInfo.access_token
- this.addOAuthToken({
- name: this.accessTokenName,
- value: tokenInfo.access_token,
- })
- }
- },
- addOAuthToken({ name, value }) {
- this.$store.commit("addOAuthToken", {
- name,
- value,
- })
- return false
- },
- removeOAuthToken(index) {
- const oldTokens = this.tokens.slice()
- this.$store.commit("removeOAuthToken", index)
- this.$toast.error(this.$t("deleted"), {
- icon: "delete",
- action: {
- text: this.$t("undo"),
- onClick: (e, toastObject) => {
- this.tokens = oldTokens
- toastObject.remove()
- },
- },
- })
- },
- useOAuthToken(value) {
- this.bearerToken = value
- this.showTokenListModal = false
- },
- addOAuthTokenReq() {
- try {
- const name = this.tokenReqName
- const details = JSON.parse(this.tokenReqDetails)
- this.$store.commit("addOAuthTokenReq", {
- name,
- details,
- })
- this.$toast.info(this.$t("token_request_saved"))
- this.showTokenRequestList = false
- } catch (e) {
- this.$toast.error(e, {
- icon: "code",
- })
- }
- },
- removeOAuthTokenReq(index) {
- const oldTokenReqs = this.tokenReqs.slice()
- const targetReqIndex = this.tokenReqs.findIndex(({ name }) => name === this.tokenReqName)
- if (targetReqIndex < 0) return
- this.$store.commit("removeOAuthTokenReq", targetReqIndex)
- this.$toast.error(this.$t("deleted"), {
- icon: "delete",
- action: {
- text: this.$t("undo"),
- onClick: (e, toastObject) => {
- this.tokenReqs = oldTokenReqs
- toastObject.remove()
- },
- },
- })
- },
- tokenReqChange({ target }) {
- const { details, name } = this.tokenReqs.find(({ name }) => name === target.value)
- const { oidcDiscoveryUrl, authUrl, accessTokenUrl, clientId, scope } = details
- this.tokenReqName = name
- this.oidcDiscoveryUrl = oidcDiscoveryUrl
- this.authUrl = authUrl
- this.accessTokenUrl = accessTokenUrl
- this.clientId = clientId
- this.scope = scope
- },
- setRequestType(val) {
- this.requestType = val
- },
- },
- async mounted() {
- // this.observeRequestButton()
- this._keyListener = function (e) {
- if (e.key === "g" && (e.ctrlKey || e.metaKey)) {
- e.preventDefault()
- if (!this.runningRequest) {
- this.sendRequest()
- } else {
- this.cancelRequest()
- }
- }
- if (e.key === "s" && (e.ctrlKey || e.metaKey)) {
- e.preventDefault()
- this.saveRequest()
- }
- if (e.key === "k" && (e.ctrlKey || e.metaKey)) {
- e.preventDefault()
- this.copyRequest()
- }
- if (e.key === "i" && (e.ctrlKey || e.metaKey)) {
- e.preventDefault()
- this.$refs.clearAll.click()
- }
- if (e.key === "Escape") {
- e.preventDefault()
- this.showCurlImportModal = this.showTokenListModal = this.showTokenRequestList = this.showSaveRequestModal = this.showCodegenModal = false
- }
- if ((e.key === "g" || e.key === "G") && e.altKey) {
- this.method = "GET"
- }
- if ((e.key === "h" || e.key === "H") && e.altKey) {
- this.method = "HEAD"
- }
- if ((e.key === "p" || e.key === "P") && e.altKey) {
- this.method = "POST"
- }
- if ((e.key === "u" || e.key === "U") && e.altKey) {
- this.method = "PUT"
- }
- if ((e.key === "x" || e.key === "X") && e.altKey) {
- this.method = "DELETE"
- }
- if (e.key == "ArrowUp" && e.altKey && this.currentMethodIndex > 0) {
- this.method = this.methodMenuItems[--this.currentMethodIndex % this.methodMenuItems.length]
- } else if (e.key == "ArrowDown" && e.altKey && this.currentMethodIndex < 9) {
- this.method = this.methodMenuItems[++this.currentMethodIndex % this.methodMenuItems.length]
- }
- }
- document.addEventListener("keydown", this._keyListener.bind(this))
- await this.oauthRedirectReq()
- },
- created() {
- this.urlExcludes = this.$store.state.postwoman.settings.URL_EXCLUDES || {
- // Exclude authentication by default for security reasons.
- auth: true,
- httpUser: true,
- httpPassword: true,
- bearerToken: true,
- }
- if (Object.keys(this.$route.query).length) this.setRouteQueries(this.$route.query)
- this.$watch(
- (vm) => [
- vm.name,
- vm.method,
- vm.url,
- vm.auth,
- vm.path,
- vm.httpUser,
- vm.httpPassword,
- vm.bearerToken,
- vm.headers,
- vm.params,
- vm.bodyParams,
- vm.contentType,
- vm.rawParams,
- ],
- (val) => {
- this.setRouteQueryState()
- }
- )
- },
- beforeDestroy() {
- document.removeEventListener("keydown", this._keyListener)
- },
- }
- </script>
|