From 805ac886888a7ead46fad8b0c9f3367fa4d6626a Mon Sep 17 00:00:00 2001 From: Nivedin <53208152+nivedin@users.noreply.github.com> Date: Thu, 29 May 2025 15:13:14 +0530 Subject: [PATCH] fix: environment and rest tab state schema migration (#5105) --- .../src/components/http/Response.vue | 3 +- .../import-export/import/insomniaEnv.ts | 9 ++-- .../helpers/import-export/import/openapi.ts | 3 +- .../helpers/import-export/import/postman.ts | 3 +- .../import-export/import/postmanEnv.ts | 6 +-- .../helpers/teams/TeamEnvironmentAdapter.ts | 36 +++++++++------ .../persistence/validation-schemas/index.ts | 2 +- .../hoppscotch-data/src/environment/index.ts | 2 +- .../hoppscotch-data/src/environment/v/2.ts | 2 +- packages/hoppscotch-data/src/rest/index.ts | 21 ++++----- packages/hoppscotch-data/src/rest/v/12.ts | 43 ++++++++++++++++++ packages/hoppscotch-data/src/rest/v/13.ts | 45 ++++++++++++++++++- packages/hoppscotch-data/src/rest/v/9.ts | 2 +- .../platform/environments/desktop/index.ts | 32 ++++++++++--- .../src/platform/environments/web/index.ts | 32 ++++++++++--- 15 files changed, 190 insertions(+), 51 deletions(-) diff --git a/packages/hoppscotch-common/src/components/http/Response.vue b/packages/hoppscotch-common/src/components/http/Response.vue index 96d52c96..c3218c2f 100644 --- a/packages/hoppscotch-common/src/components/http/Response.vue +++ b/packages/hoppscotch-common/src/components/http/Response.vue @@ -26,6 +26,7 @@ import { getStatusCodeReasonPhrase } from "~/helpers/utils/statusCodes" import { HoppRESTResponseOriginalRequest, HoppRESTRequestResponse, + RESTResOriginalReqSchemaVersion, } from "@hoppscotch/data" import { editRESTRequest } from "~/newstore/collections" import { useToast } from "@composables/toast" @@ -94,7 +95,7 @@ const onSaveAsExample = () => { } = response.req const originalRequest: HoppRESTResponseOriginalRequest = { - v: "3", + v: RESTResOriginalReqSchemaVersion, method, endpoint, headers, diff --git a/packages/hoppscotch-common/src/helpers/import-export/import/insomniaEnv.ts b/packages/hoppscotch-common/src/helpers/import-export/import/insomniaEnv.ts index 73ec2b0c..b9781ea7 100644 --- a/packages/hoppscotch-common/src/helpers/import-export/import/insomniaEnv.ts +++ b/packages/hoppscotch-common/src/helpers/import-export/import/insomniaEnv.ts @@ -4,7 +4,10 @@ import * as O from "fp-ts/Option" import { IMPORTER_INVALID_FILE_FORMAT } from "." import { z } from "zod" -import { NonSecretEnvironment } from "@hoppscotch/data" +import { + EnvironmentSchemaVersion, + NonSecretEnvironment, +} from "@hoppscotch/data" import { safeParseJSONOrYAML } from "~/helpers/functional/yaml" import { uniqueID } from "~/helpers/utils/uniqueID" @@ -67,13 +70,13 @@ export const insomniaEnvImporter = (contents: string[]) => { if (parsedInsomniaEnv.success) { const environment: NonSecretEnvironment = { id: uniqueID(), - v: 2, + v: EnvironmentSchemaVersion, name: parsedInsomniaEnv.data.name, variables: Object.entries(parsedInsomniaEnv.data.data).map( ([key, value]) => ({ key, initialValue: value, - currentValue: value, + currentValue: "", secret: false, }) ), diff --git a/packages/hoppscotch-common/src/helpers/import-export/import/openapi.ts b/packages/hoppscotch-common/src/helpers/import-export/import/openapi.ts index 20757f6c..6fead267 100644 --- a/packages/hoppscotch-common/src/helpers/import-export/import/openapi.ts +++ b/packages/hoppscotch-common/src/helpers/import-export/import/openapi.ts @@ -20,6 +20,7 @@ import { HoppRESTRequest, HoppRESTRequestResponses, HoppRESTResponseOriginalRequest, + RESTResOriginalReqSchemaVersion, } from "@hoppscotch/data" import { pipe, flow } from "fp-ts/function" import * as A from "fp-ts/Array" @@ -843,7 +844,7 @@ const convertPathToHoppReqs = ( requestVariables: parseOpenAPIVariables( (info.parameters as OpenAPIParamsType[] | undefined) ?? [] ), - v: "3", + v: RESTResOriginalReqSchemaVersion, }), }), metadata: { diff --git a/packages/hoppscotch-common/src/helpers/import-export/import/postman.ts b/packages/hoppscotch-common/src/helpers/import-export/import/postman.ts index 74308aa7..a41df9af 100644 --- a/packages/hoppscotch-common/src/helpers/import-export/import/postman.ts +++ b/packages/hoppscotch-common/src/helpers/import-export/import/postman.ts @@ -10,6 +10,7 @@ import { knownContentTypes, makeCollection, makeRESTRequest, + RESTResOriginalReqSchemaVersion, ValidContentTypes, } from "@hoppscotch/data" import * as A from "fp-ts/Array" @@ -177,7 +178,7 @@ const getHoppResponses = ( requestVariables: getHoppReqVariables( response.originalRequest?.url.variables ?? null ), - v: "3" as const, + v: RESTResOriginalReqSchemaVersion, }, } return [response.name, res] diff --git a/packages/hoppscotch-common/src/helpers/import-export/import/postmanEnv.ts b/packages/hoppscotch-common/src/helpers/import-export/import/postmanEnv.ts index 1c7d30fa..5f920395 100644 --- a/packages/hoppscotch-common/src/helpers/import-export/import/postmanEnv.ts +++ b/packages/hoppscotch-common/src/helpers/import-export/import/postmanEnv.ts @@ -1,4 +1,4 @@ -import { Environment } from "@hoppscotch/data" +import { Environment, EnvironmentSchemaVersion } from "@hoppscotch/data" import * as O from "fp-ts/Option" import * as TE from "fp-ts/TaskEither" import { z } from "zod" @@ -52,12 +52,12 @@ export const postmanEnvImporter = (contents: string[]) => { const environments: Environment[] = validationResult.data.map( ({ name, values }) => ({ id: uniqueID(), - v: 2, + v: EnvironmentSchemaVersion, name, variables: values.map(({ key, value, type }) => ({ key, initialValue: value, - currentValue: value, + currentValue: "", secret: type === "secret", })), }) diff --git a/packages/hoppscotch-common/src/helpers/teams/TeamEnvironmentAdapter.ts b/packages/hoppscotch-common/src/helpers/teams/TeamEnvironmentAdapter.ts index fcd94fb6..c43d521b 100644 --- a/packages/hoppscotch-common/src/helpers/teams/TeamEnvironmentAdapter.ts +++ b/packages/hoppscotch-common/src/helpers/teams/TeamEnvironmentAdapter.ts @@ -10,6 +10,8 @@ import { TeamEnvironmentUpdatedDocument, } from "../backend/graphql" import { TeamEnvironment } from "./TeamEnvironment" +import { Environment, EnvironmentSchemaVersion } from "@hoppscotch/data" +import { entityReference } from "verzod" type EntityType = "environment" type EntityID = `${EntityType}-${string}` @@ -112,19 +114,27 @@ export default class TeamEnvironmentAdapter { if (result.right.team) { results.push( - ...result.right.team.teamEnvironments.map( - (x) => - { - id: x.id, - teamID: x.teamID, - environment: { - v: 2, - id: x.id, - name: x.name, - variables: JSON.parse(x.variables), - }, - } - ) + ...result.right.team.teamEnvironments.map((x) => { + const environment = { + id: x.id, + name: x.name, + variables: JSON.parse(x.variables), + } + + const parsedEnvironment = + entityReference(Environment).safeParse(environment) + + return { + id: x.id, + teamID: x.teamID, + environment: parsedEnvironment.success + ? parsedEnvironment.data + : { + ...environment, + v: EnvironmentSchemaVersion, + }, + } + }) ) } diff --git a/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts b/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts index 67769fbd..4e9d8460 100644 --- a/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts +++ b/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts @@ -433,7 +433,7 @@ const HoppTestResultSchema = z .strict(), }) .strict(), - consoleEntries: z.array(z.record(z.string(), z.unknown()).optional()), + consoleEntries: z.optional(z.array(z.record(z.string(), z.unknown()))), }) .strict() diff --git a/packages/hoppscotch-data/src/environment/index.ts b/packages/hoppscotch-data/src/environment/index.ts index e2438a29..02feb02b 100644 --- a/packages/hoppscotch-data/src/environment/index.ts +++ b/packages/hoppscotch-data/src/environment/index.ts @@ -204,7 +204,7 @@ export const translateToNewEnvironmentVariables = ( return { key: x.key, initialValue: x.initialValue ?? x.value ?? "", - currentValue: x.currentValue ?? x.value ?? "", + currentValue: "", secret: false, } } diff --git a/packages/hoppscotch-data/src/environment/v/2.ts b/packages/hoppscotch-data/src/environment/v/2.ts index 7033dc9f..17ab3aef 100644 --- a/packages/hoppscotch-data/src/environment/v/2.ts +++ b/packages/hoppscotch-data/src/environment/v/2.ts @@ -32,7 +32,7 @@ export default defineVersion({ key, secret, initialValue: secret ? "" : variable.value, - currentValue: secret ? "" : variable.value, + currentValue: "", } }), } diff --git a/packages/hoppscotch-data/src/rest/index.ts b/packages/hoppscotch-data/src/rest/index.ts index 14c6d47f..d3f1ca14 100644 --- a/packages/hoppscotch-data/src/rest/index.ts +++ b/packages/hoppscotch-data/src/rest/index.ts @@ -17,9 +17,9 @@ import V7_VERSION, { HoppRESTHeaders, HoppRESTParams } from "./v/7" import V8_VERSION from "./v/8" import V9_VERSION from "./v/9" import V10_VERSION, { HoppRESTReqBody } from "./v/10" -import V11_VERSION, { HoppRESTRequestResponses } from "./v/11" +import V11_VERSION from "./v/11" import V12_VERSION from "./v/12" -import V13_VERSION, { HoppRESTAuth } from "./v/13" +import V13_VERSION, { HoppRESTAuth, HoppRESTRequestResponses } from "./v/13" export * from "./content-types" @@ -48,19 +48,19 @@ export { HoppRESTAuthDigest, PasswordGrantTypeParams } from "./v/8" export { FormDataKeyValue } from "./v/9" -export { - HoppRESTResponseOriginalRequest, - HoppRESTRequestResponse, - HoppRESTRequestResponses, - HoppRESTAuthOAuth2, - ClientCredentialsGrantTypeParams, -} from "./v/11" +export { HoppRESTAuthOAuth2, ClientCredentialsGrantTypeParams } from "./v/11" export { HoppRESTReqBody } from "./v/10" export { HoppRESTAuthHAWK, HoppRESTAuthAkamaiEdgeGrid } from "./v/12" -export { HoppRESTAuth, HoppRESTAuthJWT } from "./v/13" +export { + HoppRESTAuth, + HoppRESTAuthJWT, + HoppRESTRequestResponses, + HoppRESTResponseOriginalRequest, + HoppRESTRequestResponse, +} from "./v/13" const versionedObject = z.object({ // v is a stringified number @@ -127,6 +127,7 @@ const HoppRESTRequestEq = Eq.struct({ }) export const RESTReqSchemaVersion = "13" +export const RESTResOriginalReqSchemaVersion = "5" as const export type HoppRESTParam = HoppRESTRequest["params"][number] export type HoppRESTHeader = HoppRESTRequest["headers"][number] diff --git a/packages/hoppscotch-data/src/rest/v/12.ts b/packages/hoppscotch-data/src/rest/v/12.ts index d795f5fb..9f42b8b3 100644 --- a/packages/hoppscotch-data/src/rest/v/12.ts +++ b/packages/hoppscotch-data/src/rest/v/12.ts @@ -12,6 +12,11 @@ import { z } from "zod" import { defineVersion } from "verzod" import { HoppRESTAuthOAuth2, V11_SCHEMA } from "./11" +import { + HoppRESTResponseOriginalRequest as HoppRESTResponseOriginalRequestOld, + HoppRESTRequestResponse as HoppRESTRequestResponseOld, +} from "./9" + export const HoppRESTAuthHAWK = z.object({ authType: z.literal("hawk"), authId: z.string().catch(""), @@ -68,18 +73,56 @@ export const HoppRESTAuth = z export type HoppRESTAuth = z.infer +export const HoppRESTResponseOriginalRequest = + HoppRESTResponseOriginalRequestOld.extend({ + v: z.literal("4"), + auth: HoppRESTAuth, + }) + +export type HoppRESTResponseOriginalRequest = z.infer< + typeof HoppRESTResponseOriginalRequest +> + +export const HoppRESTRequestResponse = HoppRESTRequestResponseOld.extend({ + originalRequest: HoppRESTResponseOriginalRequest, +}) + +export type HoppRESTRequestResponse = z.infer + +export const HoppRESTRequestResponses = z.record( + z.string(), + HoppRESTRequestResponse +) + +export type HoppRESTRequestResponses = z.infer + export const V12_SCHEMA = V11_SCHEMA.extend({ v: z.literal("12"), auth: HoppRESTAuth, + responses: HoppRESTRequestResponses, }) export default defineVersion({ schema: V12_SCHEMA, initial: false, up(old: z.infer) { + // update the version number of response original request + const responses = Object.fromEntries( + Object.entries(old.responses).map(([key, response]) => [ + key, + { + ...response, + originalRequest: { + ...response.originalRequest, + v: "4" as const, + }, + }, + ]) + ) return { ...old, v: "12" as const, + responses, } }, }) diff --git a/packages/hoppscotch-data/src/rest/v/13.ts b/packages/hoppscotch-data/src/rest/v/13.ts index 29fd5317..994df695 100644 --- a/packages/hoppscotch-data/src/rest/v/13.ts +++ b/packages/hoppscotch-data/src/rest/v/13.ts @@ -14,6 +14,11 @@ import { z } from "zod" import { defineVersion } from "verzod" import { HoppRESTAuthOAuth2 } from "./11" +import { + HoppRESTResponseOriginalRequest as HoppRESTResponseOriginalRequestOld, + HoppRESTRequestResponse as HoppRESTRequestResponseOld, +} from "./9" + export const HoppRESTAuthJWT = z.object({ authType: z.literal("jwt"), secret: z.string().catch(""), @@ -66,18 +71,56 @@ export const HoppRESTAuth = z export type HoppRESTAuth = z.infer +export const HoppRESTResponseOriginalRequest = + HoppRESTResponseOriginalRequestOld.extend({ + v: z.literal("5"), + auth: HoppRESTAuth, + }) + +export type HoppRESTResponseOriginalRequest = z.infer< + typeof HoppRESTResponseOriginalRequest +> + +export const HoppRESTRequestResponse = HoppRESTRequestResponseOld.extend({ + originalRequest: HoppRESTResponseOriginalRequest, +}) + +export type HoppRESTRequestResponse = z.infer + +export const HoppRESTRequestResponses = z.record( + z.string(), + HoppRESTRequestResponse +) + +export type HoppRESTRequestResponses = z.infer + export const V13_SCHEMA = V12_SCHEMA.extend({ v: z.literal("13"), auth: HoppRESTAuth, + responses: HoppRESTRequestResponses, }) export default defineVersion({ schema: V13_SCHEMA, initial: false, - up(old: any) { + up(old: z.infer) { + // update the version number of response original request + const responses = Object.fromEntries( + Object.entries(old.responses).map(([key, response]) => [ + key, + { + ...response, + originalRequest: { + ...response.originalRequest, + v: "5" as const, + }, + }, + ]) + ) return { ...old, v: "13" as const, + responses, } }, }) diff --git a/packages/hoppscotch-data/src/rest/v/9.ts b/packages/hoppscotch-data/src/rest/v/9.ts index deba3c6f..ffacb537 100644 --- a/packages/hoppscotch-data/src/rest/v/9.ts +++ b/packages/hoppscotch-data/src/rest/v/9.ts @@ -101,7 +101,7 @@ export const HoppRESTRequestResponse = z.object({ name: z.string(), originalRequest: HoppRESTResponseOriginalRequest, status: z.string(), - code: z.optional(ValidCodes), + code: z.optional(ValidCodes).nullable().catch(null), headers: HoppRESTResponseHeaders, body: z.string(), }) diff --git a/packages/hoppscotch-selfhost-web/src/platform/environments/desktop/index.ts b/packages/hoppscotch-selfhost-web/src/platform/environments/desktop/index.ts index a8e5b878..02749f79 100644 --- a/packages/hoppscotch-selfhost-web/src/platform/environments/desktop/index.ts +++ b/packages/hoppscotch-selfhost-web/src/platform/environments/desktop/index.ts @@ -18,7 +18,11 @@ import { runGQLSubscription } from "@hoppscotch/common/helpers/backend/GQLClient import { environnmentsSyncer } from "@platform/environments/desktop/sync" import * as E from "fp-ts/Either" -import { GlobalEnvironment } from "@hoppscotch/data" +import { + Environment, + EnvironmentSchemaVersion, + GlobalEnvironment, +} from "@hoppscotch/data" import { runDispatchWithOutSyncing } from "@lib/sync" import { createUserGlobalEnvironment, @@ -82,13 +86,27 @@ async function loadUserEnvironments() { if (environments.length > 0) { runDispatchWithOutSyncing(() => { + const formatedEnvironments = environments.map( + (env) => + { + id: env.id, + name: env.name, + variables: JSON.parse(env.variables), + } + ) + replaceEnvironments( - environments.map(({ id, variables, name }) => ({ - v: 2, - id, - name, - variables: JSON.parse(variables), - })) + formatedEnvironments.map((environment) => { + const parsedEnv = + entityReference(Environment).safeParse(environment) + + return parsedEnv.success + ? parsedEnv.data + : { + ...environment, + v: EnvironmentSchemaVersion, + } + }) ) }) } diff --git a/packages/hoppscotch-selfhost-web/src/platform/environments/web/index.ts b/packages/hoppscotch-selfhost-web/src/platform/environments/web/index.ts index 9f381e62..03b0292a 100644 --- a/packages/hoppscotch-selfhost-web/src/platform/environments/web/index.ts +++ b/packages/hoppscotch-selfhost-web/src/platform/environments/web/index.ts @@ -18,7 +18,11 @@ import { EnvironmentsPlatformDef } from "@hoppscotch/common/src/platform/environ import { environnmentsSyncer } from "@platform/environments/web/sync" -import { GlobalEnvironment } from "@hoppscotch/data" +import { + Environment, + EnvironmentSchemaVersion, + GlobalEnvironment, +} from "@hoppscotch/data" import { runDispatchWithOutSyncing } from "@lib/sync" import { createUserGlobalEnvironment, @@ -82,13 +86,27 @@ async function loadUserEnvironments() { if (environments.length > 0) { runDispatchWithOutSyncing(() => { + const formatedEnvironments = environments.map( + (env) => + { + id: env.id, + name: env.name, + variables: JSON.parse(env.variables), + } + ) + replaceEnvironments( - environments.map(({ id, variables, name }) => ({ - v: 2, - id, - name, - variables: JSON.parse(variables), - })) + formatedEnvironments.map((environment) => { + const parsedEnv = + entityReference(Environment).safeParse(environment) + + return parsedEnv.success + ? parsedEnv.data + : { + ...environment, + v: EnvironmentSchemaVersion, + } + }) ) }) }