feat: filter json body response (#2404)

This commit is contained in:
Nivedin
2022-06-10 18:12:40 +05:30
committed by GitHub
parent 04a9c4dc52
commit 6b1ca1dce1
8 changed files with 174 additions and 13 deletions

View File

@@ -1,12 +1,15 @@
<template>
<div class="flex flex-col flex-1">
<div
v-if="response.type === 'success' || response.type === 'fail'"
class="flex flex-col flex-1"
>
<div
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-lowerSecondaryStickyFold"
>
<label class="font-semibold text-secondaryLight">
{{ t("response.body") }}
</label>
<div class="flex">
<div class="flex items-center">
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
@@ -15,6 +18,14 @@
svg="wrap-text"
@click.native.prevent="linewrapEnabled = !linewrapEnabled"
/>
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.filter_response')"
svg="filter"
:class="{ '!text-accent': toggleFilter }"
@click.native.prevent="toggleFilterState"
/>
<ButtonSecondary
v-if="response.body"
ref="downloadResponse"
@@ -33,7 +44,47 @@
/>
</div>
</div>
<div ref="jsonResponse" class="flex flex-col flex-1"></div>
<div
v-if="toggleFilter"
class="bg-primary flex sticky top-lowerTertiaryStickyFold z-10 border-b border-dividerLight"
>
<div
class="bg-primaryLight border-divider text-secondaryDark inline-flex flex-1 items-center"
>
<span class="inline-flex flex-1 items-center px-4">
<SmartIcon name="search" class="h-4 w-4 text-secondaryLight" />
<input
v-model="filterQueryText"
v-focus
class="input !border-0 !px-2"
:placeholder="`${t('response.filter_response_body')}`"
type="text"
/>
</span>
<div
v-if="filterResponseError"
class="px-2 py-1 text-tiny flex items-center justify-center text-accentContrast rounded"
:class="{
'bg-red-500':
filterResponseError.type === 'JSON_PARSE_FAILED' ||
filterResponseError.type === 'JSON_PATH_QUERY_ERROR',
'bg-amber-500': filterResponseError.type === 'RESPONSE_EMPTY',
}"
>
<SmartIcon name="info" class="svg-icons mr-1.5" />
<span>{{ filterResponseError.error }}</span>
</div>
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
:title="t('app.wiki')"
svg="help-circle"
to="https://github.com/JSONPath-Plus/JSONPath"
blank
/>
</div>
</div>
<div ref="jsonResponse" class="flex flex-col flex-1 h-auto h-full"></div>
<div
v-if="outlinePath"
class="sticky bottom-0 z-10 flex px-2 overflow-auto border-t bg-primaryLight border-dividerLight flex-nowrap hide-scrollbar"
@@ -142,8 +193,10 @@
<script setup lang="ts">
import * as LJSON from "lossless-json"
import * as O from "fp-ts/Option"
import * as E from "fp-ts/Either"
import { pipe } from "fp-ts/function"
import { computed, ref, reactive } from "@nuxtjs/composition-api"
import { JSONPath } from "jsonpath-plus"
import { useCodemirror } from "~/helpers/editor/codemirror"
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
import jsonParse, { JSONObjectMember, JSONValue } from "~/helpers/jsonParse"
@@ -172,9 +225,51 @@ const { downloadIcon, downloadResponse } = useDownloadResponse(
responseBodyText
)
const jsonBodyText = computed(() =>
const toggleFilter = ref(false)
const filterQueryText = ref("")
type BodyParseError =
| { type: "JSON_PARSE_FAILED" }
| { type: "JSON_PATH_QUERY_FAILED"; error: Error }
const responseJsonObject = computed(() =>
pipe(
responseBodyText.value,
E.tryCatchK(
LJSON.parse,
(): BodyParseError => ({ type: "JSON_PARSE_FAILED" })
)
)
)
const jsonResponseBodyText = computed(() => {
if (filterQueryText.value.length > 0) {
return pipe(
responseJsonObject.value,
E.chain((parsedJSON) =>
E.tryCatch(
() =>
JSONPath({
path: filterQueryText.value,
json: parsedJSON,
}) as undefined,
(err): BodyParseError => ({
type: "JSON_PATH_QUERY_FAILED",
error: err as Error,
})
)
),
E.map(JSON.stringify)
)
} else {
return E.right(responseBodyText.value)
}
})
const jsonBodyText = computed(() =>
pipe(
jsonResponseBodyText.value,
E.getOrElse(() => responseBodyText.value),
O.tryCatchK(LJSON.parse),
O.map((val) => LJSON.stringify(val, undefined, 2)),
O.getOrElse(() => responseBodyText.value)
@@ -189,6 +284,32 @@ const ast = computed(() =>
)
)
const filterResponseError = computed(() =>
pipe(
jsonResponseBodyText.value,
E.match(
(e) => {
switch (e.type) {
case "JSON_PATH_QUERY_FAILED":
return { type: "JSON_PATH_QUERY_ERROR", error: e.error.message }
case "JSON_PARSE_FAILED":
return {
type: "JSON_PARSE_FAILED",
error: t("error.json_parsing_failed").toString(),
}
}
},
(result) =>
result === "[]"
? {
type: "RESPONSE_EMPTY",
error: t("error.no_results_found").toString(),
}
: undefined
)
)
)
const outlineOptions = ref<any | null>(null)
const jsonResponse = ref<any | null>(null)
const linewrapEnabled = ref(true)
@@ -227,6 +348,11 @@ const outlinePath = computed(() =>
O.getOrElseW(() => null)
)
)
const toggleFilterState = () => {
filterQueryText.value = ""
toggleFilter.value = !toggleFilter.value
}
</script>
<style lang="scss" scoped>