feat: convert json to interfaces (#3566)
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com> Co-authored-by: Liyas Thomas <liyascthomas@gmail.com> Co-authored-by: nivedin <nivedinp@gmail.com>
This commit is contained in:
committed by
GitHub
parent
1cc845e17d
commit
bb4b640e58
@@ -78,6 +78,7 @@
|
||||
"contact_us": "Contact us",
|
||||
"cookies": "Cookies",
|
||||
"copy": "Copy",
|
||||
"copy_interface_type": "Copy interface type",
|
||||
"copy_user_id": "Copy User Auth Token",
|
||||
"developer_option": "Developer options",
|
||||
"developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.",
|
||||
@@ -780,6 +781,7 @@
|
||||
"connection_failed": "Connection failed",
|
||||
"connection_lost": "Connection lost",
|
||||
"copied_to_clipboard": "Copied to clipboard",
|
||||
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
|
||||
"deleted": "Deleted",
|
||||
"deprecated": "DEPRECATED",
|
||||
"disabled": "Disabled",
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
"postman-collection": "^4.2.0",
|
||||
"process": "^0.11.10",
|
||||
"qs": "^6.11.2",
|
||||
"quicktype-core": "^23.0.79",
|
||||
"rxjs": "^7.8.1",
|
||||
"set-cookie-parser": "^2.6.0",
|
||||
"set-cookie-parser-es": "^1.0.5",
|
||||
|
||||
@@ -160,6 +160,7 @@ declare module 'vue' {
|
||||
IconLucideRss: typeof import('~icons/lucide/rss')['default']
|
||||
IconLucideSearch: typeof import('~icons/lucide/search')['default']
|
||||
IconLucideUsers: typeof import('~icons/lucide/users')['default']
|
||||
IconLucideVerified: typeof import('~icons/lucide/verified')['default']
|
||||
InterceptorsErrorPlaceholder: typeof import('./components/interceptors/ErrorPlaceholder.vue')['default']
|
||||
InterceptorsExtensionSubtitle: typeof import('./components/interceptors/ExtensionSubtitle.vue')['default']
|
||||
LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default']
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
:title="`${t(
|
||||
'action.download_file'
|
||||
)} <kbd>${getSpecialKey()}</kbd><kbd>J</kbd>`"
|
||||
:icon="downloadResponseIcon"
|
||||
:icon="downloadIcon"
|
||||
@click="downloadResponse"
|
||||
/>
|
||||
<HoppButtonSecondary
|
||||
@@ -33,9 +33,41 @@
|
||||
:title="`${t(
|
||||
'action.copy'
|
||||
)} <kbd>${getSpecialKey()}</kbd><kbd>.</kbd>`"
|
||||
:icon="copyResponseIcon"
|
||||
@click="copyResponse(response[0].data)"
|
||||
:icon="copyIcon"
|
||||
@click="copyResponse"
|
||||
/>
|
||||
<tippy
|
||||
interactive
|
||||
trigger="click"
|
||||
theme="popover"
|
||||
:on-shown="() => copyInterfaceTippyActions.focus()"
|
||||
>
|
||||
<HoppButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('app.copy_interface_type')"
|
||||
:icon="IconMore"
|
||||
/>
|
||||
<template #content="{ hide }">
|
||||
<div
|
||||
ref="copyInterfaceTippyActions"
|
||||
class="flex flex-col focus:outline-none"
|
||||
tabindex="0"
|
||||
@keyup.escape="hide()"
|
||||
>
|
||||
<HoppSmartItem
|
||||
v-for="(language, index) in interfaceLanguages"
|
||||
:key="index"
|
||||
:label="language"
|
||||
:icon="
|
||||
copiedInterfaceLanguage === language
|
||||
? copyInterfaceIcon
|
||||
: IconCopy
|
||||
"
|
||||
@click="runCopyInterface(language)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</tippy>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="schemaEditor" class="flex flex-1 flex-col"></div>
|
||||
@@ -59,22 +91,22 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import IconWrapText from "~icons/lucide/wrap-text"
|
||||
import IconDownload from "~icons/lucide/download"
|
||||
import IconCheck from "~icons/lucide/check"
|
||||
import IconCopy from "~icons/lucide/copy"
|
||||
import IconMore from "~icons/lucide/more-horizontal"
|
||||
import { computed, reactive, ref } from "vue"
|
||||
import { refAutoReset } from "@vueuse/core"
|
||||
import { useCodemirror } from "@composables/codemirror"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
import { useI18n } from "@composables/i18n"
|
||||
import { useToast } from "@composables/toast"
|
||||
import { defineActionHandler } from "~/helpers/actions"
|
||||
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
|
||||
import { GQLResponseEvent } from "~/helpers/graphql/connection"
|
||||
import { platform } from "~/platform"
|
||||
import interfaceLanguages from "~/helpers/utils/interfaceLanguages"
|
||||
import {
|
||||
useCopyInterface,
|
||||
useCopyResponse,
|
||||
useDownloadResponse,
|
||||
} from "~/composables/lens-actions"
|
||||
|
||||
const t = useI18n()
|
||||
const toast = useToast()
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -101,6 +133,7 @@ const responseString = computed(() => {
|
||||
})
|
||||
|
||||
const schemaEditor = ref<any | null>(null)
|
||||
const copyInterfaceTippyActions = ref<any | null>(null)
|
||||
const linewrapEnabled = ref(true)
|
||||
|
||||
useCodemirror(
|
||||
@@ -118,55 +151,29 @@ useCodemirror(
|
||||
})
|
||||
)
|
||||
|
||||
const downloadResponseIcon = refAutoReset<
|
||||
typeof IconDownload | typeof IconCheck
|
||||
>(IconDownload, 1000)
|
||||
const copyResponseIcon = refAutoReset<typeof IconCopy | typeof IconCheck>(
|
||||
IconCopy,
|
||||
1000
|
||||
const { copyIcon, copyResponse } = useCopyResponse(responseString)
|
||||
const { copyInterfaceIcon, copyInterface } = useCopyInterface(responseString)
|
||||
const { downloadIcon, downloadResponse } = useDownloadResponse(
|
||||
"application/json",
|
||||
responseString
|
||||
)
|
||||
|
||||
const copyResponse = (str: string) => {
|
||||
copyToClipboard(str)
|
||||
copyResponseIcon.value = IconCheck
|
||||
toast.success(`${t("state.copied_to_clipboard")}`)
|
||||
}
|
||||
const copiedInterfaceLanguage = ref("")
|
||||
|
||||
const downloadResponse = async (str: string) => {
|
||||
const dataToWrite = str
|
||||
const file = new Blob([dataToWrite!], { type: "application/json" })
|
||||
const url = URL.createObjectURL(file)
|
||||
|
||||
const filename = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
|
||||
|
||||
URL.revokeObjectURL(url)
|
||||
|
||||
const result = await platform.io.saveFileWithDialog({
|
||||
data: dataToWrite,
|
||||
contentType: "application/json",
|
||||
suggestedFilename: filename,
|
||||
filters: [
|
||||
{
|
||||
name: "JSON file",
|
||||
extensions: ["json"],
|
||||
},
|
||||
],
|
||||
const runCopyInterface = (language: string) => {
|
||||
copyInterface(language).then(() => {
|
||||
copiedInterfaceLanguage.value = language
|
||||
})
|
||||
|
||||
if (result.type === "unknown" || result.type === "saved") {
|
||||
downloadResponseIcon.value = IconCheck
|
||||
toast.success(`${t("state.download_started")}`)
|
||||
}
|
||||
}
|
||||
|
||||
defineActionHandler(
|
||||
"response.file.download",
|
||||
() => downloadResponse(responseString.value),
|
||||
() => downloadResponse(),
|
||||
computed(() => !!props.response && props.response.length > 0)
|
||||
)
|
||||
defineActionHandler(
|
||||
"response.copy",
|
||||
() => copyResponse(responseString.value),
|
||||
() => copyResponse(),
|
||||
computed(() => !!props.response && props.response.length > 0)
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -44,6 +44,39 @@
|
||||
:icon="copyIcon"
|
||||
@click="copyResponse"
|
||||
/>
|
||||
<tippy
|
||||
v-if="response.body"
|
||||
interactive
|
||||
trigger="click"
|
||||
theme="popover"
|
||||
:on-shown="() => copyInterfaceTippyActions.focus()"
|
||||
>
|
||||
<HoppButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('app.copy_interface_type')"
|
||||
:icon="IconMore"
|
||||
/>
|
||||
<template #content="{ hide }">
|
||||
<div
|
||||
ref="copyInterfaceTippyActions"
|
||||
class="flex flex-col focus:outline-none"
|
||||
tabindex="0"
|
||||
@keyup.escape="hide()"
|
||||
>
|
||||
<HoppSmartItem
|
||||
v-for="(language, index) in interfaceLanguages"
|
||||
:key="index"
|
||||
:label="language"
|
||||
:icon="
|
||||
copiedInterfaceLanguage === language
|
||||
? copyInterfaceIcon
|
||||
: IconCopy
|
||||
"
|
||||
@click="runCopyInterface(language)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</tippy>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -201,7 +234,9 @@
|
||||
<script setup lang="ts">
|
||||
import IconWrapText from "~icons/lucide/wrap-text"
|
||||
import IconFilter from "~icons/lucide/filter"
|
||||
import IconMore from "~icons/lucide/more-horizontal"
|
||||
import IconHelpCircle from "~icons/lucide/help-circle"
|
||||
import IconCopy from "~icons/lucide/copy"
|
||||
import * as LJSON from "lossless-json"
|
||||
import * as O from "fp-ts/Option"
|
||||
import * as E from "fp-ts/Either"
|
||||
@@ -221,9 +256,11 @@ import {
|
||||
useCopyResponse,
|
||||
useResponseBody,
|
||||
useDownloadResponse,
|
||||
useCopyInterface,
|
||||
} from "@composables/lens-actions"
|
||||
import { defineActionHandler } from "~/helpers/actions"
|
||||
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
|
||||
import interfaceLanguages from "~/helpers/utils/interfaceLanguages"
|
||||
|
||||
const t = useI18n()
|
||||
|
||||
@@ -235,6 +272,13 @@ const { responseBodyText } = useResponseBody(props.response)
|
||||
|
||||
const toggleFilter = ref(false)
|
||||
const filterQueryText = ref("")
|
||||
const copiedInterfaceLanguage = ref("")
|
||||
|
||||
const runCopyInterface = (language: string) => {
|
||||
copyInterface(language).then(() => {
|
||||
copiedInterfaceLanguage.value = language
|
||||
})
|
||||
}
|
||||
|
||||
type BodyParseError =
|
||||
| { type: "JSON_PARSE_FAILED" }
|
||||
@@ -319,6 +363,7 @@ const filterResponseError = computed(() =>
|
||||
)
|
||||
|
||||
const { copyIcon, copyResponse } = useCopyResponse(jsonBodyText)
|
||||
const { copyInterfaceIcon, copyInterface } = useCopyInterface(jsonBodyText)
|
||||
const { downloadIcon, downloadResponse } = useDownloadResponse(
|
||||
"application/json",
|
||||
jsonBodyText
|
||||
@@ -327,6 +372,7 @@ const { downloadIcon, downloadResponse } = useDownloadResponse(
|
||||
// Template refs
|
||||
const tippyActions = ref<any | null>(null)
|
||||
const jsonResponse = ref<any | null>(null)
|
||||
const copyInterfaceTippyActions = ref<any | null>(null)
|
||||
const linewrapEnabled = ref(true)
|
||||
|
||||
const { cursor } = useCodemirror(
|
||||
|
||||
@@ -11,6 +11,29 @@ import { refAutoReset } from "@vueuse/core"
|
||||
import { copyToClipboard } from "@helpers/utils/clipboard"
|
||||
import { HoppRESTResponse } from "@helpers/types/HoppRESTResponse"
|
||||
import { platform } from "~/platform"
|
||||
import jsonToLanguage from "~/helpers/utils/json-to-language"
|
||||
|
||||
export function useCopyInterface(responseBodyText: Ref<string>) {
|
||||
const toast = useToast()
|
||||
const t = useI18n()
|
||||
|
||||
const copyInterfaceIcon = refAutoReset(IconCopy, 1000)
|
||||
|
||||
const copyInterface = async (targetLanguage: string) => {
|
||||
jsonToLanguage(targetLanguage, responseBodyText.value).then((res) => {
|
||||
copyToClipboard(res.lines.join("\n"))
|
||||
copyInterfaceIcon.value = IconCheck
|
||||
toast.success(
|
||||
t("state.copied_interface_to_clipboard", { language: targetLanguage })
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
copyInterfaceIcon,
|
||||
copyInterface,
|
||||
}
|
||||
}
|
||||
|
||||
export function useCopyResponse(responseBodyText: Ref<any>) {
|
||||
const toast = useToast()
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
const interfaceLanguages = [
|
||||
"cJSON",
|
||||
"C++",
|
||||
"C#",
|
||||
"Crystal",
|
||||
"Dart",
|
||||
"Elm",
|
||||
"Flow",
|
||||
"Go",
|
||||
"Haskell",
|
||||
"Java",
|
||||
"JavaScript",
|
||||
"Kotlin",
|
||||
"Objective-C",
|
||||
"PHP",
|
||||
"Pike",
|
||||
"Python",
|
||||
"Ruby",
|
||||
"Rust",
|
||||
"Scala3",
|
||||
"Smithy",
|
||||
"Swift",
|
||||
"TypeScript",
|
||||
]
|
||||
|
||||
export default interfaceLanguages
|
||||
@@ -0,0 +1,27 @@
|
||||
import {
|
||||
quicktype,
|
||||
InputData,
|
||||
jsonInputForTargetLanguage,
|
||||
} from "quicktype-core"
|
||||
|
||||
async function jsonToLanguage(targetLanguage: string, jsonString: string) {
|
||||
const jsonInput = jsonInputForTargetLanguage(targetLanguage)
|
||||
|
||||
await jsonInput.addSource({
|
||||
name: "JSONSchema",
|
||||
samples: [jsonString],
|
||||
})
|
||||
|
||||
const inputData = new InputData()
|
||||
inputData.addInput(jsonInput)
|
||||
|
||||
return await quicktype({
|
||||
inputData,
|
||||
lang: targetLanguage,
|
||||
rendererOptions: {
|
||||
"just-types": true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export default jsonToLanguage
|
||||
Reference in New Issue
Block a user