SaveRequest.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. <template>
  2. <SmartModal
  3. v-if="show"
  4. :title="`${t('collection.save_as')}`"
  5. @close="hideModal"
  6. >
  7. <template #body>
  8. <div class="flex flex-col px-2">
  9. <div class="flex relative">
  10. <input
  11. id="selectLabelSaveReq"
  12. v-model="requestName"
  13. v-focus
  14. class="input floating-input"
  15. placeholder=" "
  16. type="text"
  17. autocomplete="off"
  18. @keyup.enter="saveRequestAs"
  19. />
  20. <label for="selectLabelSaveReq">
  21. {{ t("request.name") }}
  22. </label>
  23. </div>
  24. <label class="p-4">
  25. {{ t("collection.select_location") }}
  26. </label>
  27. <CollectionsGraphql
  28. v-if="mode === 'graphql'"
  29. :doc="false"
  30. :show-coll-actions="false"
  31. :picked="picked"
  32. :saving-mode="true"
  33. @select="onSelect"
  34. />
  35. <Collections
  36. v-else
  37. :picked="picked"
  38. :save-request="true"
  39. @select="onSelect"
  40. @update-collection="updateColl"
  41. @update-coll-type="onUpdateCollType"
  42. />
  43. </div>
  44. </template>
  45. <template #footer>
  46. <span>
  47. <ButtonPrimary
  48. :label="`${t('action.save')}`"
  49. @click.native="saveRequestAs"
  50. />
  51. <ButtonSecondary
  52. :label="`${t('action.cancel')}`"
  53. @click.native="hideModal"
  54. />
  55. </span>
  56. </template>
  57. </SmartModal>
  58. </template>
  59. <script setup lang="ts">
  60. import { reactive, ref, watch } from "@nuxtjs/composition-api"
  61. import { HoppGQLRequest, isHoppRESTRequest } from "@hoppscotch/data"
  62. import {
  63. editGraphqlRequest,
  64. editRESTRequest,
  65. saveGraphqlRequestAs,
  66. saveRESTRequestAs,
  67. } from "~/newstore/collections"
  68. import { getGQLSession, useGQLRequestName } from "~/newstore/GQLSession"
  69. import {
  70. getRESTRequest,
  71. setRESTSaveContext,
  72. useRESTRequestName,
  73. } from "~/newstore/RESTSession"
  74. import * as teamUtils from "~/helpers/teams/utils"
  75. import { apolloClient } from "~/helpers/apollo"
  76. import { useI18n, useToast } from "~/helpers/utils/composables"
  77. const t = useI18n()
  78. type CollectionType =
  79. | {
  80. type: "my-collections"
  81. }
  82. | {
  83. type: "team-collections"
  84. // TODO: Figure this type out
  85. selectedTeam: {
  86. id: string
  87. }
  88. }
  89. type Picked =
  90. | {
  91. pickedType: "my-request"
  92. folderPath: string
  93. requestIndex: number
  94. }
  95. | {
  96. pickedType: "my-folder"
  97. folderPath: string
  98. }
  99. | {
  100. pickedType: "my-collection"
  101. collectionIndex: number
  102. }
  103. | {
  104. pickedType: "teams-request"
  105. requestID: string
  106. }
  107. | {
  108. pickedType: "teams-folder"
  109. folderID: string
  110. }
  111. | {
  112. pickedType: "teams-collection"
  113. collectionID: string
  114. }
  115. | {
  116. pickedType: "gql-my-request"
  117. folderPath: string
  118. requestIndex: number
  119. }
  120. | {
  121. pickedType: "gql-my-folder"
  122. folderPath: string
  123. }
  124. | {
  125. pickedType: "gql-my-collection"
  126. collectionIndex: number
  127. }
  128. const props = defineProps<{
  129. mode: "rest" | "graphql"
  130. show: boolean
  131. }>()
  132. const emit = defineEmits<{
  133. (e: "hide-modal"): void
  134. }>()
  135. const toast = useToast()
  136. // TODO: Use a better implementation with computed ?
  137. // This implementation can't work across updates to mode prop (which won't happen tho)
  138. const requestName =
  139. props.mode === "rest" ? useRESTRequestName() : useGQLRequestName()
  140. const requestData = reactive({
  141. name: requestName,
  142. collectionIndex: undefined as number | undefined,
  143. folderName: undefined as number | undefined,
  144. requestIndex: undefined as number | undefined,
  145. })
  146. const collectionsType = ref<CollectionType>({
  147. type: "my-collections",
  148. })
  149. // TODO: Figure this type out
  150. const picked = ref<Picked | null>(null)
  151. // Resets
  152. watch(
  153. () => requestData.collectionIndex,
  154. () => {
  155. requestData.folderName = undefined
  156. requestData.requestIndex = undefined
  157. }
  158. )
  159. watch(
  160. () => requestData.folderName,
  161. () => {
  162. requestData.requestIndex = undefined
  163. }
  164. )
  165. // All the methods
  166. const onUpdateCollType = (newCollType: CollectionType) => {
  167. collectionsType.value = newCollType
  168. }
  169. const onSelect = ({ picked: pickedVal }: { picked: Picked | null }) => {
  170. picked.value = pickedVal
  171. }
  172. const hideModal = () => {
  173. picked.value = null
  174. emit("hide-modal")
  175. }
  176. const saveRequestAs = async () => {
  177. if (!requestName.value) {
  178. toast.error(`${t("error.empty_req_name")}`)
  179. return
  180. }
  181. if (picked.value === null) {
  182. toast.error(`${t("collection.select")}`)
  183. return
  184. }
  185. const requestUpdated =
  186. props.mode === "rest" ? getRESTRequest() : getGQLSession().request
  187. // // Filter out all REST file inputs
  188. // if (this.mode === "rest" && requestUpdated.bodyParams) {
  189. // requestUpdated.bodyParams = requestUpdated.bodyParams.map((param) =>
  190. // param?.value?.[0] instanceof File ? { ...param, value: "" } : param
  191. // )
  192. // }
  193. if (picked.value.pickedType === "my-request") {
  194. if (!isHoppRESTRequest(requestUpdated))
  195. throw new Error("requestUpdated is not a REST Request")
  196. editRESTRequest(
  197. picked.value.folderPath,
  198. picked.value.requestIndex,
  199. requestUpdated
  200. )
  201. setRESTSaveContext({
  202. originLocation: "user-collection",
  203. folderPath: picked.value.folderPath,
  204. requestIndex: picked.value.requestIndex,
  205. })
  206. requestSaved()
  207. } else if (picked.value.pickedType === "my-folder") {
  208. if (!isHoppRESTRequest(requestUpdated))
  209. throw new Error("requestUpdated is not a REST Request")
  210. const insertionIndex = saveRESTRequestAs(
  211. picked.value.folderPath,
  212. requestUpdated
  213. )
  214. setRESTSaveContext({
  215. originLocation: "user-collection",
  216. folderPath: picked.value.folderPath,
  217. requestIndex: insertionIndex,
  218. })
  219. requestSaved()
  220. } else if (picked.value.pickedType === "my-collection") {
  221. if (!isHoppRESTRequest(requestUpdated))
  222. throw new Error("requestUpdated is not a REST Request")
  223. const insertionIndex = saveRESTRequestAs(
  224. `${picked.value.collectionIndex}`,
  225. requestUpdated
  226. )
  227. setRESTSaveContext({
  228. originLocation: "user-collection",
  229. folderPath: `${picked.value.collectionIndex}`,
  230. requestIndex: insertionIndex,
  231. })
  232. requestSaved()
  233. } else if (picked.value.pickedType === "teams-request") {
  234. if (!isHoppRESTRequest(requestUpdated))
  235. throw new Error("requestUpdated is not a REST Request")
  236. if (collectionsType.value.type !== "team-collections")
  237. throw new Error("Collections Type mismatch")
  238. teamUtils
  239. .overwriteRequestTeams(
  240. apolloClient,
  241. JSON.stringify(requestUpdated),
  242. requestUpdated.name,
  243. picked.value.requestID
  244. )
  245. .then(() => {
  246. requestSaved()
  247. })
  248. .catch((error) => {
  249. toast.error(`${t("profile.no_permission")}`)
  250. throw new Error(error)
  251. })
  252. setRESTSaveContext({
  253. originLocation: "team-collection",
  254. requestID: picked.value.requestID,
  255. })
  256. } else if (picked.value.pickedType === "teams-folder") {
  257. if (!isHoppRESTRequest(requestUpdated))
  258. throw new Error("requestUpdated is not a REST Request")
  259. if (collectionsType.value.type !== "team-collections")
  260. throw new Error("Collections Type mismatch")
  261. try {
  262. const req = await teamUtils.saveRequestAsTeams(
  263. apolloClient,
  264. JSON.stringify(requestUpdated),
  265. requestUpdated.name,
  266. collectionsType.value.selectedTeam.id,
  267. picked.value.folderID
  268. )
  269. if (req && req.id) {
  270. setRESTSaveContext({
  271. originLocation: "team-collection",
  272. requestID: req.id,
  273. teamID: collectionsType.value.selectedTeam.id,
  274. collectionID: picked.value.folderID,
  275. })
  276. }
  277. requestSaved()
  278. } catch (error) {
  279. toast.error(`${t("profile.no_permission")}`)
  280. console.error(error)
  281. }
  282. } else if (picked.value.pickedType === "teams-collection") {
  283. if (!isHoppRESTRequest(requestUpdated))
  284. throw new Error("requestUpdated is not a REST Request")
  285. if (collectionsType.value.type !== "team-collections")
  286. throw new Error("Collections Type mismatch")
  287. try {
  288. const req = await teamUtils.saveRequestAsTeams(
  289. apolloClient,
  290. JSON.stringify(requestUpdated),
  291. requestUpdated.name,
  292. collectionsType.value.selectedTeam.id,
  293. picked.value.collectionID
  294. )
  295. if (req && req.id) {
  296. setRESTSaveContext({
  297. originLocation: "team-collection",
  298. requestID: req.id,
  299. teamID: collectionsType.value.selectedTeam.id,
  300. collectionID: picked.value.collectionID,
  301. })
  302. }
  303. requestSaved()
  304. } catch (error) {
  305. toast.error(`${t("profile.no_permission")}`)
  306. console.error(error)
  307. }
  308. } else if (picked.value.pickedType === "gql-my-request") {
  309. // TODO: Check for GQL request ?
  310. editGraphqlRequest(
  311. picked.value.folderPath,
  312. picked.value.requestIndex,
  313. requestUpdated as HoppGQLRequest
  314. )
  315. requestSaved()
  316. } else if (picked.value.pickedType === "gql-my-folder") {
  317. // TODO: Check for GQL request ?
  318. saveGraphqlRequestAs(
  319. picked.value.folderPath,
  320. requestUpdated as HoppGQLRequest
  321. )
  322. requestSaved()
  323. } else if (picked.value.pickedType === "gql-my-collection") {
  324. // TODO: Check for GQL request ?
  325. saveGraphqlRequestAs(
  326. `${picked.value.collectionIndex}`,
  327. requestUpdated as HoppGQLRequest
  328. )
  329. requestSaved()
  330. }
  331. }
  332. const requestSaved = () => {
  333. toast.success(`${t("request.added")}`)
  334. hideModal()
  335. }
  336. const updateColl = (ev: CollectionType["type"]) => {
  337. collectionsType.value.type = ev
  338. }
  339. </script>