From 7621ff2961e77b9b417a9053bc97ac59e437e09c Mon Sep 17 00:00:00 2001 From: James George Date: Wed, 20 Mar 2024 20:13:22 +0530 Subject: [PATCH] feat: add extended support for versioned entities in the CLI (#3912) --- .../src/__tests__/commands/test.spec.ts | 102 +++++++++++++++--- .../functions/checks/isRESTCollection.spec.ts | 84 --------------- .../samples/collections/coll-v1-req-v0.json | 27 +++++ .../samples/collections/coll-v1-req-v1.json | 48 +++++++++ .../samples/collections/coll-v2-req-v2.json | 54 ++++++++++ .../samples/collections/coll-v2-req-v3.json | 54 ++++++++++ .../collection-level-headers-auth-coll.json | 12 +-- .../samples/collections/sample-coll.json | 26 +++++ .../samples/environments/env-v0.json | 9 ++ .../samples/environments/env-v1.json | 10 ++ .../hoppscotch-cli/src/options/test/env.ts | 2 +- packages/hoppscotch-cli/src/utils/checks.ts | 44 -------- packages/hoppscotch-cli/src/utils/mutators.ts | 34 +++++- .../hoppscotch-data/src/collection/index.ts | 5 +- 14 files changed, 352 insertions(+), 159 deletions(-) delete mode 100644 packages/hoppscotch-cli/src/__tests__/functions/checks/isRESTCollection.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v0.json create mode 100644 packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v1.json create mode 100644 packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v2.json create mode 100644 packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v3.json create mode 100644 packages/hoppscotch-cli/src/__tests__/samples/collections/sample-coll.json create mode 100644 packages/hoppscotch-cli/src/__tests__/samples/environments/env-v0.json create mode 100644 packages/hoppscotch-cli/src/__tests__/samples/environments/env-v1.json diff --git a/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts b/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts index 675e317509..25b3325d24 100644 --- a/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts +++ b/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts @@ -20,7 +20,7 @@ describe("Test `hopp test ` command:", () => { const out = getErrorCode(stderr); expect(out).toBe("INVALID_ARGUMENT"); }); - }) + }); describe("Supplied collection export file validations", () => { test("Errors with the code `FILE_NOT_FOUND` if the supplied collection export file doesn't exist", async () => { @@ -66,6 +66,43 @@ describe("Test `hopp test ` command:", () => { }); }); + describe("Versioned entities", () => { + describe("Collections & Requests", () => { + const testFixtures = [ + { fileName: "coll-v1-req-v0.json", collVersion: 1, reqVersion: 0 }, + { fileName: "coll-v1-req-v1.json", collVersion: 1, reqVersion: 1 }, + { fileName: "coll-v2-req-v2.json", collVersion: 2, reqVersion: 2 }, + { fileName: "coll-v2-req-v3.json", collVersion: 2, reqVersion: 3 }, + ]; + + testFixtures.forEach(({ collVersion, fileName, reqVersion }) => { + test(`Successfully processes a supplied collection export file where the collection is based on the "v${collVersion}" schema and the request following the "v${reqVersion}" schema`, async () => { + const args = `test ${getTestJsonFilePath(fileName, "collection")}`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }); + }); + }); + + describe("Environments", () => { + const testFixtures = [ + { fileName: "env-v0.json", version: 0 }, + { fileName: "env-v1.json", version: 1 }, + ]; + + testFixtures.forEach(({ fileName, version }) => { + test(`Successfully processes the supplied collection and environment export files where the environment is based on the "v${version}" schema`, async () => { + const ENV_PATH = getTestJsonFilePath(fileName, "environment"); + const args = `test ${getTestJsonFilePath("sample-coll.json", "collection")} --env ${ENV_PATH}`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }); + }); + }); + }); + test("Successfully processes a supplied collection export file of the expected format", async () => { const args = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`; const { error } = await runCLI(args); @@ -75,7 +112,8 @@ describe("Test `hopp test ` command:", () => { test("Successfully inherits headers and authorization set at the root collection", async () => { const args = `test ${getTestJsonFilePath( - "collection-level-headers-auth-coll.json", "collection" + "collection-level-headers-auth-coll.json", + "collection" )}`; const { error } = await runCLI(args); @@ -84,7 +122,8 @@ describe("Test `hopp test ` command:", () => { test("Persists environment variables set in the pre-request script for consumption in the test script", async () => { const args = `test ${getTestJsonFilePath( - "pre-req-script-env-var-persistence-coll.json", "collection" + "pre-req-script-env-var-persistence-coll.json", + "collection" )}`; const { error } = await runCLI(args); @@ -106,7 +145,8 @@ describe("Test `hopp test --env ` command:", () => { test("Errors with the code `INVALID_FILE_TYPE` if the supplied environment export file doesn't end with the `.json` extension", async () => { const args = `${VALID_TEST_ARGS} --env ${getTestJsonFilePath( - "notjson-coll.txt", "collection" + "notjson-coll.txt", + "collection" )}`; const { stderr } = await runCLI(args); @@ -123,7 +163,10 @@ describe("Test `hopp test --env ` command:", () => { }); test("Errors with the code `MALFORMED_ENV_FILE` on supplying a malformed environment export file", async () => { - const ENV_PATH = getTestJsonFilePath("malformed-envs.json", "environment"); + const ENV_PATH = getTestJsonFilePath( + "malformed-envs.json", + "environment" + ); const args = `${VALID_TEST_ARGS} --env ${ENV_PATH}`; const { stderr } = await runCLI(args); @@ -142,7 +185,10 @@ describe("Test `hopp test --env ` command:", () => { }); test("Successfully resolves values from the supplied environment export file", async () => { - const TESTS_PATH = getTestJsonFilePath("env-flag-tests-coll.json", "collection"); + const TESTS_PATH = getTestJsonFilePath( + "env-flag-tests-coll.json", + "collection" + ); const ENV_PATH = getTestJsonFilePath("env-flag-envs.json", "environment"); const args = `test ${TESTS_PATH} --env ${ENV_PATH}`; @@ -151,8 +197,14 @@ describe("Test `hopp test --env ` command:", () => { }); test("Successfully resolves environment variables referenced in the request body", async () => { - const COLL_PATH = getTestJsonFilePath("req-body-env-vars-coll.json", "collection"); - const ENVS_PATH = getTestJsonFilePath("req-body-env-vars-envs.json", "environment"); + const COLL_PATH = getTestJsonFilePath( + "req-body-env-vars-coll.json", + "collection" + ); + const ENVS_PATH = getTestJsonFilePath( + "req-body-env-vars-envs.json", + "environment" + ); const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; const { error } = await runCLI(args); @@ -160,7 +212,10 @@ describe("Test `hopp test --env ` command:", () => { }); test("Works with shorth `-e` flag", async () => { - const TESTS_PATH = getTestJsonFilePath("env-flag-tests-coll.json", "collection"); + const TESTS_PATH = getTestJsonFilePath( + "env-flag-tests-coll.json", + "collection" + ); const ENV_PATH = getTestJsonFilePath("env-flag-envs.json", "environment"); const args = `test ${TESTS_PATH} -e ${ENV_PATH}`; @@ -183,7 +238,10 @@ describe("Test `hopp test --env ` command:", () => { secretHeaderValue: "secret-header-value", }; - const COLL_PATH = getTestJsonFilePath("secret-envs-coll.json", "collection"); + const COLL_PATH = getTestJsonFilePath( + "secret-envs-coll.json", + "collection" + ); const ENVS_PATH = getTestJsonFilePath("secret-envs.json", "environment"); const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; @@ -197,8 +255,14 @@ describe("Test `hopp test --env ` command:", () => { // Prefers values specified in the environment export file over values set in the system environment test("Successfully picks the values for secret environment variables set directly in the environment export file and persists the environment variables set from the pre-request script", async () => { - const COLL_PATH = getTestJsonFilePath("secret-envs-coll.json", "collection"); - const ENVS_PATH = getTestJsonFilePath("secret-supplied-values-envs.json", "environment"); + const COLL_PATH = getTestJsonFilePath( + "secret-envs-coll.json", + "collection" + ); + const ENVS_PATH = getTestJsonFilePath( + "secret-supplied-values-envs.json", + "environment" + ); const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; const { error, stdout } = await runCLI(args); @@ -212,9 +276,13 @@ describe("Test `hopp test --env ` command:", () => { // Values set from the scripting context takes the highest precedence test("Setting values for secret environment variables from the pre-request script overrides values set at the supplied environment export file", async () => { const COLL_PATH = getTestJsonFilePath( - "secret-envs-persistence-coll.json", "collection" + "secret-envs-persistence-coll.json", + "collection" + ); + const ENVS_PATH = getTestJsonFilePath( + "secret-supplied-values-envs.json", + "environment" ); - const ENVS_PATH = getTestJsonFilePath("secret-supplied-values-envs.json", "environment"); const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; const { error, stdout } = await runCLI(args); @@ -227,10 +295,12 @@ describe("Test `hopp test --env ` command:", () => { test("Persists secret environment variable values set from the pre-request script for consumption in the request and post-request script context", async () => { const COLL_PATH = getTestJsonFilePath( - "secret-envs-persistence-scripting-coll.json", "collection" + "secret-envs-persistence-scripting-coll.json", + "collection" ); const ENVS_PATH = getTestJsonFilePath( - "secret-envs-persistence-scripting-envs.json", "environment" + "secret-envs-persistence-scripting-envs.json", + "environment" ); const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; diff --git a/packages/hoppscotch-cli/src/__tests__/functions/checks/isRESTCollection.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/checks/isRESTCollection.spec.ts deleted file mode 100644 index b9b30e9aca..0000000000 --- a/packages/hoppscotch-cli/src/__tests__/functions/checks/isRESTCollection.spec.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { isRESTCollection } from "../../../utils/checks"; - -describe("isRESTCollection", () => { - test("Undefined collection value.", () => { - expect(isRESTCollection(undefined)).toBeFalsy(); - }); - - test("Invalid id value.", () => { - expect( - isRESTCollection({ - v: 1, - name: "test", - id: 1, - }) - ).toBeFalsy(); - }); - - test("Invalid requests value.", () => { - expect( - isRESTCollection({ - v: 1, - name: "test", - id: "1", - requests: null, - }) - ).toBeFalsy(); - }); - - test("Invalid folders value.", () => { - expect( - isRESTCollection({ - v: 1, - name: "test", - id: "1", - requests: [], - folders: undefined, - }) - ).toBeFalsy(); - }); - - test("Invalid RESTCollection(s) in folders.", () => { - expect( - isRESTCollection({ - v: 1, - name: "test", - id: "1", - requests: [], - folders: [ - { - v: 1, - name: "test1", - id: "2", - requests: undefined, - folders: [], - }, - ], - }) - ).toBeFalsy(); - }); - - test("Invalid HoppRESTRequest(s) in requests.", () => { - expect( - isRESTCollection({ - v: 1, - name: "test", - id: "1", - requests: [{}], - folders: [], - }) - ).toBeFalsy(); - }); - - test("Valid RESTCollection.", () => { - expect( - isRESTCollection({ - v: 1, - name: "test", - id: "1", - requests: [], - folders: [], - }) - ).toBeTruthy(); - }); -}); diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v0.json b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v0.json new file mode 100644 index 0000000000..6393ba6ad3 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v0.json @@ -0,0 +1,27 @@ +{ + "v": 1, + "name": "coll-v1", + "folders": [], + "requests": [ + { + "url": "https://httpbin.org", + "path": "/get", + "headers": [ + { "key": "Inactive-Header", "value": "Inactive Header", "active": false }, + { "key": "Authorization", "value": "Bearer token123", "active": true } + ], + "params": [ + { "key": "key", "value": "value", "active": true }, + { "key": "inactive-key", "value": "inactive-param", "active": false } + ], + "name": "req-v0", + "method": "GET", + "preRequestScript": "", + "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"Inactive-Header\"]).toBe(undefined)\n})", + "contentType": "application/json", + "body": "", + "auth": "Bearer Token", + "bearerToken": "token123" + } + ] +} \ No newline at end of file diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v1.json b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v1.json new file mode 100644 index 0000000000..61f9adb52b --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v1.json @@ -0,0 +1,48 @@ +{ + "v": 1, + "name": "coll-v1", + "folders": [], + "requests": [ + { + "v": "1", + "endpoint": "https://httpbin.org/get", + "headers": [ + { + "key": "Inactive-Header", + "value": "Inactive Header", + "active": false + }, + { + "key": "Authorization", + "value": "Bearer token123", + "active": true + } + ], + "params": [ + { + "key": "key", + "value": "value", + "active": true + }, + { + "key": "inactive-key", + "value": "inactive-param", + "active": false + } + ], + "name": "req-v1", + "method": "GET", + "preRequestScript": "", + "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"Inactive-Header\"]).toBe(undefined)\n})", + "body": { + "contentType": null, + "body": null + }, + "auth": { + "authType": "bearer", + "authActive": true, + "token": "token123" + } + } + ] +} \ No newline at end of file diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v2.json b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v2.json new file mode 100644 index 0000000000..780373f828 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v2.json @@ -0,0 +1,54 @@ +{ + "v": 2, + "name": "coll-v2", + "folders": [], + "requests": [ + { + "v": "2", + "endpoint": "https://httpbin.org/get", + "headers": [ + { + "key": "Inactive-Header", + "value": "Inactive Header", + "active": false + }, + { + "key": "Authorization", + "value": "Bearer token123", + "active": true + } + ], + "params": [ + { + "key": "key", + "value": "value", + "active": true + }, + { + "key": "inactive-key", + "value": "inactive-param", + "active": false + } + ], + "name": "req-v2", + "method": "GET", + "preRequestScript": "", + "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"Inactive-Header\"]).toBe(undefined)\n})", + "body": { + "contentType": null, + "body": null + }, + "auth": { + "authType": "bearer", + "authActive": true, + "token": "token123" + }, + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [] +} \ No newline at end of file diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v3.json b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v3.json new file mode 100644 index 0000000000..fcce134a8d --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v3.json @@ -0,0 +1,54 @@ +{ + "v": 2, + "name": "coll-v2", + "folders": [], + "requests": [ + { + "v": "3", + "endpoint": "https://httpbin.org/get", + "headers": [ + { + "key": "Inactive-Header", + "value": "Inactive Header", + "active": false + }, + { + "key": "Authorization", + "value": "Bearer token123", + "active": true + } + ], + "params": [ + { + "key": "key", + "value": "value", + "active": true + }, + { + "key": "inactive-key", + "value": "inactive-param", + "active": false + } + ], + "name": "req-v3", + "method": "GET", + "preRequestScript": "", + "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"Inactive-Header\"]).toBe(undefined)\n})", + "body": { + "contentType": null, + "body": null + }, + "auth": { + "authType": "bearer", + "authActive": true, + "token": "token123" + }, + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [] +} \ No newline at end of file diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/collection-level-headers-auth-coll.json b/packages/hoppscotch-cli/src/__tests__/samples/collections/collection-level-headers-auth-coll.json index 5e779482f5..ca986c8ae7 100644 --- a/packages/hoppscotch-cli/src/__tests__/samples/collections/collection-level-headers-auth-coll.json +++ b/packages/hoppscotch-cli/src/__tests__/samples/collections/collection-level-headers-auth-coll.json @@ -1,18 +1,18 @@ [ { - "v": 1, + "v": 2, "name": "CollectionA", "folders": [ { - "v": 1, + "v": 2, "name": "FolderA", "folders": [ { - "v": 1, + "v": 2, "name": "FolderB", "folders": [ { - "v": 1, + "v": 2, "name": "FolderC", "folders": [], "requests": [ @@ -153,11 +153,11 @@ } }, { - "v": 1, + "v": 2, "name": "CollectionB", "folders": [ { - "v": 1, + "v": 2, "name": "FolderA", "folders": [], "requests": [ diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/sample-coll.json b/packages/hoppscotch-cli/src/__tests__/samples/collections/sample-coll.json new file mode 100644 index 0000000000..b0ca8cecf2 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/samples/collections/sample-coll.json @@ -0,0 +1,26 @@ +{ + "v": 1, + "name": "tests", + "folders": [], + "requests": [ + { + "v": "2", + "endpoint": "<>", + "name": "", + "params": [], + "headers": [], + "method": "GET", + "auth": { + "authType": "none", + "authActive": true + }, + "preRequestScript": "", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.method).toBe(\"GET\");\n pw.expect(pw.response.body.headers).toBeType(\"object\");\n});", + "body": { + "contentType": null, + "body": null + }, + "requestVariables": [] + } + ] +} \ No newline at end of file diff --git a/packages/hoppscotch-cli/src/__tests__/samples/environments/env-v0.json b/packages/hoppscotch-cli/src/__tests__/samples/environments/env-v0.json new file mode 100644 index 0000000000..16b232857c --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/samples/environments/env-v0.json @@ -0,0 +1,9 @@ +{ + "name": "env-v0", + "variables": [ + { + "key": "baseURL", + "value": "https://echo.hoppscotch.io" + } + ] +} \ No newline at end of file diff --git a/packages/hoppscotch-cli/src/__tests__/samples/environments/env-v1.json b/packages/hoppscotch-cli/src/__tests__/samples/environments/env-v1.json new file mode 100644 index 0000000000..4ab5aa65f7 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/samples/environments/env-v1.json @@ -0,0 +1,10 @@ +{ + "name": "env-v0", + "variables": [ + { + "key": "baseURL", + "value": "https://echo.hoppscotch.io", + "secret": false + } + ] + } \ No newline at end of file diff --git a/packages/hoppscotch-cli/src/options/test/env.ts b/packages/hoppscotch-cli/src/options/test/env.ts index 1f0be6de41..2cec231a1d 100644 --- a/packages/hoppscotch-cli/src/options/test/env.ts +++ b/packages/hoppscotch-cli/src/options/test/env.ts @@ -47,7 +47,7 @@ export async function parseEnvsData(path: string) { if (HoppEnvKeyPairResult.success) { for (const [key, value] of Object.entries(HoppEnvKeyPairResult.data)) { - envPairs.push({ key, value }); + envPairs.push({ key, value, secret: false }); } } else if (HoppEnvExportObjectResult.type === "ok") { envPairs.push(...HoppEnvExportObjectResult.value.variables); diff --git a/packages/hoppscotch-cli/src/utils/checks.ts b/packages/hoppscotch-cli/src/utils/checks.ts index f935cbe4ac..083aab7f2e 100644 --- a/packages/hoppscotch-cli/src/utils/checks.ts +++ b/packages/hoppscotch-cli/src/utils/checks.ts @@ -1,5 +1,3 @@ -import { HoppCollection, isHoppRESTRequest } from "@hoppscotch/data"; -import * as A from "fp-ts/Array"; import { CommanderError } from "commander"; import { HoppCLIError, HoppErrnoException } from "../types/errors"; @@ -14,48 +12,6 @@ export const hasProperty =

( prop: P ): target is Record => prop in target; -/** - * Typeguard to check valid Hoppscotch REST Collection. - * @param param The object to be checked. - * @returns True, if unknown parameter is valid Hoppscotch REST Collection; - * False, otherwise. - */ -export const isRESTCollection = (param: unknown): param is HoppCollection => { - if (!!param && typeof param === "object") { - if (!hasProperty(param, "v") || typeof param.v !== "number") { - return false; - } - if (!hasProperty(param, "name") || typeof param.name !== "string") { - return false; - } - if (hasProperty(param, "id") && typeof param.id !== "string") { - return false; - } - if (!hasProperty(param, "requests") || !Array.isArray(param.requests)) { - return false; - } else { - // Checks each requests array to be valid HoppRESTRequest. - const checkRequests = A.every(isHoppRESTRequest)(param.requests); - if (!checkRequests) { - return false; - } - } - if (!hasProperty(param, "folders") || !Array.isArray(param.folders)) { - return false; - } else { - // Checks each folder to be valid REST collection. - const checkFolders = A.every(isRESTCollection)(param.folders); - if (!checkFolders) { - return false; - } - } - - return true; - } - - return false; -}; - /** * Checks if given error data is of type HoppCLIError, based on existence * of code property. diff --git a/packages/hoppscotch-cli/src/utils/mutators.ts b/packages/hoppscotch-cli/src/utils/mutators.ts index 79053ed129..91b1383f4e 100644 --- a/packages/hoppscotch-cli/src/utils/mutators.ts +++ b/packages/hoppscotch-cli/src/utils/mutators.ts @@ -1,8 +1,11 @@ +import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data"; import fs from "fs/promises"; -import { FormDataEntry } from "../types/request"; +import { entityReference } from "verzod"; +import { z } from "zod"; + import { error } from "../types/errors"; -import { isRESTCollection, isHoppErrnoException } from "./checks"; -import { HoppCollection } from "@hoppscotch/data"; +import { FormDataEntry } from "../types/request"; +import { isHoppErrnoException } from "./checks"; /** * Parses array of FormDataEntry to FormData. @@ -67,7 +70,11 @@ export async function parseCollectionData( ? contents : [contents]; - if (maybeArrayOfCollections.some((x) => !isRESTCollection(x))) { + const collectionSchemaParsedResult = z + .array(entityReference(HoppCollection)) + .safeParse(maybeArrayOfCollections); + + if (!collectionSchemaParsedResult.success) { throw error({ code: "MALFORMED_COLLECTION", path, @@ -75,5 +82,22 @@ export async function parseCollectionData( }); } - return maybeArrayOfCollections as HoppCollection[]; + return collectionSchemaParsedResult.data.map((collection) => { + const requestSchemaParsedResult = z + .array(entityReference(HoppRESTRequest)) + .safeParse(collection.requests); + + if (!requestSchemaParsedResult.success) { + throw error({ + code: "MALFORMED_COLLECTION", + path, + data: "Please check the collection data.", + }); + } + + return { + ...collection, + requests: requestSchemaParsedResult.data, + }; + }); } diff --git a/packages/hoppscotch-data/src/collection/index.ts b/packages/hoppscotch-data/src/collection/index.ts index 47e5180c10..7ed072c94d 100644 --- a/packages/hoppscotch-data/src/collection/index.ts +++ b/packages/hoppscotch-data/src/collection/index.ts @@ -8,8 +8,7 @@ import { translateToNewRequest } from "../rest" import { translateToGQLRequest } from "../graphql" const versionedObject = z.object({ - // v is a stringified number - v: z.string().regex(/^\d+$/).transform(Number), + v: z.number(), }) export const HoppCollection = createVersionedEntity({ @@ -26,7 +25,7 @@ export const HoppCollection = createVersionedEntity({ // For V1 we have to check the schema const result = V1_VERSION.schema.safeParse(data) - return result.success ? 0 : null + return result.success ? 1 : null }, })