diff --git a/package-lock.json b/package-lock.json index 996c03e4ee..b8c28340d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,7 +67,8 @@ "ua-parser-js": "^1.0.35", "unset-value": "^2.0.1", "uuid": "^9.0.0", - "valid-url": "^1.0.9" + "valid-url": "^1.0.9", + "zod": "^3.22.4" }, "devDependencies": { "@commitlint/config-conventional": "^17.6.3", @@ -4804,15 +4805,73 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.5", - "license": "MIT", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dependencies": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { "version": "7.22.6", "dev": true, @@ -4851,11 +4910,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.5", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -4896,20 +4956,22 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -4994,8 +5056,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "license": "MIT", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } @@ -5022,11 +5085,12 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.5", - "license": "MIT", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -5035,7 +5099,8 @@ }, "node_modules/@babel/highlight/node_modules/ansi-styles": { "version": "3.2.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dependencies": { "color-convert": "^1.9.0" }, @@ -5045,7 +5110,8 @@ }, "node_modules/@babel/highlight/node_modules/chalk": { "version": "2.4.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -5057,25 +5123,29 @@ }, "node_modules/@babel/highlight/node_modules/color-convert": { "version": "1.9.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dependencies": { "color-name": "1.1.3" } }, "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/supports-color": { "version": "5.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dependencies": { "has-flag": "^3.0.0" }, @@ -5084,9 +5154,10 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.6", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, - "license": "MIT", "bin": { "parser": "bin/babel-parser.js" }, @@ -5258,31 +5329,33 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.6", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.6", - "@babel/types": "^7.22.5", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -5299,12 +5372,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.5", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -14790,7 +14864,8 @@ }, "node_modules/js-tokens": { "version": "4.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "4.1.0", @@ -16194,9 +16269,16 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.4", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -16958,7 +17040,9 @@ } }, "node_modules/postcss": { - "version": "8.4.21", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "dev": true, "funding": [ { @@ -16968,11 +17052,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -19955,6 +20042,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 8a7c9a2ebc..dca91b2a35 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,8 @@ "ua-parser-js": "^1.0.35", "unset-value": "^2.0.1", "uuid": "^9.0.0", - "valid-url": "^1.0.9" + "valid-url": "^1.0.9", + "zod": "^3.22.4" }, "devDependencies": { "@commitlint/config-conventional": "^17.6.3", diff --git a/test/integrations/component.test.ts b/test/integrations/component.test.ts index 8b2a2ec7bd..d0a4f75cb5 100644 --- a/test/integrations/component.test.ts +++ b/test/integrations/component.test.ts @@ -7,7 +7,13 @@ import axios from 'axios'; import bodyParser from 'koa-bodyparser'; import { Command } from 'commander'; import { createHttpTerminator } from 'http-terminator'; -import { MockHttpCallsData, TestCaseData } from './testTypes'; +import { + MockHttpCallsData, + TestCaseData, + processorTransformSchema, + routerTransformSchema, + batchTransformSchema, +} from './testTypes'; import { applicationRoutes } from '../../src/routes/index'; import MockAxiosAdapter from 'axios-mock-adapter'; import { @@ -21,6 +27,7 @@ import tags from '../../src/v0/util/tags'; import { Server } from 'http'; import { appendFileSync } from 'fs'; import { responses } from '../testHelper'; +import { validate } from '../../src/util/eventValidation'; // To run single destination test cases // npm run test:ts -- component --destination=adobe_analytics @@ -98,6 +105,19 @@ const rootDir = __dirname; const allTestDataFilePaths = getTestDataFilePaths(rootDir, opts); const DEFAULT_VERSION = 'v0'; +const validateSchema = (response, feature: string) => { + switch (feature) { + case tags.FEATURES.ROUTER: + routerTransformSchema.parse(response); + break; + case tags.FEATURES.BATCH: + batchTransformSchema.parse(response); + break; + case tags.FEATURES.PROCESSOR: + processorTransformSchema.parse(response); + break; + } +}; const testRoute = async (route, tcData: TestCaseData) => { const inputReq = tcData.input.request; const { headers, params, body } = inputReq; @@ -127,6 +147,7 @@ const testRoute = async (route, tcData: TestCaseData) => { if (outputResp?.body) { expect(response.body).toEqual(outputResp.body); + validateSchema(response.body, tcData.feature); } if (outputResp.headers !== undefined) { diff --git a/test/integrations/destinations/persistiq/router/data.ts b/test/integrations/destinations/persistiq/router/data.ts index 051a8a4bca..6f1050b41e 100644 --- a/test/integrations/destinations/persistiq/router/data.ts +++ b/test/integrations/destinations/persistiq/router/data.ts @@ -53,6 +53,9 @@ export const data = [ ], }, }, + metadata: { + jobId: 1, + }, }, ], destType: 'persistiq', @@ -102,7 +105,7 @@ export const data = [ persistIqAttributesMapping: [{ from: 'useroccupation', to: 'occupation' }], }, }, - metadata: [null], + metadata: [{ jobId: 1 }], statusCode: 200, }, ], diff --git a/test/integrations/testTypes.ts b/test/integrations/testTypes.ts index 51667e8044..864a658ba7 100644 --- a/test/integrations/testTypes.ts +++ b/test/integrations/testTypes.ts @@ -1,4 +1,5 @@ import { AxiosResponse } from 'axios'; +import { ZodTypeAny, z } from 'zod'; import MockAdapter from 'axios-mock-adapter'; export interface requestType { @@ -44,3 +45,96 @@ export type MockHttpCallsData = { httpReq: Record; httpRes: Partial; }; + +export const arrayOrSingleElement = (schema: ZodTypeAny) => z.union([z.array(schema), schema]); +export const formElementSchema = arrayOrSingleElement(z.union([z.string(), z.number(), z.boolean()])); +export const eventResponseSchema = z.union([ + z.object({ + error: z.string(), + statTags: z + .object({ + destType: z.string().optional(), + errorCategory: z.string().optional(), + errorType: z.string().optional(), + feature: z.string().optional(), + implementation: z.string().optional(), + module: z.string().optional(), + }) + .optional(), + statusCode: z.number().optional(), + }), + z.object({ + output: z + .object({ + version: z.string().optional(), + type: z.string().optional(), + method: z.string().optional(), + endpoint: z.string().optional(), + headers: z.record(z.string(), z.string()).optional(), + params: z.record(z.string(), z.any()).optional(), + body: z + .object({ + JSON: z.record(z.string(), z.any()).optional(), + XML: z + .object({ + payload: z.string().optional(), + }) + .optional(), + JSON_ARRAY: z + .object({ + batch: z.any().optional(), + }) + .optional(), + FORM: z.record(z.string(), formElementSchema).optional(), + GZIP: z + .object({ + payload: z.string().optional(), + }) + .optional(), + }) + .optional(), + files: z.record(z.string(), z.any()).optional(), + userId: z.string().optional(), + }) + .optional(), + metadata: z.record(z.string(), z.any()).optional(), + statusCode: z.number().optional(), + }), +]); + +export const batchTransformSchema = z.array( + z.object({ + batchedRequest: arrayOrSingleElement(eventResponseSchema).optional(), + metadata: z.array(z.record(z.string(), z.any())).optional(), + destination: z + .object({ + ID: z.string().optional(), + Name: z.string().optional(), + DestinationDefinition: z + .object({ + ID: z.string().optional(), + Name: z.string().optional(), + DisplayName: z.string().optional(), + Config: z.any().optional(), + ResponseRules: z.any().optional(), + }) + .optional(), + Config: z.any().optional(), + Enabled: z.boolean().optional(), + WorkspaceID: z.string().optional(), + Transformations: z.any().optional(), + IsProcessorEnabled: z.boolean().optional(), + RevisionID: z.string().optional(), + }) + .optional(), + batched: z.boolean().optional(), + statusCode: z.number().optional(), + error: z.string().optional(), + }), +); + +export const routerTransformSchema = z.object({ + output: batchTransformSchema, +}); + +export const processorTransformSchema = z.array(eventResponseSchema);