From 40d65dba49ac1f98bbedf662f9e88cfbd789843f Mon Sep 17 00:00:00 2001 From: Nivedin <53208152+nivedin@users.noreply.github.com> Date: Mon, 30 Mar 2026 15:27:54 +0530 Subject: [PATCH] fix: add missing field and `translateToNewRequest` for history (#6068) --- .../hoppscotch-common/src/newstore/history.ts | 20 +++------ .../src/platform/history/desktop/index.ts | 43 +++++++------------ .../src/platform/history/web/index.ts | 43 +++++++------------ 3 files changed, 36 insertions(+), 70 deletions(-) diff --git a/packages/hoppscotch-common/src/newstore/history.ts b/packages/hoppscotch-common/src/newstore/history.ts index a686d8ae..8b3dc390 100644 --- a/packages/hoppscotch-common/src/newstore/history.ts +++ b/packages/hoppscotch-common/src/newstore/history.ts @@ -341,22 +341,14 @@ export function removeDuplicateGraphqlHistoryEntry(id: string) { // Listen to completed responses to add to history executedResponses$.subscribe((res) => { + // Spread to auto-capture any future fields, but omit _ref_id and id + // since history entries are snapshots and shouldn't carry collection/firestore references + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { _ref_id, id, ...request } = res.req + addRESTHistoryEntry( makeRESTHistoryEntry({ - request: { - auth: res.req.auth, - body: res.req.body, - endpoint: res.req.endpoint, - headers: res.req.headers, - method: res.req.method, - name: res.req.name, - params: res.req.params, - preRequestScript: res.req.preRequestScript, - testScript: res.req.testScript, - requestVariables: res.req.requestVariables, - v: res.req.v, - responses: res.req.responses, - }, + request, responseMeta: { duration: res.meta.responseDuration, statusCode: res.statusCode, diff --git a/packages/hoppscotch-selfhost-web/src/platform/history/desktop/index.ts b/packages/hoppscotch-selfhost-web/src/platform/history/desktop/index.ts index fd79ac3c..f36240d5 100644 --- a/packages/hoppscotch-selfhost-web/src/platform/history/desktop/index.ts +++ b/packages/hoppscotch-selfhost-web/src/platform/history/desktop/index.ts @@ -15,6 +15,7 @@ import { deleteGraphqlHistoryEntry, clearGraphqlHistory, } from "@hoppscotch/common/newstore/history" +import { translateToNewRequest, translateToGQLRequest } from "@hoppscotch/data" import { HistoryPlatformDef } from "@hoppscotch/common/platform/history" import { getUserHistoryEntries, @@ -98,7 +99,7 @@ async function loadHistoryEntries() { const restHistoryEntries: RESTHistoryEntry[] = restEntries.map((entry) => ({ v: 1, - request: JSON.parse(entry.request), + request: translateToNewRequest(JSON.parse(entry.request)), responseMeta: JSON.parse(entry.responseMetadata), star: entry.isStarred, updatedOn: new Date(entry.executedOn), @@ -107,7 +108,7 @@ async function loadHistoryEntries() { const gqlHistoryEntries: GQLHistoryEntry[] = gqlEntries.map((entry) => ({ v: 1, - request: JSON.parse(entry.request), + request: translateToGQLRequest(JSON.parse(entry.request)), response: JSON.parse(entry.responseMetadata), star: entry.isStarred, updatedOn: new Date(entry.executedOn), @@ -165,7 +166,7 @@ function setupUserHistoryCreatedSubscription() { ? addRESTHistoryEntry({ v: 1, id, - request: JSON.parse(request), + request: translateToNewRequest(JSON.parse(request)), responseMeta: JSON.parse(responseMetadata), star: isStarred, updatedOn: new Date(executedOn), @@ -173,7 +174,7 @@ function setupUserHistoryCreatedSubscription() { : addGraphqlHistoryEntry({ v: 1, id, - request: JSON.parse(request), + request: translateToGQLRequest(JSON.parse(request)), response: JSON.parse(responseMetadata), star: isStarred, updatedOn: new Date(executedOn), @@ -192,45 +193,31 @@ function setupUserHistoryUpdatedSubscription() { userHistoryUpdated$.subscribe((res) => { if (E.isRight(res)) { - const { id, executedOn, isStarred, request, responseMetadata, reqType } = - res.right.userHistoryUpdated + const { id, reqType, isStarred } = res.right.userHistoryUpdated if (reqType == ReqType.Rest) { - const updatedRestEntryIndex = restHistoryStore.value.state.findIndex( + const existingEntry = restHistoryStore.value.state.find( (entry) => entry.id == id ) - if (updatedRestEntryIndex != -1) { + // Only toggle if the store entry's star doesn't match the server state. + // Without this guard, the subscription echo from the same client's own + // toggle would cause a second toggle and revert the star. + if (existingEntry && existingEntry.star !== isStarred) { runDispatchWithOutSyncing(() => { - toggleRESTHistoryEntryStar({ - v: 1, - id, - request: JSON.parse(request), - responseMeta: JSON.parse(responseMetadata), - // because the star will be toggled in the store, we need to pass the opposite value - star: !isStarred, - updatedOn: new Date(executedOn), - }) + toggleRESTHistoryEntryStar(existingEntry) }) } } if (reqType == ReqType.Gql) { - const updatedGQLEntryIndex = graphqlHistoryStore.value.state.findIndex( + const existingEntry = graphqlHistoryStore.value.state.find( (entry) => entry.id == id ) - if (updatedGQLEntryIndex != -1) { + if (existingEntry && existingEntry.star !== isStarred) { runDispatchWithOutSyncing(() => { - toggleGraphqlHistoryEntryStar({ - v: 1, - id, - request: JSON.parse(request), - response: JSON.parse(responseMetadata), - // because the star will be toggled in the store, we need to pass the opposite value - star: !isStarred, - updatedOn: new Date(executedOn), - }) + toggleGraphqlHistoryEntryStar(existingEntry) }) } } diff --git a/packages/hoppscotch-selfhost-web/src/platform/history/web/index.ts b/packages/hoppscotch-selfhost-web/src/platform/history/web/index.ts index 03858cb2..64e1186c 100644 --- a/packages/hoppscotch-selfhost-web/src/platform/history/web/index.ts +++ b/packages/hoppscotch-selfhost-web/src/platform/history/web/index.ts @@ -15,6 +15,7 @@ import { deleteGraphqlHistoryEntry, clearGraphqlHistory, } from "@hoppscotch/common/newstore/history" +import { translateToNewRequest, translateToGQLRequest } from "@hoppscotch/data" import { HistoryPlatformDef } from "@hoppscotch/common/platform/history" import { getUserHistoryEntries, @@ -101,7 +102,7 @@ async function loadHistoryEntries() { const restHistoryEntries: RESTHistoryEntry[] = restEntries.map((entry) => ({ v: 1, - request: JSON.parse(entry.request), + request: translateToNewRequest(JSON.parse(entry.request)), responseMeta: JSON.parse(entry.responseMetadata), star: entry.isStarred, updatedOn: new Date(entry.executedOn), @@ -110,7 +111,7 @@ async function loadHistoryEntries() { const gqlHistoryEntries: GQLHistoryEntry[] = gqlEntries.map((entry) => ({ v: 1, - request: JSON.parse(entry.request), + request: translateToGQLRequest(JSON.parse(entry.request)), response: JSON.parse(entry.responseMetadata), star: entry.isStarred, updatedOn: new Date(entry.executedOn), @@ -168,7 +169,7 @@ function setupUserHistoryCreatedSubscription() { ? addRESTHistoryEntry({ v: 1, id, - request: JSON.parse(request), + request: translateToNewRequest(JSON.parse(request)), responseMeta: JSON.parse(responseMetadata), star: isStarred, updatedOn: new Date(executedOn), @@ -176,7 +177,7 @@ function setupUserHistoryCreatedSubscription() { : addGraphqlHistoryEntry({ v: 1, id, - request: JSON.parse(request), + request: translateToGQLRequest(JSON.parse(request)), response: JSON.parse(responseMetadata), star: isStarred, updatedOn: new Date(executedOn), @@ -195,45 +196,31 @@ function setupUserHistoryUpdatedSubscription() { userHistoryUpdated$.subscribe((res) => { if (E.isRight(res)) { - const { id, executedOn, isStarred, request, responseMetadata, reqType } = - res.right.userHistoryUpdated + const { id, reqType, isStarred } = res.right.userHistoryUpdated if (reqType == ReqType.Rest) { - const updatedRestEntryIndex = restHistoryStore.value.state.findIndex( + const existingEntry = restHistoryStore.value.state.find( (entry) => entry.id == id ) - if (updatedRestEntryIndex != -1) { + // Only toggle if the store entry's star doesn't match the server state. + // Without this guard, the subscription echo from the same client's own + // toggle would cause a second toggle and revert the star. + if (existingEntry && existingEntry.star !== isStarred) { runDispatchWithOutSyncing(() => { - toggleRESTHistoryEntryStar({ - v: 1, - id, - request: JSON.parse(request), - responseMeta: JSON.parse(responseMetadata), - // because the star will be toggled in the store, we need to pass the opposite value - star: !isStarred, - updatedOn: new Date(executedOn), - }) + toggleRESTHistoryEntryStar(existingEntry) }) } } if (reqType == ReqType.Gql) { - const updatedGQLEntryIndex = graphqlHistoryStore.value.state.findIndex( + const existingEntry = graphqlHistoryStore.value.state.find( (entry) => entry.id == id ) - if (updatedGQLEntryIndex != -1) { + if (existingEntry && existingEntry.star !== isStarred) { runDispatchWithOutSyncing(() => { - toggleGraphqlHistoryEntryStar({ - v: 1, - id, - request: JSON.parse(request), - response: JSON.parse(responseMetadata), - // because the star will be toggled in the store, we need to pass the opposite value - star: !isStarred, - updatedOn: new Date(executedOn), - }) + toggleGraphqlHistoryEntryStar(existingEntry) }) } }