index.vue 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. <template>
  2. <Splitpanes
  3. class="smart-splitter"
  4. :rtl="SIDEBAR_ON_LEFT && windowInnerWidth.x.value >= 768"
  5. :class="{
  6. '!flex-row-reverse': SIDEBAR_ON_LEFT && windowInnerWidth.x.value >= 768,
  7. }"
  8. :horizontal="!(windowInnerWidth.x.value >= 768)"
  9. >
  10. <Pane size="75" min-size="65" class="hide-scrollbar !overflow-auto">
  11. <Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT">
  12. <Pane class="hide-scrollbar !overflow-auto">
  13. <HttpRequest />
  14. <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10">
  15. <SmartTab
  16. :id="'params'"
  17. :label="`${$t('tab.parameters')}`"
  18. :selected="true"
  19. :info="`${newActiveParamsCount$}`"
  20. >
  21. <HttpParameters />
  22. </SmartTab>
  23. <SmartTab :id="'bodyParams'" :label="`${$t('tab.body')}`">
  24. <HttpBody />
  25. </SmartTab>
  26. <SmartTab
  27. :id="'headers'"
  28. :label="`${$t('tab.headers')}`"
  29. :info="`${newActiveHeadersCount$}`"
  30. >
  31. <HttpHeaders />
  32. </SmartTab>
  33. <SmartTab
  34. :id="'authorization'"
  35. :label="`${$t('tab.authorization')}`"
  36. >
  37. <HttpAuthorization />
  38. </SmartTab>
  39. <SmartTab
  40. :id="'preRequestScript'"
  41. :label="`${$t('tab.pre_request_script')}`"
  42. >
  43. <HttpPreRequestScript />
  44. </SmartTab>
  45. <SmartTab :id="'tests'" :label="`${$t('tab.tests')}`">
  46. <HttpTests />
  47. </SmartTab>
  48. </SmartTabs>
  49. </Pane>
  50. <Pane class="hide-scrollbar !overflow-auto flex flex-col">
  51. <HttpResponse ref="response" />
  52. </Pane>
  53. </Splitpanes>
  54. </Pane>
  55. <Pane
  56. v-if="SIDEBAR"
  57. size="25"
  58. min-size="20"
  59. class="hide-scrollbar !overflow-auto"
  60. >
  61. <SmartTabs styles="sticky bg-primary z-10 top-0" vertical>
  62. <SmartTab
  63. :id="'history'"
  64. icon="clock"
  65. :label="`${$t('tab.history')}`"
  66. :selected="true"
  67. >
  68. <History ref="historyComponent" :page="'rest'" />
  69. </SmartTab>
  70. <SmartTab
  71. :id="'collections'"
  72. icon="folder"
  73. :label="`${$t('tab.collections')}`"
  74. >
  75. <Collections />
  76. </SmartTab>
  77. <SmartTab
  78. :id="'env'"
  79. icon="layers"
  80. :label="`${$t('environment.title')}`"
  81. >
  82. <Environments />
  83. </SmartTab>
  84. </SmartTabs>
  85. </Pane>
  86. <SmartConfirmModal
  87. :show="confirmSync"
  88. :title="`${$t('confirm.sync')}`"
  89. @hide-modal="confirmSync = false"
  90. @resolve="syncRequest"
  91. />
  92. </Splitpanes>
  93. </template>
  94. <script lang="ts">
  95. import {
  96. computed,
  97. defineComponent,
  98. onBeforeMount,
  99. onBeforeUnmount,
  100. onMounted,
  101. Ref,
  102. ref,
  103. useContext,
  104. watch,
  105. } from "@nuxtjs/composition-api"
  106. import { Splitpanes, Pane } from "splitpanes"
  107. import "splitpanes/dist/splitpanes.css"
  108. import { map } from "rxjs/operators"
  109. import { Subscription } from "rxjs"
  110. import isEqual from "lodash/isEqual"
  111. import { useSetting } from "~/newstore/settings"
  112. import {
  113. restRequest$,
  114. restActiveParamsCount$,
  115. restActiveHeadersCount$,
  116. getRESTRequest,
  117. setRESTRequest,
  118. setRESTAuth,
  119. restAuth$,
  120. } from "~/newstore/RESTSession"
  121. import { translateExtURLParams } from "~/helpers/RESTExtURLParams"
  122. import {
  123. pluckRef,
  124. useReadonlyStream,
  125. useStream,
  126. } from "~/helpers/utils/composables"
  127. import { loadRequestFromSync, startRequestSync } from "~/helpers/fb/request"
  128. import { onLoggedIn } from "~/helpers/fb/auth"
  129. import {
  130. FormDataKeyValue,
  131. HoppRESTRequest,
  132. } from "~/helpers/types/HoppRESTRequest"
  133. import { oauthRedirect } from "~/helpers/oauth"
  134. import { HoppRESTAuthOAuth2 } from "~/helpers/types/HoppRESTAuth"
  135. import useWindowSize from "~/helpers/utils/useWindowSize"
  136. function bindRequestToURLParams() {
  137. const {
  138. route,
  139. app: { router },
  140. } = useContext()
  141. const request = useStream(restRequest$, getRESTRequest(), setRESTRequest)
  142. // Process headers and params to proper values
  143. const headers = computed(() => {
  144. const filtered = request.value.headers.filter((x) => x.key !== "")
  145. return filtered.length > 0 ? JSON.stringify(filtered) : null
  146. })
  147. const params = computed(() => {
  148. const filtered = request.value.params.filter((x) => x.key !== "")
  149. return filtered.length > 0 ? JSON.stringify(filtered) : null
  150. })
  151. const body = computed(() => {
  152. const contentType = request.value.body.contentType
  153. if (contentType === "multipart/form-data") {
  154. const body = request.value.body.body as FormDataKeyValue[]
  155. const filtered = body.filter((x) => x.key !== "")
  156. return JSON.stringify({ body: filtered, contentType })
  157. }
  158. return JSON.stringify({ body: request.value.body.body, contentType })
  159. })
  160. // Combine them together to a cleaner value
  161. const urlParams = computed(() => ({
  162. v: request.value.v,
  163. method: request.value.method,
  164. endpoint: request.value.endpoint,
  165. headers: headers.value,
  166. params: params.value,
  167. body: body.value,
  168. }))
  169. // Watch and update accordingly
  170. watch(urlParams, () => {
  171. history.replaceState(
  172. window.location.href,
  173. "",
  174. `${router!.options.base}?${encodeURI(
  175. Object.entries(urlParams.value)
  176. .filter((x) => x[1] !== null)
  177. .map((x) => `${x[0]}=${x[1]!}`)
  178. .join("&")
  179. )}`
  180. )
  181. })
  182. // Now, we have to see the initial URL param and set that as the request
  183. onMounted(() => {
  184. const query = route.value.query
  185. // If query params are empty, or contains code or error param (these are from Oauth Redirect)
  186. // We skip URL params parsing
  187. if (Object.keys(query).length === 0 || query.code || query.error) return
  188. setRESTRequest(translateExtURLParams(query))
  189. })
  190. }
  191. function oAuthURL() {
  192. const auth = useStream(
  193. restAuth$,
  194. { authType: "none", authActive: true },
  195. setRESTAuth
  196. )
  197. const oauth2Token = pluckRef(auth as Ref<HoppRESTAuthOAuth2>, "token")
  198. onBeforeMount(async () => {
  199. const tokenInfo = await oauthRedirect()
  200. if (Object.prototype.hasOwnProperty.call(tokenInfo, "access_token")) {
  201. if (typeof tokenInfo === "object") {
  202. oauth2Token.value = tokenInfo.access_token
  203. }
  204. }
  205. })
  206. }
  207. function setupRequestSync(
  208. confirmSync: Ref<boolean>,
  209. requestForSync: Ref<HoppRESTRequest | null>
  210. ) {
  211. const { route } = useContext()
  212. // Subscription to request sync
  213. let sub: Subscription | null = null
  214. // Load request on login resolve and start sync
  215. onLoggedIn(async () => {
  216. if (
  217. Object.keys(route.value.query).length === 0 &&
  218. !(route.value.query.code || route.value.query.error)
  219. ) {
  220. const request = await loadRequestFromSync()
  221. if (request) {
  222. // setRESTRequest(request)
  223. if (!isEqual(request, getRESTRequest())) {
  224. requestForSync.value = request
  225. confirmSync.value = true
  226. }
  227. }
  228. }
  229. sub = startRequestSync()
  230. })
  231. // Stop subscripton to stop syncing
  232. onBeforeUnmount(() => {
  233. sub?.unsubscribe()
  234. })
  235. }
  236. export default defineComponent({
  237. components: { Splitpanes, Pane },
  238. setup() {
  239. const requestForSync = ref<HoppRESTRequest | null>(null)
  240. const confirmSync = ref(false)
  241. const syncRequest = () => {
  242. setRESTRequest(requestForSync.value!)
  243. }
  244. setupRequestSync(confirmSync, requestForSync)
  245. bindRequestToURLParams()
  246. return {
  247. windowInnerWidth: useWindowSize(),
  248. newActiveParamsCount$: useReadonlyStream(
  249. restActiveParamsCount$.pipe(
  250. map((e) => {
  251. if (e === 0) return null
  252. return `${e}`
  253. })
  254. ),
  255. null
  256. ),
  257. newActiveHeadersCount$: useReadonlyStream(
  258. restActiveHeadersCount$.pipe(
  259. map((e) => {
  260. if (e === 0) return null
  261. return `${e}`
  262. })
  263. ),
  264. null
  265. ),
  266. SIDEBAR: useSetting("SIDEBAR"),
  267. COLUMN_LAYOUT: useSetting("COLUMN_LAYOUT"),
  268. SIDEBAR_ON_LEFT: useSetting("SIDEBAR_ON_LEFT"),
  269. confirmSync,
  270. syncRequest,
  271. oAuthURL,
  272. requestForSync,
  273. }
  274. },
  275. })
  276. </script>