feat: implement export single collection

This commit is contained in:
Andrew Bastin
2022-02-07 23:42:52 +05:30
parent 85521b4c37
commit 93ccf5dcf6
8 changed files with 269 additions and 29 deletions

View File

@@ -126,11 +126,11 @@
<SmartItem
ref="exportAction"
svg="download"
:label="$t('export.as_json')"
:label="$t('export.export')"
:shortcut="['X']"
@click.native="
() => {
$emit('export-collection')
exportCollection()
options.tippy().hide()
}
"
@@ -265,6 +265,23 @@ export default defineComponent({
},
},
methods: {
exportCollection() {
const collectionJSON = JSON.stringify(this.collection)
const file = new Blob([collectionJSON], { type: "application/json" })
const a = document.createElement("a")
const url = URL.createObjectURL(file)
a.href = url
a.download = `${this.collection.name}.json`
document.body.appendChild(a)
a.click()
this.$toast.success(this.$t("state.download_started").toString())
setTimeout(() => {
document.body.removeChild(a)
URL.revokeObjectURL(url)
}, 1000)
},
toggleShowChildren() {
if (this.$props.saveRequest)
this.$emit("select", {
@@ -284,7 +301,7 @@ export default defineComponent({
collectionID: this.collection.id,
})
},
dropEvent({ dataTransfer }) {
dropEvent({ dataTransfer }: any) {
this.dragging = !this.dragging
const folderPath = dataTransfer.getData("folderPath")
const requestIndex = dataTransfer.getData("requestIndex")

View File

@@ -106,11 +106,11 @@
<SmartItem
ref="exportAction"
svg="download"
:label="$t('export.as_json')"
:label="$t('export.export')"
:shortcut="['X']"
@click.native="
() => {
$emit('export-collection')
exportFolder()
options.tippy().hide()
}
"
@@ -253,6 +253,23 @@ export default defineComponent({
},
},
methods: {
exportFolder() {
const folderJSON = JSON.stringify(this.folder)
const file = new Blob([folderJSON], { type: "application/json" })
const a = document.createElement("a")
const url = URL.createObjectURL(file)
a.href = url
a.download = `${this.folder.name}.json`
document.body.appendChild(a)
a.click()
this.$toast.success(this.$t("state.download_started").toString())
setTimeout(() => {
document.body.removeChild(a)
URL.revokeObjectURL(url)
}, 1000)
},
toggleShowChildren() {
if (this.$props.saveRequest)
this.$emit("select", {

View File

@@ -127,18 +127,10 @@
<SmartItem
ref="exportAction"
svg="download"
:label="$t('export.as_json')"
:label="$t('export.export')"
:shortcut="['X']"
:loading="exportLoading"
@click.native="
() => {
$emit('export-collection')
// TODO: remove the below line
exportLoading = true
// TODO: remove the below line, instead hide the tooltip after finishing export
options.tippy().hide()
}
"
@click.native="exportCollection"
/>
</div>
</tippy>
@@ -230,6 +222,10 @@
<script lang="ts">
import { defineComponent, ref } from "@nuxtjs/composition-api"
import * as E from "fp-ts/Either"
import {
getCompleteCollectionTree,
teamCollToHoppRESTColl,
} from "~/helpers/backend/helpers"
import { moveRESTTeamRequest } from "~/helpers/backend/mutations/TeamRequest"
import { useI18n } from "~/helpers/utils/composables"
@@ -286,7 +282,44 @@ export default defineComponent({
},
},
methods: {
editRequest(event) {
async exportCollection() {
this.exportLoading = true
const result = await getCompleteCollectionTree(this.collection.id)()
if (E.isLeft(result)) {
this.$toast.error(this.$t("error.something_went_wrong").toString())
console.log(result.left)
this.exportLoading = false
this.options.tippy().hide()
return
}
const hoppColl = teamCollToHoppRESTColl(result.right)
const collectionJSON = JSON.stringify(hoppColl)
const file = new Blob([collectionJSON], { type: "application/json" })
const a = document.createElement("a")
const url = URL.createObjectURL(file)
a.href = url
a.download = `${hoppColl.name}.json`
document.body.appendChild(a)
a.click()
this.$toast.success(this.$t("state.download_started").toString())
setTimeout(() => {
document.body.removeChild(a)
URL.revokeObjectURL(url)
}, 1000)
this.exportLoading = false
this.options.tippy().hide()
},
editRequest(event: any) {
this.$emit("edit-request", event)
if (this.$props.saveRequest)
this.$emit("select", {
@@ -315,10 +348,10 @@ export default defineComponent({
collectionID: this.collection.id,
})
},
expandCollection(collectionID) {
expandCollection(collectionID: string) {
this.$emit("expand-collection", collectionID)
},
async dropEvent({ dataTransfer }) {
async dropEvent({ dataTransfer }: any) {
this.dragging = !this.dragging
const requestIndex = dataTransfer.getData("requestIndex")
const moveRequestResult = await moveRESTTeamRequest(
@@ -328,7 +361,7 @@ export default defineComponent({
if (E.isLeft(moveRequestResult))
this.$toast.error(`${this.$t("error.something_went_wrong")}`)
},
removeRequest({ collectionIndex, folderName, requestIndex }) {
removeRequest({ collectionIndex, folderName, requestIndex }: any) {
this.$emit("remove-request", {
collectionIndex,
folderName,

View File

@@ -108,18 +108,10 @@
<SmartItem
ref="exportAction"
svg="download"
:label="$t('export.as_json')"
:label="$t('export.export')"
:shortcut="['X']"
:loading="exportLoading"
@click.native="
() => {
$emit('export-collection')
// TODO: remove the below line
exportLoading = true
// TODO: remove the below line, instead hide the tooltip after finishing export
options.tippy().hide()
}
"
@click.native="exportFolder"
/>
</div>
</tippy>
@@ -211,6 +203,10 @@ import { defineComponent, ref } from "@nuxtjs/composition-api"
import * as E from "fp-ts/Either"
import { runMutation } from "~/helpers/backend/GQLClient"
import { DeleteCollectionDocument } from "~/helpers/backend/graphql"
import {
getCompleteCollectionTree,
teamCollToHoppRESTColl,
} from "~/helpers/backend/helpers"
import { moveRESTTeamRequest } from "~/helpers/backend/mutations/TeamRequest"
export default defineComponent({
@@ -263,6 +259,43 @@ export default defineComponent({
},
},
methods: {
async exportFolder() {
this.exportLoading = true
const result = await getCompleteCollectionTree(this.folder.id)()
if (E.isLeft(result)) {
this.$toast.error(this.$t("error.something_went_wrong").toString())
console.log(result.left)
this.exportLoading = false
this.options.tippy().hide()
return
}
const hoppColl = teamCollToHoppRESTColl(result.right)
const collectionJSON = JSON.stringify(hoppColl)
const file = new Blob([collectionJSON], { type: "application/json" })
const a = document.createElement("a")
const url = URL.createObjectURL(file)
a.href = url
a.download = `${hoppColl.name}.json`
document.body.appendChild(a)
a.click()
this.$toast.success(this.$t("state.download_started").toString())
setTimeout(() => {
document.body.removeChild(a)
URL.revokeObjectURL(url)
}, 1000)
this.exportLoading = false
this.options.tippy().hide()
},
toggleShowChildren() {
if (this.$props.saveRequest)
this.$emit("select", {

View File

@@ -0,0 +1,7 @@
query GetCollectionChildrenIDs($collectionID: ID!, $cursor: String) {
collection(collectionID: $collectionID) {
children(cursor: $cursor) {
id
}
}
}

View File

@@ -0,0 +1,5 @@
query GetCollectionTitle($collectionID: ID!) {
collection(collectionID: $collectionID) {
title
}
}

View File

@@ -0,0 +1,127 @@
import * as A from "fp-ts/Array"
import * as E from "fp-ts/Either"
import * as TE from "fp-ts/TaskEither"
import { pipe, flow } from "fp-ts/function"
import {
HoppCollection,
HoppRESTRequest,
makeCollection,
translateToNewRequest,
} from "@hoppscotch/data"
import { TeamCollection } from "../teams/TeamCollection"
import { TeamRequest } from "../teams/TeamRequest"
import { GQLError, runGQLQuery } from "./GQLClient"
import {
GetCollectionChildrenIDsDocument,
GetCollectionRequestsDocument,
GetCollectionTitleDocument,
} from "./graphql"
const BACKEND_PAGE_SIZE = 10
const getCollectionChildrenIDs = async (collID: string) => {
const collsList: string[] = []
while (true) {
const data = await runGQLQuery({
query: GetCollectionChildrenIDsDocument,
variables: {
collectionID: collID,
cursor:
collsList.length > 0 ? collsList[collsList.length - 1] : undefined,
},
})
if (E.isLeft(data)) {
return E.left(data.left)
}
collsList.push(...data.right.collection!.children.map((x) => x.id))
if (data.right.collection!.children.length !== BACKEND_PAGE_SIZE) break
}
return E.right(collsList)
}
const getCollectionRequests = async (collID: string) => {
const reqList: TeamRequest[] = []
while (true) {
const data = await runGQLQuery({
query: GetCollectionRequestsDocument,
variables: {
collectionID: collID,
cursor: reqList.length > 0 ? reqList[reqList.length - 1].id : undefined,
},
})
if (E.isLeft(data)) {
return E.left(data.left)
}
reqList.push(
...data.right.requestsInCollection.map(
(x) =>
<TeamRequest>{
id: x.id,
request: translateToNewRequest(JSON.parse(x.request)),
collectionID: collID,
title: x.title,
}
)
)
if (data.right.requestsInCollection.length !== BACKEND_PAGE_SIZE) break
}
return E.right(reqList)
}
export const getCompleteCollectionTree = (
collID: string
): TE.TaskEither<GQLError<string>, TeamCollection> =>
pipe(
TE.Do,
TE.bind("title", () =>
pipe(
() =>
runGQLQuery({
query: GetCollectionTitleDocument,
variables: {
collectionID: collID,
},
}),
TE.map((x) => x.collection!.title)
)
),
TE.bind("children", () =>
pipe(
// TaskEither -> () => Promise<Either>
() => getCollectionChildrenIDs(collID),
TE.chain(flow(A.map(getCompleteCollectionTree), TE.sequenceArray))
)
),
TE.bind("requests", () => () => getCollectionRequests(collID)),
TE.map(
({ title, children, requests }) =>
<TeamCollection>{
id: collID,
children,
requests,
title,
}
)
)
export const teamCollToHoppRESTColl = (
coll: TeamCollection
): HoppCollection<HoppRESTRequest> =>
makeCollection({
name: coll.title,
folders: coll.children?.map(teamCollToHoppRESTColl) ?? [],
requests: coll.requests?.map((x) => x.request) ?? [],
})

View File

@@ -187,6 +187,7 @@
"test_script_fail": "Could not execute post-request script"
},
"export": {
"export": "Export",
"as_json": "Export as JSON",
"create_secret_gist": "Create secret Gist",
"gist_created": "Gist created",