refactor: gql request object and history typing updates

This commit is contained in:
Andrew Bastin
2021-08-24 21:58:04 +05:30
parent c5f8ab3394
commit d1b2539d67
11 changed files with 240 additions and 107 deletions

View File

@@ -97,7 +97,8 @@
</template>
<script lang="ts">
import { defineComponent } from "@nuxtjs/composition-api"
import { defineComponent, PropType } from "@nuxtjs/composition-api"
import { HoppGQLRequest, makeGQLRequest } from "~/helpers/types/HoppGQLRequest"
import { removeGraphqlRequest } from "~/newstore/collections"
import { setGQLSession } from "~/newstore/GQLSession"
@@ -107,7 +108,7 @@ export default defineComponent({
picked: { type: Object, default: null },
// Whether the request is being saved (activate 'select' event)
savingMode: { type: Boolean, default: false },
request: { type: Object, default: () => {} },
request: { type: Object as PropType<HoppGQLRequest>, default: () => {} },
folderPath: { type: String, default: null },
requestIndex: { type: Number, default: null },
doc: Boolean,
@@ -143,11 +144,13 @@ export default defineComponent({
this.pick()
} else {
setGQLSession({
name: this.$props.request.name,
url: this.$props.request.url,
query: this.$props.request.query,
headers: this.$props.request.headers,
variables: this.$props.request.variables,
request: makeGQLRequest({
name: this.$props.request.name,
url: this.$props.request.url,
query: this.$props.request.query,
headers: this.$props.request.headers,
variables: this.$props.request.variables,
}),
schema: "",
response: "",
})

View File

@@ -291,9 +291,10 @@ import {
} from "~/newstore/GQLSession"
import { commonHeaders } from "~/helpers/headers"
import { GQLConnection } from "~/helpers/GQLConnection"
import { addGraphqlHistoryEntry } from "~/newstore/history"
import { makeGQLHistoryEntry, addGraphqlHistoryEntry } from "~/newstore/history"
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
import { getCurrentStrategyID } from "~/helpers/network"
import { makeGQLRequest } from "~/helpers/types/HoppGQLRequest"
export default defineComponent({
props: {
@@ -377,20 +378,19 @@ export default defineComponent({
response.value = JSON.stringify(JSON.parse(responseText), null, 2)
const historyEntry = {
url: runURL,
query: runQuery,
variables: runVariables,
star: false,
headers: runHeaders,
response: response.value,
date: new Date().toLocaleDateString(),
time: new Date().toLocaleTimeString(),
updatedOn: new Date(),
duration,
}
addGraphqlHistoryEntry(historyEntry)
addGraphqlHistoryEntry(
makeGQLHistoryEntry({
request: makeGQLRequest({
name: "",
url: runURL,
query: runQuery,
headers: runHeaders,
variables: runVariables,
}),
response: response.value,
star: false,
})
)
$toast.success(t("state.finished_in", { duration }).toString(), {
icon: "done",

View File

@@ -213,10 +213,10 @@ import {
import { GraphQLField, GraphQLType } from "graphql"
import { map } from "rxjs/operators"
import { GQLConnection } from "~/helpers/GQLConnection"
import { GQLHeader } from "~/helpers/types/HoppGQLRequest"
import { copyToClipboard } from "~/helpers/utils/clipboard"
import { useReadonlyStream } from "~/helpers/utils/composables"
import {
GQLHeader,
setGQLHeaders,
setGQLQuery,
setGQLResponse,

View File

@@ -59,14 +59,19 @@
</template>
<script lang="ts">
import { computed, defineComponent, ref } from "@nuxtjs/composition-api"
import {
computed,
defineComponent,
PropType,
ref,
} from "@nuxtjs/composition-api"
import { makeGQLRequest } from "~/helpers/types/HoppGQLRequest"
import { setGQLSession } from "~/newstore/GQLSession"
// TODO: Concrete entry data type
import { GQLHistoryEntry } from "~/newstore/history"
export default defineComponent({
props: {
entry: { type: Object, default: () => {} },
entry: { type: Object as PropType<GQLHistoryEntry>, default: () => {} },
showMore: Boolean,
},
setup(props) {
@@ -74,8 +79,8 @@ export default defineComponent({
const query = computed(() =>
expand.value
? (props.entry.query.split("\n") as string[])
: (props.entry.query
? (props.entry.request.query.split("\n") as string[])
: (props.entry.request.query
.split("\n")
.slice(0, 2)
.concat(["..."]) as string[])
@@ -83,13 +88,15 @@ export default defineComponent({
const useEntry = () => {
setGQLSession({
name: "",
url: props.entry.url,
headers: props.entry.headers,
response: props.entry.response,
request: makeGQLRequest({
name: props.entry.request.name,
url: props.entry.request.url,
headers: props.entry.request.headers,
query: props.entry.request.query,
variables: props.entry.request.variables,
}),
schema: "",
query: props.entry.query,
variables: props.entry.variables,
response: props.entry.response,
})
}

View File

@@ -96,6 +96,8 @@ import {
toggleRESTHistoryEntryStar,
deleteGraphqlHistoryEntry,
deleteRESTHistoryEntry,
RESTHistoryEntry,
GQLHistoryEntry,
} from "~/newstore/history"
import { setRESTRequest } from "~/newstore/RESTSession"
@@ -105,7 +107,7 @@ export default defineComponent({
},
setup(props) {
return {
history: useReadonlyStream(
history: useReadonlyStream<RESTHistoryEntry[] | GQLHistoryEntry[]>(
props.page === "rest" ? restHistory$ : graphqlHistory$,
[]
),
@@ -120,19 +122,23 @@ export default defineComponent({
},
computed: {
filteredHistory(): any[] {
const filteringHistory = this.history
const filteringHistory = this.history as Array<
RESTHistoryEntry | GQLHistoryEntry
>
return filteringHistory.filter((entry) => {
const filterText = this.filterText.toLowerCase()
return Object.keys(entry).some((key) => {
let value = entry[key]
if (value) {
value = typeof value !== "string" ? value.toString() : value
return value.toLowerCase().includes(filterText)
}
return false
})
})
return filteringHistory.filter(
(entry: RESTHistoryEntry | GQLHistoryEntry) => {
const filterText = this.filterText.toLowerCase()
return Object.keys(entry).some((key) => {
let value = entry[key as keyof typeof entry]
if (value) {
value = typeof value !== "string" ? value.toString() : value
return value.toLowerCase().includes(filterText)
}
return false
})
}
)
},
},
methods: {

View File

@@ -11,7 +11,7 @@ import {
} from "graphql"
import { distinctUntilChanged, map } from "rxjs/operators"
import { sendNetworkRequest } from "./network"
import { GQLHeader } from "~/newstore/GQLSession"
import { GQLHeader } from "./types/HoppGQLRequest"
const GQL_SCHEMA_POLL_INTERVAL = 7000

View File

@@ -3,12 +3,14 @@ import "firebase/firestore"
import { currentUser$ } from "./auth"
import { settingsStore } from "~/newstore/settings"
import {
GQLHistoryEntry,
graphqlHistoryStore,
HISTORY_LIMIT,
RESTHistoryEntry,
restHistoryStore,
setGraphqlHistoryEntries,
setRESTHistoryEntries,
translateToNewGQLHistory,
translateToNewRESTHistory,
} from "~/newstore/history"
@@ -190,12 +192,12 @@ export function initHistory() {
.orderBy("updatedOn", "desc")
.limit(HISTORY_LIMIT)
.onSnapshot((historyRef) => {
const history: any[] = []
const history: GQLHistoryEntry[] = []
historyRef.forEach((doc) => {
const entry = doc.data()
entry.id = doc.id
history.push(entry)
history.push(translateToNewGQLHistory(entry))
})
loadedGraphqlHistory = false

View File

@@ -0,0 +1,41 @@
export type GQLHeader = {
key: string
value: string
active: boolean
}
export type HoppGQLRequest = {
v: number
name: string
url: string
headers: GQLHeader[]
query: string
variables: string
}
export function translateToGQLRequest(x: any): HoppGQLRequest {
if (x.v && x.v === 1) return x
// Old request
const name = x.name ?? "Untitled"
const url = x.url ?? ""
const headers = x.headers ?? []
const query = x.query ?? ""
const variables = x.variables ?? []
return {
v: 1,
name,
url,
headers,
query,
variables,
}
}
export function makeGQLRequest(x: Omit<HoppGQLRequest, "v">) {
return {
v: 1,
...x,
}
}

View File

@@ -1,35 +1,32 @@
import { distinctUntilChanged, pluck } from "rxjs/operators"
import DispatchingStore, { defineDispatchers } from "./DispatchingStore"
import { useStream } from "~/helpers/utils/composables"
export type GQLHeader = {
key: string
value: string
active: boolean
}
import {
GQLHeader,
HoppGQLRequest,
makeGQLRequest,
} from "~/helpers/types/HoppGQLRequest"
type GQLSession = {
name: string
url: string
headers: GQLHeader[]
request: HoppGQLRequest
schema: string
query: string
variables: string
response: string
}
export const defaultGQLSession: GQLSession = {
name: "",
url: "https://rickandmortyapi.com/graphql",
headers: [],
schema: "",
query: `query GetCharacter($id: ID!) {
request: makeGQLRequest({
name: "",
url: "https://rickandmortyapi.com/graphql",
headers: [],
variables: `{ "id": "1" }`,
query: `query GetCharacter($id: ID!) {
character(id: $id) {
id
name
}
}`,
variables: `{ "id": "1" }`,
}),
schema: "",
response: "",
}
@@ -37,29 +34,44 @@ const dispatchers = defineDispatchers({
setSession(_: GQLSession, { session }: { session: GQLSession }) {
return session
},
setName(_: GQLSession, { newName }: { newName: string }) {
setName(curr: GQLSession, { newName }: { newName: string }) {
return {
name: newName,
request: {
...curr.request,
name: newName,
},
}
},
setURL(_: GQLSession, { newURL }: { newURL: string }) {
setURL(curr: GQLSession, { newURL }: { newURL: string }) {
return {
url: newURL,
request: {
...curr.request,
url: newURL,
},
}
},
setHeaders(_, { headers }: { headers: GQLHeader[] }) {
setHeaders(curr: GQLSession, { headers }: { headers: GQLHeader[] }) {
return {
headers,
request: {
...curr.request,
headers,
},
}
},
addHeader(curr: GQLSession, { header }: { header: GQLHeader }) {
return {
headers: [...curr.headers, header],
request: {
...curr.request,
headers: [...curr.request.headers, header],
},
}
},
removeHeader(curr: GQLSession, { headerIndex }: { headerIndex: number }) {
return {
headers: curr.headers.filter((_x, i) => i !== headerIndex),
request: {
...curr.request,
headers: curr.request.headers.filter((_x, i) => i !== headerIndex),
},
}
},
updateHeader(
@@ -70,19 +82,28 @@ const dispatchers = defineDispatchers({
}: { headerIndex: number; updatedHeader: GQLHeader }
) {
return {
headers: curr.headers.map((x, i) =>
i === headerIndex ? updatedHeader : x
),
request: {
...curr.request,
headers: curr.request.headers.map((x, i) =>
i === headerIndex ? updatedHeader : x
),
},
}
},
setQuery(_: GQLSession, { newQuery }: { newQuery: string }) {
setQuery(curr: GQLSession, { newQuery }: { newQuery: string }) {
return {
query: newQuery,
request: {
...curr.request,
query: newQuery,
},
}
},
setVariables(_: GQLSession, { newVariables }: { newVariables: string }) {
setVariables(curr: GQLSession, { newVariables }: { newVariables: string }) {
return {
variables: newVariables,
request: {
...curr.request,
variables: newVariables,
},
}
},
setResponse(_: GQLSession, { newResponse }: { newResponse: string }) {
@@ -193,34 +214,32 @@ export function setGQLSession(session: GQLSession) {
}
export function useGQLRequestName() {
return useStream(gqlName$, gqlSessionStore.value.name, (val) => {
return useStream(gqlName$, "", (newName) => {
gqlSessionStore.dispatch({
dispatcher: "setName",
payload: {
newName: val,
},
payload: { newName },
})
})
}
export const gqlName$ = gqlSessionStore.subject$.pipe(
pluck("name"),
pluck("request", "name"),
distinctUntilChanged()
)
export const gqlURL$ = gqlSessionStore.subject$.pipe(
pluck("url"),
pluck("request", "url"),
distinctUntilChanged()
)
export const gqlQuery$ = gqlSessionStore.subject$.pipe(
pluck("query"),
pluck("request", "query"),
distinctUntilChanged()
)
export const gqlVariables$ = gqlSessionStore.subject$.pipe(
pluck("variables"),
pluck("request", "variables"),
distinctUntilChanged()
)
export const gqlHeaders$ = gqlSessionStore.subject$.pipe(
pluck("headers"),
pluck("request", "headers"),
distinctUntilChanged()
)

View File

@@ -6,6 +6,10 @@ import {
HoppRESTRequest,
translateToNewRequest,
} from "~/helpers/types/HoppRESTRequest"
import {
HoppGQLRequest,
translateToGQLRequest,
} from "~/helpers/types/HoppGQLRequest"
export type RESTHistoryEntry = {
v: number
@@ -22,7 +26,18 @@ export type RESTHistoryEntry = {
id?: string // For when Firebase Firestore is set
}
export function makeHistoryEntry(
export type GQLHistoryEntry = {
v: number
request: HoppGQLRequest
response: string
star: boolean
id?: string // For when Firestore ID is set
}
export function makeRESTHistoryEntry(
x: Omit<RESTHistoryEntry, "v">
): RESTHistoryEntry {
return {
@@ -31,6 +46,15 @@ export function makeHistoryEntry(
}
}
export function makeGQLHistoryEntry(
x: Omit<GQLHistoryEntry, "v">
): GQLHistoryEntry {
return {
v: 1,
...x,
}
}
export function translateToNewRESTHistory(x: any): RESTHistoryEntry {
if (x.v === 1) return x
@@ -40,15 +64,33 @@ export function translateToNewRESTHistory(x: any): RESTHistoryEntry {
const duration = x.duration ?? null
const statusCode = x.status ?? null
const obj: RESTHistoryEntry = {
v: 1,
const obj: RESTHistoryEntry = makeRESTHistoryEntry({
request,
star,
responseMeta: {
duration,
statusCode,
},
}
})
if (x.id) obj.id = x.id
return obj
}
export function translateToNewGQLHistory(x: any): GQLHistoryEntry {
if (x.v === 1) return x
// Legacy
const request = translateToGQLRequest(x)
const star = x.star ?? false
const response = x.response ?? ""
const obj: GQLHistoryEntry = makeGQLHistoryEntry({
request,
star,
response,
})
if (x.id) obj.id = x.id
@@ -60,7 +102,7 @@ export const defaultRESTHistoryState = {
}
export const defaultGraphqlHistoryState = {
state: [] as any[],
state: [] as GQLHistoryEntry[],
}
export const HISTORY_LIMIT = 50
@@ -114,17 +156,26 @@ const RESTHistoryDispatchers = defineDispatchers({
})
const GQLHistoryDispatchers = defineDispatchers({
setEntries(_: GraphqlHistoryType, { entries }: { entries: any[] }) {
setEntries(
_: GraphqlHistoryType,
{ entries }: { entries: GQLHistoryEntry[] }
) {
return {
state: entries,
}
},
addEntry(currentVal: GraphqlHistoryType, { entry }) {
addEntry(
currentVal: GraphqlHistoryType,
{ entry }: { entry: GQLHistoryEntry }
) {
return {
state: [entry, ...currentVal.state].slice(0, HISTORY_LIMIT),
}
},
deleteEntry(currentVal: GraphqlHistoryType, { entry }) {
deleteEntry(
currentVal: GraphqlHistoryType,
{ entry }: { entry: GQLHistoryEntry }
) {
return {
state: currentVal.state.filter((e) => !eq(e, entry)),
}
@@ -134,7 +185,10 @@ const GQLHistoryDispatchers = defineDispatchers({
state: [],
}
},
toggleStar(currentVal: GraphqlHistoryType, { entry }) {
toggleStar(
currentVal: GraphqlHistoryType,
{ entry }: { entry: GQLHistoryEntry }
) {
return {
state: currentVal.state.map((e) => {
if (eq(e, entry) && e.star !== undefined) {
@@ -197,21 +251,21 @@ export function toggleRESTHistoryEntryStar(entry: RESTHistoryEntry) {
})
}
export function setGraphqlHistoryEntries(entries: any[]) {
export function setGraphqlHistoryEntries(entries: GQLHistoryEntry[]) {
graphqlHistoryStore.dispatch({
dispatcher: "setEntries",
payload: { entries },
})
}
export function addGraphqlHistoryEntry(entry: any) {
export function addGraphqlHistoryEntry(entry: GQLHistoryEntry) {
graphqlHistoryStore.dispatch({
dispatcher: "addEntry",
payload: { entry },
})
}
export function deleteGraphqlHistoryEntry(entry: any) {
export function deleteGraphqlHistoryEntry(entry: GQLHistoryEntry) {
graphqlHistoryStore.dispatch({
dispatcher: "deleteEntry",
payload: { entry },
@@ -225,7 +279,7 @@ export function clearGraphqlHistory() {
})
}
export function toggleGraphqlHistoryEntryStar(entry: any) {
export function toggleGraphqlHistoryEntryStar(entry: GQLHistoryEntry) {
graphqlHistoryStore.dispatch({
dispatcher: "toggleStar",
payload: { entry },
@@ -238,7 +292,7 @@ completedRESTResponse$.subscribe((res) => {
if (res.type === "loading" || res.type === "network_fail") return
addRESTHistoryEntry(
makeHistoryEntry({
makeRESTHistoryEntry({
request: res.req,
responseMeta: {
duration: res.meta.responseDuration,

View File

@@ -17,6 +17,7 @@ import {
setRESTHistoryEntries,
setGraphqlHistoryEntries,
translateToNewRESTHistory,
translateToNewGQLHistory,
} from "./history"
import {
restCollectionStore,
@@ -114,7 +115,7 @@ function setupHistoryPersistence() {
const graphqlHistoryData = JSON.parse(
window.localStorage.getItem("graphqlHistory") || "[]"
)
).map(translateToNewGQLHistory)
setRESTHistoryEntries(restHistoryData)
setGraphqlHistoryEntries(graphqlHistoryData)