RawBody.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <template>
  2. <div class="flex flex-col flex-1">
  3. <div
  4. class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-upperMobileRawStickyFold sm:top-upperMobileRawTertiaryStickyFold"
  5. >
  6. <label class="font-semibold text-secondaryLight">
  7. {{ t("request.raw_body") }}
  8. </label>
  9. <div class="flex">
  10. <ButtonSecondary
  11. v-tippy="{ theme: 'tooltip' }"
  12. to="https://docs.hoppscotch.io/features/body"
  13. blank
  14. :title="t('app.wiki')"
  15. svg="help-circle"
  16. />
  17. <ButtonSecondary
  18. v-tippy="{ theme: 'tooltip' }"
  19. :title="t('state.linewrap')"
  20. :class="{ '!text-accent': linewrapEnabled }"
  21. svg="wrap-text"
  22. @click.native.prevent="linewrapEnabled = !linewrapEnabled"
  23. />
  24. <ButtonSecondary
  25. v-tippy="{ theme: 'tooltip' }"
  26. :title="t('action.clear')"
  27. svg="trash-2"
  28. @click.native="clearContent"
  29. />
  30. <ButtonSecondary
  31. v-if="contentType && contentType.endsWith('json')"
  32. ref="prettifyRequest"
  33. v-tippy="{ theme: 'tooltip' }"
  34. :title="t('action.prettify')"
  35. :svg="prettifyIcon"
  36. @click.native="prettifyRequestBody"
  37. />
  38. <label for="payload">
  39. <ButtonSecondary
  40. v-tippy="{ theme: 'tooltip' }"
  41. :title="t('import.title')"
  42. svg="file-plus"
  43. @click.native="$refs.payload.click()"
  44. />
  45. </label>
  46. <input
  47. ref="payload"
  48. class="input"
  49. name="payload"
  50. type="file"
  51. @change="uploadPayload"
  52. />
  53. </div>
  54. </div>
  55. <div ref="rawBodyParameters" class="flex flex-col flex-1"></div>
  56. </div>
  57. </template>
  58. <script setup lang="ts">
  59. import { computed, reactive, Ref, ref } from "@nuxtjs/composition-api"
  60. import * as TO from "fp-ts/TaskOption"
  61. import { pipe } from "fp-ts/function"
  62. import { HoppRESTReqBody, ValidContentTypes } from "@hoppscotch/data"
  63. import { useCodemirror } from "~/helpers/editor/codemirror"
  64. import { getEditorLangForMimeType } from "~/helpers/editorutils"
  65. import { pluckRef, useI18n, useToast } from "~/helpers/utils/composables"
  66. import { isJSONContentType } from "~/helpers/utils/contenttypes"
  67. import { useRESTRequestBody } from "~/newstore/RESTSession"
  68. import jsonLinter from "~/helpers/editor/linting/json"
  69. import { readFileAsText } from "~/helpers/functional/files"
  70. type PossibleContentTypes = Exclude<
  71. ValidContentTypes,
  72. "multipart/form-data" | "application/x-www-form-urlencoded"
  73. >
  74. const t = useI18n()
  75. const props = defineProps<{
  76. contentType: PossibleContentTypes
  77. }>()
  78. const toast = useToast()
  79. const rawParamsBody = pluckRef(
  80. useRESTRequestBody() as Ref<
  81. HoppRESTReqBody & {
  82. contentType: PossibleContentTypes
  83. }
  84. >,
  85. "body"
  86. )
  87. const prettifyIcon = ref("wand")
  88. const rawInputEditorLang = computed(() =>
  89. getEditorLangForMimeType(props.contentType)
  90. )
  91. const langLinter = computed(() =>
  92. isJSONContentType(props.contentType) ? jsonLinter : null
  93. )
  94. const linewrapEnabled = ref(true)
  95. const rawBodyParameters = ref<any | null>(null)
  96. useCodemirror(
  97. rawBodyParameters,
  98. rawParamsBody,
  99. reactive({
  100. extendedEditorConfig: {
  101. lineWrapping: linewrapEnabled,
  102. mode: rawInputEditorLang,
  103. placeholder: t("request.raw_body").toString(),
  104. },
  105. linter: langLinter,
  106. completer: null,
  107. environmentHighlights: true,
  108. })
  109. )
  110. const clearContent = () => {
  111. rawParamsBody.value = ""
  112. }
  113. const uploadPayload = async (e: InputEvent) => {
  114. await pipe(
  115. (e.target as HTMLInputElement).files?.[0],
  116. TO.of,
  117. TO.chain(TO.fromPredicate((f): f is File => f !== undefined)),
  118. TO.chain(readFileAsText),
  119. TO.matchW(
  120. () => toast.error(`${t("action.choose_file")}`),
  121. (result) => {
  122. rawParamsBody.value = result
  123. toast.success(`${t("state.file_imported")}`)
  124. }
  125. )
  126. )()
  127. }
  128. const prettifyRequestBody = () => {
  129. try {
  130. const jsonObj = JSON.parse(rawParamsBody.value)
  131. rawParamsBody.value = JSON.stringify(jsonObj, null, 2)
  132. prettifyIcon.value = "check"
  133. } catch (e) {
  134. console.error(e)
  135. prettifyIcon.value = "info"
  136. toast.error(`${t("error.json_prettify_invalid_body")}`)
  137. }
  138. setTimeout(() => (prettifyIcon.value = "wand"), 1000)
  139. }
  140. </script>