collections.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939
  1. import { pluck } from "rxjs/operators"
  2. import {
  3. HoppGQLRequest,
  4. translateToGQLRequest,
  5. HoppRESTRequest,
  6. translateToNewRequest,
  7. } from "@hoppscotch/data"
  8. import DispatchingStore, { defineDispatchers } from "./DispatchingStore"
  9. import { getRESTSaveContext, setRESTSaveContext } from "./RESTSession"
  10. export interface Collection<T extends HoppRESTRequest | HoppGQLRequest> {
  11. v: number
  12. name: string
  13. folders: Collection<T>[]
  14. requests: T[]
  15. id?: string // For Firestore ID
  16. }
  17. const defaultRESTCollectionState = {
  18. state: [
  19. makeCollection<HoppRESTRequest>({
  20. name: "My Collection",
  21. folders: [],
  22. requests: [],
  23. }),
  24. ],
  25. }
  26. const defaultGraphqlCollectionState = {
  27. state: [
  28. makeCollection<HoppGQLRequest>({
  29. name: "My GraphQL Collection",
  30. folders: [],
  31. requests: [],
  32. }),
  33. ],
  34. }
  35. export function makeCollection<T extends HoppRESTRequest | HoppGQLRequest>(
  36. x: Omit<Collection<T>, "v">
  37. ): Collection<T> {
  38. return {
  39. v: 1,
  40. ...x,
  41. }
  42. }
  43. export function translateToNewRESTCollection(
  44. x: any
  45. ): Collection<HoppRESTRequest> {
  46. if (x.v && x.v === 1) return x
  47. // Legacy
  48. const name = x.name ?? "Untitled"
  49. const folders = (x.folders ?? []).map(translateToNewRESTCollection)
  50. const requests = (x.requests ?? []).map(translateToNewRequest)
  51. const obj = makeCollection<HoppRESTRequest>({
  52. name,
  53. folders,
  54. requests,
  55. })
  56. if (x.id) obj.id = x.id
  57. return obj
  58. }
  59. export function translateToNewGQLCollection(
  60. x: any
  61. ): Collection<HoppGQLRequest> {
  62. if (x.v && x.v === 1) return x
  63. // Legacy
  64. const name = x.name ?? "Untitled"
  65. const folders = (x.folders ?? []).map(translateToNewGQLCollection)
  66. const requests = (x.requests ?? []).map(translateToGQLRequest)
  67. const obj = makeCollection<HoppGQLRequest>({
  68. name,
  69. folders,
  70. requests,
  71. })
  72. if (x.id) obj.id = x.id
  73. return obj
  74. }
  75. type RESTCollectionStoreType = typeof defaultRESTCollectionState
  76. type GraphqlCollectionStoreType = typeof defaultGraphqlCollectionState
  77. function navigateToFolderWithIndexPath(
  78. collections: Collection<HoppRESTRequest | HoppGQLRequest>[],
  79. indexPaths: number[]
  80. ) {
  81. if (indexPaths.length === 0) return null
  82. let target = collections[indexPaths.shift() as number]
  83. while (indexPaths.length > 0)
  84. target = target.folders[indexPaths.shift() as number]
  85. return target !== undefined ? target : null
  86. }
  87. const restCollectionDispatchers = defineDispatchers({
  88. setCollections(
  89. _: RESTCollectionStoreType,
  90. { entries }: { entries: Collection<HoppRESTRequest>[] }
  91. ) {
  92. return {
  93. state: entries,
  94. }
  95. },
  96. appendCollections(
  97. { state }: RESTCollectionStoreType,
  98. { entries }: { entries: Collection<HoppRESTRequest>[] }
  99. ) {
  100. return {
  101. state: [...state, ...entries],
  102. }
  103. },
  104. addCollection(
  105. { state }: RESTCollectionStoreType,
  106. { collection }: { collection: Collection<any> }
  107. ) {
  108. return {
  109. state: [...state, collection],
  110. }
  111. },
  112. removeCollection(
  113. { state }: RESTCollectionStoreType,
  114. { collectionIndex }: { collectionIndex: number }
  115. ) {
  116. return {
  117. state: (state as any).filter(
  118. (_: any, i: number) => i !== collectionIndex
  119. ),
  120. }
  121. },
  122. editCollection(
  123. { state }: RESTCollectionStoreType,
  124. {
  125. collectionIndex,
  126. collection,
  127. }: { collectionIndex: number; collection: Collection<any> }
  128. ) {
  129. return {
  130. state: state.map((col, index) =>
  131. index === collectionIndex ? collection : col
  132. ),
  133. }
  134. },
  135. addFolder(
  136. { state }: RESTCollectionStoreType,
  137. { name, path }: { name: string; path: string }
  138. ) {
  139. const newFolder: Collection<HoppRESTRequest> = makeCollection({
  140. name,
  141. folders: [],
  142. requests: [],
  143. })
  144. const newState = state
  145. const indexPaths = path.split("/").map((x) => parseInt(x))
  146. const target = navigateToFolderWithIndexPath(newState, indexPaths)
  147. if (target === null) {
  148. console.log(`Could not parse path '${path}'. Ignoring add folder request`)
  149. return {}
  150. }
  151. target.folders.push(newFolder)
  152. return {
  153. state: newState,
  154. }
  155. },
  156. editFolder(
  157. { state }: RESTCollectionStoreType,
  158. { path, folder }: { path: string; folder: string }
  159. ) {
  160. const newState = state
  161. const indexPaths = path.split("/").map((x) => parseInt(x))
  162. const target = navigateToFolderWithIndexPath(newState, indexPaths)
  163. if (target === null) {
  164. console.log(
  165. `Could not parse path '${path}'. Ignoring edit folder request`
  166. )
  167. return {}
  168. }
  169. Object.assign(target, folder)
  170. return {
  171. state: newState,
  172. }
  173. },
  174. removeFolder({ state }: RESTCollectionStoreType, { path }: { path: string }) {
  175. const newState = state
  176. const indexPaths = path.split("/").map((x) => parseInt(x))
  177. if (indexPaths.length === 0) {
  178. console.log(
  179. "Given path too short. If this is a collection, use removeCollection dispatcher instead. Skipping request."
  180. )
  181. return {}
  182. }
  183. // We get the index path to the folder itself,
  184. // we have to find the folder containing the target folder,
  185. // so we pop the last path index
  186. const folderIndex = indexPaths.pop() as number
  187. const containingFolder = navigateToFolderWithIndexPath(newState, indexPaths)
  188. if (containingFolder === null) {
  189. console.log(
  190. `Could not resolve path '${path}'. Skipping removeFolder dispatch.`
  191. )
  192. return {}
  193. }
  194. containingFolder.folders.splice(folderIndex, 1)
  195. return {
  196. state: newState,
  197. }
  198. },
  199. editRequest(
  200. { state }: RESTCollectionStoreType,
  201. {
  202. path,
  203. requestIndex,
  204. requestNew,
  205. }: { path: string; requestIndex: number; requestNew: any }
  206. ) {
  207. const newState = state
  208. const indexPaths = path.split("/").map((x) => parseInt(x))
  209. const targetLocation = navigateToFolderWithIndexPath(newState, indexPaths)
  210. if (targetLocation === null) {
  211. console.log(
  212. `Could not resolve path '${path}'. Ignoring editRequest dispatch.`
  213. )
  214. return {}
  215. }
  216. targetLocation.requests = targetLocation.requests.map((req, index) =>
  217. index !== requestIndex ? req : requestNew
  218. )
  219. return {
  220. state: newState,
  221. }
  222. },
  223. saveRequestAs(
  224. { state }: RESTCollectionStoreType,
  225. { path, request }: { path: string; request: any }
  226. ) {
  227. const newState = state
  228. const indexPaths = path.split("/").map((x) => parseInt(x))
  229. const targetLocation = navigateToFolderWithIndexPath(newState, indexPaths)
  230. if (targetLocation === null) {
  231. console.log(
  232. `Could not resolve path '${path}'. Ignoring saveRequestAs dispatch.`
  233. )
  234. return {}
  235. }
  236. targetLocation.requests.push(request)
  237. return {
  238. state: newState,
  239. }
  240. },
  241. removeRequest(
  242. { state }: RESTCollectionStoreType,
  243. { path, requestIndex }: { path: string; requestIndex: number }
  244. ) {
  245. const newState = state
  246. const indexPaths = path.split("/").map((x) => parseInt(x))
  247. const targetLocation = navigateToFolderWithIndexPath(newState, indexPaths)
  248. if (targetLocation === null) {
  249. console.log(
  250. `Could not resolve path '${path}'. Ignoring removeRequest dispatch.`
  251. )
  252. return {}
  253. }
  254. targetLocation.requests.splice(requestIndex, 1)
  255. // If the save context is set and is set to the same source, we invalidate it
  256. const saveCtx = getRESTSaveContext()
  257. if (
  258. saveCtx?.originLocation === "user-collection" &&
  259. saveCtx.folderPath === path &&
  260. saveCtx.requestIndex === requestIndex
  261. ) {
  262. setRESTSaveContext(null)
  263. }
  264. return {
  265. state: newState,
  266. }
  267. },
  268. moveRequest(
  269. { state }: RESTCollectionStoreType,
  270. {
  271. path,
  272. requestIndex,
  273. destinationPath,
  274. }: { path: string; requestIndex: number; destinationPath: string }
  275. ) {
  276. const newState = state
  277. const indexPaths = path.split("/").map((x) => parseInt(x))
  278. const targetLocation = navigateToFolderWithIndexPath(newState, indexPaths)
  279. if (targetLocation === null) {
  280. console.log(
  281. `Could not resolve source path '${path}'. Skipping moveRequest dispatch.`
  282. )
  283. return {}
  284. }
  285. const req = targetLocation.requests[requestIndex]
  286. const destIndexPaths = destinationPath.split("/").map((x) => parseInt(x))
  287. const destLocation = navigateToFolderWithIndexPath(newState, destIndexPaths)
  288. if (destLocation === null) {
  289. console.log(
  290. `Could not resolve destination path '${destinationPath}'. Skipping moveRequest dispatch.`
  291. )
  292. return {}
  293. }
  294. destLocation.requests.push(req)
  295. targetLocation.requests.splice(requestIndex, 1)
  296. return {
  297. state: newState,
  298. }
  299. },
  300. })
  301. const gqlCollectionDispatchers = defineDispatchers({
  302. setCollections(
  303. _: GraphqlCollectionStoreType,
  304. { entries }: { entries: Collection<any>[] }
  305. ) {
  306. return {
  307. state: entries,
  308. }
  309. },
  310. appendCollections(
  311. { state }: GraphqlCollectionStoreType,
  312. { entries }: { entries: Collection<any>[] }
  313. ) {
  314. return {
  315. state: [...state, ...entries],
  316. }
  317. },
  318. addCollection(
  319. { state }: GraphqlCollectionStoreType,
  320. { collection }: { collection: Collection<any> }
  321. ) {
  322. return {
  323. state: [...state, collection],
  324. }
  325. },
  326. removeCollection(
  327. { state }: GraphqlCollectionStoreType,
  328. { collectionIndex }: { collectionIndex: number }
  329. ) {
  330. return {
  331. state: (state as any).filter(
  332. (_: any, i: number) => i !== collectionIndex
  333. ),
  334. }
  335. },
  336. editCollection(
  337. { state }: GraphqlCollectionStoreType,
  338. {
  339. collectionIndex,
  340. collection,
  341. }: { collectionIndex: number; collection: Collection<any> }
  342. ) {
  343. return {
  344. state: state.map((col, index) =>
  345. index === collectionIndex ? collection : col
  346. ),
  347. }
  348. },
  349. addFolder(
  350. { state }: GraphqlCollectionStoreType,
  351. { name, path }: { name: string; path: string }
  352. ) {
  353. const newFolder: Collection<HoppGQLRequest> = makeCollection({
  354. name,
  355. folders: [],
  356. requests: [],
  357. })
  358. const newState = state
  359. const indexPaths = path.split("/").map((x) => parseInt(x))
  360. const target = navigateToFolderWithIndexPath(newState, indexPaths)
  361. if (target === null) {
  362. console.log(`Could not parse path '${path}'. Ignoring add folder request`)
  363. return {}
  364. }
  365. target.folders.push(newFolder)
  366. return {
  367. state: newState,
  368. }
  369. },
  370. editFolder(
  371. { state }: GraphqlCollectionStoreType,
  372. { path, folder }: { path: string; folder: string }
  373. ) {
  374. const newState = state
  375. const indexPaths = path.split("/").map((x) => parseInt(x))
  376. const target = navigateToFolderWithIndexPath(newState, indexPaths)
  377. if (target === null) {
  378. console.log(
  379. `Could not parse path '${path}'. Ignoring edit folder request`
  380. )
  381. return {}
  382. }
  383. Object.assign(target, folder)
  384. return {
  385. state: newState,
  386. }
  387. },
  388. removeFolder(
  389. { state }: GraphqlCollectionStoreType,
  390. { path }: { path: string }
  391. ) {
  392. const newState = state
  393. const indexPaths = path.split("/").map((x) => parseInt(x))
  394. if (indexPaths.length === 0) {
  395. console.log(
  396. "Given path too short. If this is a collection, use removeCollection dispatcher instead. Skipping request."
  397. )
  398. return {}
  399. }
  400. // We get the index path to the folder itself,
  401. // we have to find the folder containing the target folder,
  402. // so we pop the last path index
  403. const folderIndex = indexPaths.pop() as number
  404. const containingFolder = navigateToFolderWithIndexPath(newState, indexPaths)
  405. if (containingFolder === null) {
  406. console.log(
  407. `Could not resolve path '${path}'. Skipping removeFolder dispatch.`
  408. )
  409. return {}
  410. }
  411. containingFolder.folders.splice(folderIndex, 1)
  412. return {
  413. state: newState,
  414. }
  415. },
  416. editRequest(
  417. { state }: GraphqlCollectionStoreType,
  418. {
  419. path,
  420. requestIndex,
  421. requestNew,
  422. }: { path: string; requestIndex: number; requestNew: any }
  423. ) {
  424. const newState = state
  425. const indexPaths = path.split("/").map((x) => parseInt(x))
  426. const targetLocation = navigateToFolderWithIndexPath(newState, indexPaths)
  427. if (targetLocation === null) {
  428. console.log(
  429. `Could not resolve path '${path}'. Ignoring editRequest dispatch.`
  430. )
  431. return {}
  432. }
  433. targetLocation.requests = targetLocation.requests.map((req, index) =>
  434. index !== requestIndex ? req : requestNew
  435. )
  436. return {
  437. state: newState,
  438. }
  439. },
  440. saveRequestAs(
  441. { state }: GraphqlCollectionStoreType,
  442. { path, request }: { path: string; request: any }
  443. ) {
  444. const newState = state
  445. const indexPaths = path.split("/").map((x) => parseInt(x))
  446. const targetLocation = navigateToFolderWithIndexPath(newState, indexPaths)
  447. if (targetLocation === null) {
  448. console.log(
  449. `Could not resolve path '${path}'. Ignoring saveRequestAs dispatch.`
  450. )
  451. return {}
  452. }
  453. targetLocation.requests.push(request)
  454. return {
  455. state: newState,
  456. }
  457. },
  458. removeRequest(
  459. { state }: GraphqlCollectionStoreType,
  460. { path, requestIndex }: { path: string; requestIndex: number }
  461. ) {
  462. const newState = state
  463. const indexPaths = path.split("/").map((x) => parseInt(x))
  464. const targetLocation = navigateToFolderWithIndexPath(newState, indexPaths)
  465. if (targetLocation === null) {
  466. console.log(
  467. `Could not resolve path '${path}'. Ignoring removeRequest dispatch.`
  468. )
  469. return {}
  470. }
  471. targetLocation.requests.splice(requestIndex, 1)
  472. // If the save context is set and is set to the same source, we invalidate it
  473. const saveCtx = getRESTSaveContext()
  474. if (
  475. saveCtx?.originLocation === "user-collection" &&
  476. saveCtx.folderPath === path &&
  477. saveCtx.requestIndex === requestIndex
  478. ) {
  479. setRESTSaveContext(null)
  480. }
  481. return {
  482. state: newState,
  483. }
  484. },
  485. moveRequest(
  486. { state }: GraphqlCollectionStoreType,
  487. {
  488. path,
  489. requestIndex,
  490. destinationPath,
  491. }: { path: string; requestIndex: number; destinationPath: string }
  492. ) {
  493. const newState = state
  494. const indexPaths = path.split("/").map((x) => parseInt(x))
  495. const targetLocation = navigateToFolderWithIndexPath(newState, indexPaths)
  496. if (targetLocation === null) {
  497. console.log(
  498. `Could not resolve source path '${path}'. Skipping moveRequest dispatch.`
  499. )
  500. return {}
  501. }
  502. const req = targetLocation.requests[requestIndex]
  503. const destIndexPaths = destinationPath.split("/").map((x) => parseInt(x))
  504. const destLocation = navigateToFolderWithIndexPath(newState, destIndexPaths)
  505. if (destLocation === null) {
  506. console.log(
  507. `Could not resolve destination path '${destinationPath}'. Skipping moveRequest dispatch.`
  508. )
  509. return {}
  510. }
  511. destLocation.requests.push(req)
  512. targetLocation.requests.splice(requestIndex, 1)
  513. return {
  514. state: newState,
  515. }
  516. },
  517. })
  518. export const restCollectionStore = new DispatchingStore(
  519. defaultRESTCollectionState,
  520. restCollectionDispatchers
  521. )
  522. export const graphqlCollectionStore = new DispatchingStore(
  523. defaultGraphqlCollectionState,
  524. gqlCollectionDispatchers
  525. )
  526. export function setRESTCollections(entries: Collection<HoppRESTRequest>[]) {
  527. restCollectionStore.dispatch({
  528. dispatcher: "setCollections",
  529. payload: {
  530. entries,
  531. },
  532. })
  533. }
  534. export const restCollections$ = restCollectionStore.subject$.pipe(
  535. pluck("state")
  536. )
  537. export const graphqlCollections$ = graphqlCollectionStore.subject$.pipe(
  538. pluck("state")
  539. )
  540. export function appendRESTCollections(entries: Collection<HoppRESTRequest>[]) {
  541. restCollectionStore.dispatch({
  542. dispatcher: "appendCollections",
  543. payload: {
  544. entries,
  545. },
  546. })
  547. }
  548. export function addRESTCollection(collection: Collection<HoppRESTRequest>) {
  549. restCollectionStore.dispatch({
  550. dispatcher: "addCollection",
  551. payload: {
  552. collection,
  553. },
  554. })
  555. }
  556. export function removeRESTCollection(collectionIndex: number) {
  557. restCollectionStore.dispatch({
  558. dispatcher: "removeCollection",
  559. payload: {
  560. collectionIndex,
  561. },
  562. })
  563. }
  564. export function editRESTCollection(
  565. collectionIndex: number,
  566. collection: Collection<HoppRESTRequest>
  567. ) {
  568. restCollectionStore.dispatch({
  569. dispatcher: "editCollection",
  570. payload: {
  571. collectionIndex,
  572. collection,
  573. },
  574. })
  575. }
  576. export function addRESTFolder(name: string, path: string) {
  577. restCollectionStore.dispatch({
  578. dispatcher: "addFolder",
  579. payload: {
  580. name,
  581. path,
  582. },
  583. })
  584. }
  585. export function editRESTFolder(
  586. path: string,
  587. folder: Collection<HoppRESTRequest>
  588. ) {
  589. restCollectionStore.dispatch({
  590. dispatcher: "editFolder",
  591. payload: {
  592. path,
  593. folder,
  594. },
  595. })
  596. }
  597. export function removeRESTFolder(path: string) {
  598. restCollectionStore.dispatch({
  599. dispatcher: "removeFolder",
  600. payload: {
  601. path,
  602. },
  603. })
  604. }
  605. export function editRESTRequest(
  606. path: string,
  607. requestIndex: number,
  608. requestNew: HoppRESTRequest
  609. ) {
  610. const indexPaths = path.split("/").map((x) => parseInt(x))
  611. if (
  612. !navigateToFolderWithIndexPath(restCollectionStore.value.state, indexPaths)
  613. )
  614. throw new Error("Path not found")
  615. restCollectionStore.dispatch({
  616. dispatcher: "editRequest",
  617. payload: {
  618. path,
  619. requestIndex,
  620. requestNew,
  621. },
  622. })
  623. }
  624. export function saveRESTRequestAs(path: string, request: HoppRESTRequest) {
  625. // For calculating the insertion request index
  626. const targetLocation = navigateToFolderWithIndexPath(
  627. restCollectionStore.value.state,
  628. path.split("/").map((x) => parseInt(x))
  629. )
  630. const insertionIndex = targetLocation!.requests.length
  631. restCollectionStore.dispatch({
  632. dispatcher: "saveRequestAs",
  633. payload: {
  634. path,
  635. request,
  636. },
  637. })
  638. return insertionIndex
  639. }
  640. export function removeRESTRequest(path: string, requestIndex: number) {
  641. restCollectionStore.dispatch({
  642. dispatcher: "removeRequest",
  643. payload: {
  644. path,
  645. requestIndex,
  646. },
  647. })
  648. }
  649. export function moveRESTRequest(
  650. path: string,
  651. requestIndex: number,
  652. destinationPath: string
  653. ) {
  654. restCollectionStore.dispatch({
  655. dispatcher: "moveRequest",
  656. payload: {
  657. path,
  658. requestIndex,
  659. destinationPath,
  660. },
  661. })
  662. }
  663. export function setGraphqlCollections(entries: Collection<HoppGQLRequest>[]) {
  664. graphqlCollectionStore.dispatch({
  665. dispatcher: "setCollections",
  666. payload: {
  667. entries,
  668. },
  669. })
  670. }
  671. export function appendGraphqlCollections(
  672. entries: Collection<HoppGQLRequest>[]
  673. ) {
  674. graphqlCollectionStore.dispatch({
  675. dispatcher: "appendCollections",
  676. payload: {
  677. entries,
  678. },
  679. })
  680. }
  681. export function addGraphqlCollection(collection: Collection<HoppGQLRequest>) {
  682. graphqlCollectionStore.dispatch({
  683. dispatcher: "addCollection",
  684. payload: {
  685. collection,
  686. },
  687. })
  688. }
  689. export function removeGraphqlCollection(collectionIndex: number) {
  690. graphqlCollectionStore.dispatch({
  691. dispatcher: "removeCollection",
  692. payload: {
  693. collectionIndex,
  694. },
  695. })
  696. }
  697. export function editGraphqlCollection(
  698. collectionIndex: number,
  699. collection: Collection<HoppGQLRequest>
  700. ) {
  701. graphqlCollectionStore.dispatch({
  702. dispatcher: "editCollection",
  703. payload: {
  704. collectionIndex,
  705. collection,
  706. },
  707. })
  708. }
  709. export function addGraphqlFolder(name: string, path: string) {
  710. graphqlCollectionStore.dispatch({
  711. dispatcher: "addFolder",
  712. payload: {
  713. name,
  714. path,
  715. },
  716. })
  717. }
  718. export function editGraphqlFolder(
  719. path: string,
  720. folder: Collection<HoppGQLRequest>
  721. ) {
  722. graphqlCollectionStore.dispatch({
  723. dispatcher: "editFolder",
  724. payload: {
  725. path,
  726. folder,
  727. },
  728. })
  729. }
  730. export function removeGraphqlFolder(path: string) {
  731. graphqlCollectionStore.dispatch({
  732. dispatcher: "removeFolder",
  733. payload: {
  734. path,
  735. },
  736. })
  737. }
  738. export function editGraphqlRequest(
  739. path: string,
  740. requestIndex: number,
  741. requestNew: HoppGQLRequest
  742. ) {
  743. graphqlCollectionStore.dispatch({
  744. dispatcher: "editRequest",
  745. payload: {
  746. path,
  747. requestIndex,
  748. requestNew,
  749. },
  750. })
  751. }
  752. export function saveGraphqlRequestAs(path: string, request: HoppGQLRequest) {
  753. graphqlCollectionStore.dispatch({
  754. dispatcher: "saveRequestAs",
  755. payload: {
  756. path,
  757. request,
  758. },
  759. })
  760. }
  761. export function removeGraphqlRequest(path: string, requestIndex: number) {
  762. graphqlCollectionStore.dispatch({
  763. dispatcher: "removeRequest",
  764. payload: {
  765. path,
  766. requestIndex,
  767. },
  768. })
  769. }
  770. export function moveGraphqlRequest(
  771. path: string,
  772. requestIndex: number,
  773. destinationPath: string
  774. ) {
  775. graphqlCollectionStore.dispatch({
  776. dispatcher: "moveRequest",
  777. payload: {
  778. path,
  779. requestIndex,
  780. destinationPath,
  781. },
  782. })
  783. }