diff --git a/package-lock.json b/package-lock.json index 5d6484e4..fc8c5e5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@opentelemetry/sdk-trace-node": "^1.30.1", "@opentelemetry/semantic-conventions": "^1.17.0", "@protobuf-ts/twirp-transport": "^2.11.1", - "@use-tusk/drift-schemas": "^0.1.10", + "@use-tusk/drift-schemas": "^0.1.11", "import-in-the-middle": "^1.14.4", "install": "^0.13.0", "js-yaml": "^4.1.0", @@ -2441,9 +2441,9 @@ "license": "ISC" }, "node_modules/@use-tusk/drift-schemas": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/@use-tusk/drift-schemas/-/drift-schemas-0.1.10.tgz", - "integrity": "sha512-aG31cTfd5zdIOOSSrQwq/KsNYwtrSlb9cFozxCQHa1Suow8o8+z/I4G9XkGm86tp9S8jPlFkjQX4DKCBd1rxuQ==", + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@use-tusk/drift-schemas/-/drift-schemas-0.1.11.tgz", + "integrity": "sha512-KFgOV/Qt+9OgIW7sVizhyQ+DJIBxEfz8QlQYNre19udRpG2QWbvIF9ZeZ/ZJF4fJAMgPPcgjPbObKcVm/SwVQg==", "dependencies": { "@protobuf-ts/runtime": "^2.11.0", "@protobuf-ts/runtime-rpc": "^2.11.1" diff --git a/package.json b/package.json index 39511857..e1d4314d 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "@opentelemetry/sdk-trace-node": "^1.30.1", "@opentelemetry/semantic-conventions": "^1.17.0", "@protobuf-ts/twirp-transport": "^2.11.1", - "@use-tusk/drift-schemas": "^0.1.10", + "@use-tusk/drift-schemas": "^0.1.11", "import-in-the-middle": "^1.14.4", "install": "^0.13.0", "js-yaml": "^4.1.0", diff --git a/src/core/ProtobufCommunicator.ts b/src/core/ProtobufCommunicator.ts index de238136..aa3225e6 100644 --- a/src/core/ProtobufCommunicator.ts +++ b/src/core/ProtobufCommunicator.ts @@ -384,8 +384,8 @@ export class ProtobufCommunicator { submoduleName: span.submoduleName || "", inputValue: toStruct(span.inputValue), outputValue: toStruct(span.outputValue), - inputSchema: toStruct(span.inputSchema), - outputSchema: toStruct(span.outputSchema), + inputSchema: span.inputSchema, + outputSchema: span.outputSchema, inputSchemaHash: span.inputSchemaHash || "", outputSchemaHash: span.outputSchemaHash || "", inputValueHash: span.inputValueHash || "", diff --git a/src/core/tracing/JsonSchemaHelper.test.ts b/src/core/tracing/JsonSchemaHelper.test.ts index b96daece..b85e57c9 100644 --- a/src/core/tracing/JsonSchemaHelper.test.ts +++ b/src/core/tracing/JsonSchemaHelper.test.ts @@ -9,97 +9,100 @@ import { } from "./JsonSchemaHelper"; test("getDetailedType - should correctly identify primitive types", (t) => { - t.is(JsonSchemaHelper.getDetailedType(null), "NULL"); - t.is(JsonSchemaHelper.getDetailedType(undefined), "UNDEFINED"); - t.is(JsonSchemaHelper.getDetailedType("hello"), "STRING"); - t.is(JsonSchemaHelper.getDetailedType(42), "NUMBER"); - t.is(JsonSchemaHelper.getDetailedType(3.14), "NUMBER"); - t.is(JsonSchemaHelper.getDetailedType(true), "BOOLEAN"); - t.is(JsonSchemaHelper.getDetailedType(false), "BOOLEAN"); - t.is(JsonSchemaHelper.getDetailedType(BigInt(123)), "NUMBER"); - t.is(JsonSchemaHelper.getDetailedType(Symbol("test")), "STRING"); + t.is(JsonSchemaHelper.getDetailedType(null), JsonSchemaType.NULL); + t.is(JsonSchemaHelper.getDetailedType(undefined), JsonSchemaType.UNDEFINED); + t.is(JsonSchemaHelper.getDetailedType("hello"), JsonSchemaType.STRING); + t.is(JsonSchemaHelper.getDetailedType(42), JsonSchemaType.NUMBER); + t.is(JsonSchemaHelper.getDetailedType(3.14), JsonSchemaType.NUMBER); + t.is(JsonSchemaHelper.getDetailedType(true), JsonSchemaType.BOOLEAN); + t.is(JsonSchemaHelper.getDetailedType(false), JsonSchemaType.BOOLEAN); + t.is(JsonSchemaHelper.getDetailedType(BigInt(123)), JsonSchemaType.NUMBER); + t.is(JsonSchemaHelper.getDetailedType(Symbol("test")), JsonSchemaType.STRING); }); test("getDetailedType - should correctly identify object types", (t) => { - t.is(JsonSchemaHelper.getDetailedType({}), "OBJECT"); - t.is(JsonSchemaHelper.getDetailedType([]), "ORDERED_LIST"); - t.is(JsonSchemaHelper.getDetailedType(new Date()), "STRING"); - t.is(JsonSchemaHelper.getDetailedType(/regex/), "OBJECT"); - t.is(JsonSchemaHelper.getDetailedType(new Error("test")), "OBJECT"); - t.is(JsonSchemaHelper.getDetailedType(new Set()), "UNORDERED_LIST"); - t.is(JsonSchemaHelper.getDetailedType(new Map()), "OBJECT"); - t.is(JsonSchemaHelper.getDetailedType(() => {}), "FUNCTION"); + t.is(JsonSchemaHelper.getDetailedType({}), JsonSchemaType.OBJECT); + t.is(JsonSchemaHelper.getDetailedType([]), JsonSchemaType.ORDERED_LIST); + t.is(JsonSchemaHelper.getDetailedType(new Date()), JsonSchemaType.STRING); + t.is(JsonSchemaHelper.getDetailedType(/regex/), JsonSchemaType.OBJECT); + t.is(JsonSchemaHelper.getDetailedType(new Error("test")), JsonSchemaType.OBJECT); + t.is(JsonSchemaHelper.getDetailedType(new Set()), JsonSchemaType.UNORDERED_LIST); + t.is(JsonSchemaHelper.getDetailedType(new Map()), JsonSchemaType.OBJECT); + t.is(JsonSchemaHelper.getDetailedType(() => {}), JsonSchemaType.FUNCTION); }); test("getDetailedType - should correctly identify typed arrays", (t) => { - t.is(JsonSchemaHelper.getDetailedType(new Int8Array()), "STRING"); - t.is(JsonSchemaHelper.getDetailedType(new Uint8Array()), "STRING"); - t.is(JsonSchemaHelper.getDetailedType(new Uint8ClampedArray()), "STRING"); - t.is(JsonSchemaHelper.getDetailedType(new Int16Array()), "STRING"); - t.is(JsonSchemaHelper.getDetailedType(new Uint16Array()), "STRING"); - t.is(JsonSchemaHelper.getDetailedType(new Int32Array()), "STRING"); - t.is(JsonSchemaHelper.getDetailedType(new Uint32Array()), "STRING"); - t.is(JsonSchemaHelper.getDetailedType(new Float32Array()), "STRING"); - t.is(JsonSchemaHelper.getDetailedType(new Float64Array()), "STRING"); - t.is(JsonSchemaHelper.getDetailedType(new DataView(new ArrayBuffer(8))), "STRING"); - t.is(JsonSchemaHelper.getDetailedType(new ArrayBuffer(8)), "STRING"); + t.is(JsonSchemaHelper.getDetailedType(new Int8Array()), JsonSchemaType.STRING); + t.is(JsonSchemaHelper.getDetailedType(new Uint8Array()), JsonSchemaType.STRING); + t.is(JsonSchemaHelper.getDetailedType(new Uint8ClampedArray()), JsonSchemaType.STRING); + t.is(JsonSchemaHelper.getDetailedType(new Int16Array()), JsonSchemaType.STRING); + t.is(JsonSchemaHelper.getDetailedType(new Uint16Array()), JsonSchemaType.STRING); + t.is(JsonSchemaHelper.getDetailedType(new Int32Array()), JsonSchemaType.STRING); + t.is(JsonSchemaHelper.getDetailedType(new Uint32Array()), JsonSchemaType.STRING); + t.is(JsonSchemaHelper.getDetailedType(new Float32Array()), JsonSchemaType.STRING); + t.is(JsonSchemaHelper.getDetailedType(new Float64Array()), JsonSchemaType.STRING); + t.is(JsonSchemaHelper.getDetailedType(new DataView(new ArrayBuffer(8))), JsonSchemaType.STRING); + t.is(JsonSchemaHelper.getDetailedType(new ArrayBuffer(8)), JsonSchemaType.STRING); }); test("getDetailedType - should handle arguments object", (t) => { function testFunc() { return JsonSchemaHelper.getDetailedType(arguments); } - t.is(testFunc(), "ORDERED_LIST"); + t.is(testFunc(), JsonSchemaType.ORDERED_LIST); }); test("getDetailedType - should fall back to STRING for unknown types", (t) => { const unknownObj = Object.create(null); Object.setPrototypeOf(unknownObj, null); - t.is(JsonSchemaHelper.getDetailedType(unknownObj), "OBJECT"); + t.is(JsonSchemaHelper.getDetailedType(unknownObj), JsonSchemaType.OBJECT); }); test("generateSchema - should generate schema for primitive types", (t) => { - t.deepEqual(JsonSchemaHelper.generateSchema(null), { type: "NULL" }); - t.deepEqual(JsonSchemaHelper.generateSchema(undefined), { type: "UNDEFINED" }); - t.deepEqual(JsonSchemaHelper.generateSchema("test"), { type: "STRING" }); - t.deepEqual(JsonSchemaHelper.generateSchema(42), { type: "NUMBER" }); - t.deepEqual(JsonSchemaHelper.generateSchema(true), { type: "BOOLEAN" }); + t.deepEqual(JsonSchemaHelper.generateSchema(null), { type: JsonSchemaType.NULL, properties: {} }); + t.deepEqual(JsonSchemaHelper.generateSchema(undefined), { type: JsonSchemaType.UNDEFINED, properties: {} }); + t.deepEqual(JsonSchemaHelper.generateSchema("test"), { type: JsonSchemaType.STRING, properties: {} }); + t.deepEqual(JsonSchemaHelper.generateSchema(42), { type: JsonSchemaType.NUMBER, properties: {} }); + t.deepEqual(JsonSchemaHelper.generateSchema(true), { type: JsonSchemaType.BOOLEAN, properties: {} }); }); test("generateSchema - should generate schema for arrays", (t) => { t.deepEqual(JsonSchemaHelper.generateSchema([]), { - type: "ORDERED_LIST", - items: null, + type: JsonSchemaType.ORDERED_LIST, + properties: {}, }); t.deepEqual(JsonSchemaHelper.generateSchema([1, 2, 3]), { - type: "ORDERED_LIST", - items: { type: "NUMBER" }, + type: JsonSchemaType.ORDERED_LIST, + items: { type: JsonSchemaType.NUMBER, properties: {} }, + properties: {}, }); t.deepEqual(JsonSchemaHelper.generateSchema(["a", "b"]), { - type: "ORDERED_LIST", - items: { type: "STRING" }, + type: JsonSchemaType.ORDERED_LIST, + items: { type: JsonSchemaType.STRING, properties: {} }, + properties: {}, }); t.deepEqual(JsonSchemaHelper.generateSchema([{ id: 1 }]), { - type: "ORDERED_LIST", + type: JsonSchemaType.ORDERED_LIST, items: { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { - id: { type: "NUMBER" }, + id: { type: JsonSchemaType.NUMBER, properties: {} }, }, }, + properties: {}, }); }); test("generateSchema - should generate schema for objects", (t) => { const simpleObj = { name: "John", age: 30 }; t.deepEqual(JsonSchemaHelper.generateSchema(simpleObj), { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { - name: { type: "STRING" }, - age: { type: "NUMBER" }, + name: { type: JsonSchemaType.STRING, properties: {} }, + age: { type: JsonSchemaType.NUMBER, properties: {} }, }, }); @@ -111,15 +114,15 @@ test("generateSchema - should generate schema for objects", (t) => { }, }; t.deepEqual(JsonSchemaHelper.generateSchema(nestedObj), { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { user: { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { profile: { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { - name: { type: "STRING" }, + name: { type: JsonSchemaType.STRING, properties: {} }, }, }, }, @@ -131,27 +134,29 @@ test("generateSchema - should generate schema for objects", (t) => { test("generateSchema - should generate schema for Set objects", (t) => { const emptySet = new Set(); t.deepEqual(JsonSchemaHelper.generateSchema(emptySet), { - type: "UNORDERED_LIST", - items: null, + type: JsonSchemaType.UNORDERED_LIST, + properties: {}, }); const numberSet = new Set([1, 2, 3]); t.deepEqual(JsonSchemaHelper.generateSchema(numberSet), { - type: "UNORDERED_LIST", - items: { type: "NUMBER" }, + type: JsonSchemaType.UNORDERED_LIST, + items: { type: JsonSchemaType.NUMBER, properties: {} }, + properties: {}, }); const stringSet = new Set(["a", "b"]); t.deepEqual(JsonSchemaHelper.generateSchema(stringSet), { - type: "UNORDERED_LIST", - items: { type: "STRING" }, + type: JsonSchemaType.UNORDERED_LIST, + items: { type: JsonSchemaType.STRING, properties: {} }, + properties: {}, }); }); test("generateSchema - should generate schema for Map objects", (t) => { const emptyMap = new Map(); t.deepEqual(JsonSchemaHelper.generateSchema(emptyMap), { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: {}, }); @@ -161,10 +166,10 @@ test("generateSchema - should generate schema for Map objects", (t) => { ] as [string, any][]); t.deepEqual(JsonSchemaHelper.generateSchema(map), { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { - key1: { type: "STRING" }, - key2: { type: "NUMBER" }, + key1: { type: JsonSchemaType.STRING, properties: {} }, + key2: { type: JsonSchemaType.NUMBER, properties: {} }, }, }); }); @@ -184,14 +189,15 @@ test("generateSchema - should apply schema merges", (t) => { const schema = JsonSchemaHelper.generateSchema(data, merges); t.deepEqual(schema, { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { body: { - type: "STRING", + type: JsonSchemaType.STRING, encoding: EncodingType.BASE64, decodedType: DecodedType.JSON, + properties: {}, }, - header: { type: JsonSchemaType.STRING }, + header: { type: JsonSchemaType.STRING, properties: {} }, }, }); }); @@ -213,13 +219,13 @@ test("generateSchema - should not apply schema merges to nested properties with const schema = JsonSchemaHelper.generateSchema(data, merges); t.deepEqual(schema, { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { body: { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { - title: { type: "STRING" }, - body: { type: "STRING" }, // Should NOT have encoding/decodedType + title: { type: JsonSchemaType.STRING, properties: {} }, + body: { type: JsonSchemaType.STRING, properties: {} }, // Should NOT have encoding/decodedType }, encoding: EncodingType.BASE64, decodedType: DecodedType.JSON, @@ -320,10 +326,10 @@ test("generateSchemaAndHash - should generate schema and hashes for simple data" const result = JsonSchemaHelper.generateSchemaAndHash(data); t.deepEqual(result.schema, { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { - name: { type: "STRING" }, - age: { type: "NUMBER" }, + name: { type: JsonSchemaType.STRING, properties: {} }, + age: { type: JsonSchemaType.NUMBER, properties: {} }, }, }); t.is(typeof result.decodedValueHash, "string"); @@ -351,17 +357,17 @@ test("generateSchemaAndHash - should handle schema merges with base64 encoding", const result = JsonSchemaHelper.generateSchemaAndHash(data, schemaMerges); t.deepEqual(result.schema, { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { body: { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { - message: { type: "STRING" }, + message: { type: JsonSchemaType.STRING, properties: {} }, }, encoding: EncodingType.BASE64, decodedType: DecodedType.JSON, }, - contentType: { type: "STRING" }, + contentType: { type: JsonSchemaType.STRING, properties: {} }, }, }); @@ -380,10 +386,10 @@ test("generateSchemaAndHash - should normalize data by removing undefined values const result = JsonSchemaHelper.generateSchemaAndHash(data); t.deepEqual(result.schema, { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { - name: { type: "STRING" }, - city: { type: "STRING" }, + name: { type: JsonSchemaType.STRING, properties: {} }, + city: { type: JsonSchemaType.STRING, properties: {} }, }, }); t.false(Object.prototype.hasOwnProperty.call(result.schema.properties, "age")); @@ -406,7 +412,8 @@ test("generateSchemaAndHash - should handle decoding errors gracefully", (t) => // The warning message is expected and demonstrates graceful error handling const result = JsonSchemaHelper.generateSchemaAndHash(data, schemaMerges); t.deepEqual(result.schema.properties?.body, { - type: "STRING", + type: JsonSchemaType.STRING, + properties: {}, encoding: EncodingType.BASE64, decodedType: DecodedType.JSON, }); @@ -422,19 +429,20 @@ test("generateSchemaAndHash - should handle empty objects and arrays", (t) => { const result = JsonSchemaHelper.generateSchemaAndHash(data); t.deepEqual(result.schema, { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: { emptyObj: { - type: "OBJECT", + type: JsonSchemaType.OBJECT, properties: {}, }, emptyArr: { - type: "ORDERED_LIST", - items: null, + type: JsonSchemaType.ORDERED_LIST, + properties: {}, }, items: { - type: "ORDERED_LIST", - items: { type: "NUMBER" }, + type: JsonSchemaType.ORDERED_LIST, + items: { type: JsonSchemaType.NUMBER, properties: {} }, + properties: {}, }, }, }); @@ -443,42 +451,44 @@ test("generateSchemaAndHash - should handle empty objects and arrays", (t) => { test("getTypeMapping - should return the type mapping object", (t) => { const mapping = JsonSchemaHelper.getTypeMapping(); t.truthy(mapping); - t.is(mapping.string, "STRING"); - t.is(mapping.number, "NUMBER"); - t.is(mapping.boolean, "BOOLEAN"); - t.is(mapping.null, "NULL"); - t.is(mapping.undefined, "UNDEFINED"); - t.is(mapping.Array, "ORDERED_LIST"); - t.is(mapping.object, "OBJECT"); + t.is(mapping.string, JsonSchemaType.STRING); + t.is(mapping.number, JsonSchemaType.NUMBER); + t.is(mapping.boolean, JsonSchemaType.BOOLEAN); + t.is(mapping.null, JsonSchemaType.NULL); + t.is(mapping.undefined, JsonSchemaType.UNDEFINED); + t.is(mapping.Array, JsonSchemaType.ORDERED_LIST); + t.is(mapping.object, JsonSchemaType.OBJECT); }); test("EncodingType and DecodedType enums - should have correct EncodingType values", (t) => { - t.is(EncodingType.BASE64, "BASE64"); + t.is(EncodingType.BASE64, 1); + t.is(EncodingType.UNSPECIFIED, 0); }); test("EncodingType and DecodedType enums - should have correct DecodedType values", (t) => { - t.is(DecodedType.JSON, "JSON"); - t.is(DecodedType.HTML, "HTML"); - t.is(DecodedType.CSS, "CSS"); - t.is(DecodedType.JAVASCRIPT, "JAVASCRIPT"); - t.is(DecodedType.XML, "XML"); - t.is(DecodedType.YAML, "YAML"); - t.is(DecodedType.MARKDOWN, "MARKDOWN"); - t.is(DecodedType.CSV, "CSV"); - t.is(DecodedType.SQL, "SQL"); - t.is(DecodedType.GRAPHQL, "GRAPHQL"); - t.is(DecodedType.PLAIN_TEXT, "PLAIN_TEXT"); - t.is(DecodedType.FORM_DATA, "FORM_DATA"); - t.is(DecodedType.MULTIPART_FORM, "MULTIPART_FORM"); - t.is(DecodedType.PDF, "PDF"); - t.is(DecodedType.AUDIO, "AUDIO"); - t.is(DecodedType.VIDEO, "VIDEO"); - t.is(DecodedType.GZIP, "GZIP"); - t.is(DecodedType.BINARY, "BINARY"); - t.is(DecodedType.JPEG, "JPEG"); - t.is(DecodedType.PNG, "PNG"); - t.is(DecodedType.GIF, "GIF"); - t.is(DecodedType.WEBP, "WEBP"); - t.is(DecodedType.SVG, "SVG"); - t.is(DecodedType.ZIP, "ZIP"); + t.is(DecodedType.UNSPECIFIED, 0); + t.is(DecodedType.JSON, 1); + t.is(DecodedType.HTML, 2); + t.is(DecodedType.CSS, 3); + t.is(DecodedType.JAVASCRIPT, 4); + t.is(DecodedType.XML, 5); + t.is(DecodedType.YAML, 6); + t.is(DecodedType.MARKDOWN, 7); + t.is(DecodedType.CSV, 8); + t.is(DecodedType.SQL, 9); + t.is(DecodedType.GRAPHQL, 10); + t.is(DecodedType.PLAIN_TEXT, 11); + t.is(DecodedType.FORM_DATA, 12); + t.is(DecodedType.MULTIPART_FORM, 13); + t.is(DecodedType.PDF, 14); + t.is(DecodedType.AUDIO, 15); + t.is(DecodedType.VIDEO, 16); + t.is(DecodedType.GZIP, 17); + t.is(DecodedType.BINARY, 18); + t.is(DecodedType.JPEG, 19); + t.is(DecodedType.PNG, 20); + t.is(DecodedType.GIF, 21); + t.is(DecodedType.WEBP, 22); + t.is(DecodedType.SVG, 23); + t.is(DecodedType.ZIP, 24); }); diff --git a/src/core/tracing/JsonSchemaHelper.ts b/src/core/tracing/JsonSchemaHelper.ts index 20fa9dd1..76f241d1 100644 --- a/src/core/tracing/JsonSchemaHelper.ts +++ b/src/core/tracing/JsonSchemaHelper.ts @@ -1,17 +1,11 @@ import * as crypto from "crypto"; import { logger } from "../utils/logger"; - -export enum JsonSchemaType { - NUMBER = "NUMBER", - STRING = "STRING", - BOOLEAN = "BOOLEAN", - NULL = "NULL", - UNDEFINED = "UNDEFINED", - OBJECT = "OBJECT", - ORDERED_LIST = "ORDERED_LIST", - UNORDERED_LIST = "UNORDERED_LIST", - FUNCTION = "FUNCTION", -} +import { + JsonSchemaType, + EncodingType, + DecodedType, + type JsonSchema, +} from "@use-tusk/drift-schemas/core/json_schema"; // Standardized schema type mapping const jsToJsonSchemaTypeMapping = { @@ -47,45 +41,8 @@ const jsToJsonSchemaTypeMapping = { ["Arguments"]: JsonSchemaType.ORDERED_LIST, } as const; -export enum EncodingType { - BASE64 = "BASE64", -} - -export enum DecodedType { - JSON = "JSON", - HTML = "HTML", - CSS = "CSS", - JAVASCRIPT = "JAVASCRIPT", - XML = "XML", - YAML = "YAML", - MARKDOWN = "MARKDOWN", - CSV = "CSV", - SQL = "SQL", - GRAPHQL = "GRAPHQL", - PLAIN_TEXT = "PLAIN_TEXT", - FORM_DATA = "FORM_DATA", - MULTIPART_FORM = "MULTIPART_FORM", - PDF = "PDF", - AUDIO = "AUDIO", - VIDEO = "VIDEO", - GZIP = "GZIP", - BINARY = "BINARY", - JPEG = "JPEG", - PNG = "PNG", - GIF = "GIF", - WEBP = "WEBP", - SVG = "SVG", - ZIP = "ZIP", -} - -export interface JsonSchema { - type: JsonSchemaType; - properties?: Record; - items?: JsonSchema | null; - encoding?: EncodingType; - decodedType?: DecodedType; - matchImportance?: number; // Should be between 0 and 1, 0 being the lowest importance and 1 being the highest importance -} +// Re-export proto types for convenience +export { JsonSchemaType, EncodingType, DecodedType, type JsonSchema }; // The following types can be merged with the generated schema to provide additional information // This is set in the instrumentation layer and merged with the generated schema @@ -212,39 +169,49 @@ export class JsonSchemaHelper { /** * Generate schema from data object using standardized types + * + * Note: We properties always exists on JsonSchema because proto3 maps cannot be marked optional. + * The JSON data is a bit inefficient because of this, but the easiest way to handle this is to keep it for now. */ static generateSchema(data: any, schemaMerges?: SchemaMerges): JsonSchema { if (data === null) { - return { type: jsToJsonSchemaTypeMapping["null"] }; + return { type: jsToJsonSchemaTypeMapping["null"], properties: {} }; } if (data === undefined) { - return { type: jsToJsonSchemaTypeMapping["undefined"] }; + return { type: jsToJsonSchemaTypeMapping["undefined"], properties: {} }; } const detailedType = JsonSchemaHelper.getDetailedType(data); if (detailedType === JsonSchemaType.ORDERED_LIST) { if (Array.isArray(data) && data.length === 0) { - return { type: JsonSchemaType.ORDERED_LIST, items: null }; + return { type: JsonSchemaType.ORDERED_LIST, properties: {} }; + } + const items = Array.isArray(data) && data.length > 0 ? JsonSchemaHelper.generateSchema(data[0]) : undefined; + if (items !== undefined) { + return { + type: JsonSchemaType.ORDERED_LIST, + items, + properties: {}, + }; } - return { - type: JsonSchemaType.ORDERED_LIST, - items: - Array.isArray(data) && data.length > 0 ? JsonSchemaHelper.generateSchema(data[0]) : null, - }; + return { type: JsonSchemaType.ORDERED_LIST, properties: {} }; } if (detailedType === JsonSchemaType.UNORDERED_LIST) { // Handle Set objects if (data instanceof Set) { const firstItem = data.size > 0 ? data.values().next().value : null; - return { - type: JsonSchemaType.UNORDERED_LIST, - items: firstItem !== null ? JsonSchemaHelper.generateSchema(firstItem) : null, - }; + if (firstItem !== null) { + return { + type: JsonSchemaType.UNORDERED_LIST, + items: JsonSchemaHelper.generateSchema(firstItem), + properties: {}, + }; + } } - return { type: JsonSchemaType.UNORDERED_LIST, items: null }; + return { type: JsonSchemaType.UNORDERED_LIST, properties: {} }; } if (detailedType === JsonSchemaType.OBJECT) { @@ -253,35 +220,31 @@ export class JsonSchemaHelper { // Handle Map objects if (data instanceof Map) { data.forEach((value, key) => { - if (schema.properties) { - const keyString = String(key); - const generatedSchema = JsonSchemaHelper.generateSchema(value); - - // Check for schema override for this key - if (schemaMerges && schemaMerges[keyString]) { - schema.properties[keyString] = JsonSchemaHelper.mergeSchemaWithMerges( - generatedSchema, - schemaMerges[keyString], - ); - } else { - schema.properties[keyString] = generatedSchema; - } + const keyString = String(key); + const generatedSchema = JsonSchemaHelper.generateSchema(value); + + // Check for schema override for this key + if (schemaMerges && schemaMerges[keyString]) { + schema.properties[keyString] = JsonSchemaHelper.mergeSchemaWithMerges( + generatedSchema, + schemaMerges[keyString], + ); + } else { + schema.properties[keyString] = generatedSchema; } }); } else if (typeof data === "object") { Object.keys(data).forEach((key) => { - if (schema.properties) { - const generatedSchema = JsonSchemaHelper.generateSchema(data[key]); - - // Check for schema override for this key - if (schemaMerges && schemaMerges[key]) { - schema.properties[key] = JsonSchemaHelper.mergeSchemaWithMerges( - generatedSchema, - schemaMerges[key], - ); - } else { - schema.properties[key] = generatedSchema; - } + const generatedSchema = JsonSchemaHelper.generateSchema(data[key]); + + // Check for schema override for this key + if (schemaMerges && schemaMerges[key]) { + schema.properties[key] = JsonSchemaHelper.mergeSchemaWithMerges( + generatedSchema, + schemaMerges[key], + ); + } else { + schema.properties[key] = generatedSchema; } }); } @@ -289,8 +252,8 @@ export class JsonSchemaHelper { return schema; } - // For primitive types, return the standardized type - return { type: detailedType }; + // For primitive types, return just the type + return { type: detailedType, properties: {} }; } /** diff --git a/src/core/tracing/SpanTransformer.ts b/src/core/tracing/SpanTransformer.ts index 343622dc..b1603dbb 100644 --- a/src/core/tracing/SpanTransformer.ts +++ b/src/core/tracing/SpanTransformer.ts @@ -1,6 +1,6 @@ import { ReadableSpan } from "@opentelemetry/sdk-trace-base"; import { SpanKind as OtSpanKind } from "@opentelemetry/api"; -import { JsonSchemaHelper, JsonSchema, JsonSchemaType } from "./JsonSchemaHelper"; +import { JsonSchemaHelper, JsonSchemaType, JsonSchema } from "./JsonSchemaHelper"; import { CleanSpanData, MetadataObject, TdSpanAttributes } from "../types"; import { PackageType, StatusCode } from "@use-tusk/drift-schemas/core/span"; import { logger, OriginalGlobalUtils } from "../utils"; diff --git a/src/core/tracing/adapters/ApiSpanAdapter.ts b/src/core/tracing/adapters/ApiSpanAdapter.ts index b54b4454..c1f962d5 100644 --- a/src/core/tracing/adapters/ApiSpanAdapter.ts +++ b/src/core/tracing/adapters/ApiSpanAdapter.ts @@ -91,8 +91,8 @@ export class ApiSpanAdapter implements SpanExportAdapter { packageType: cleanSpan.packageType || PackageType.UNSPECIFIED, inputValue: toStruct(cleanSpan.inputValue), outputValue: toStruct(cleanSpan.outputValue), - inputSchema: toStruct(cleanSpan.inputSchema), - outputSchema: toStruct(cleanSpan.outputSchema), + inputSchema: cleanSpan.inputSchema, + outputSchema: cleanSpan.outputSchema, inputSchemaHash: cleanSpan.inputSchemaHash || "", outputSchemaHash: cleanSpan.outputSchemaHash || "", inputValueHash: cleanSpan.inputValueHash || "", diff --git a/src/core/types.ts b/src/core/types.ts index 7c3bf001..d7231148 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -1,6 +1,7 @@ import { createContextKey } from "@opentelemetry/api"; import { StatusCode, PackageType } from "@use-tusk/drift-schemas/core/span"; import { SpanKind } from "@opentelemetry/api"; +import type { JsonSchema } from "@use-tusk/drift-schemas/core/json_schema"; export const REPLAY_TRACE_ID_CONTEXT_KEY = createContextKey("td.replayTraceId"); export const SPAN_KIND_CONTEXT_KEY = createContextKey("td.spanKind"); @@ -60,11 +61,12 @@ export type CleanSpanData = { packageType?: PackageType; - // keep these as plain JSON for readability + // Values are kept as plain JSON for readability inputValue: unknown; outputValue: unknown; - inputSchema: unknown; - outputSchema: unknown; + // Schemas use the proto JsonSchema type + inputSchema: JsonSchema; + outputSchema?: JsonSchema; // optional bc replay outbound spans don't have output schemas inputSchemaHash: string; outputSchemaHash: string; diff --git a/src/instrumentation/libraries/fetch/integration-tests/drop.test.ts b/src/instrumentation/libraries/fetch/integration-tests/drop.test.ts index ec091b9f..4023f498 100644 --- a/src/instrumentation/libraries/fetch/integration-tests/drop.test.ts +++ b/src/instrumentation/libraries/fetch/integration-tests/drop.test.ts @@ -112,11 +112,11 @@ test("should drop fetch span data but keep span present when calling dropped end // Should have transformMetadata indicating it was dropped t.truthy(droppedSpan!.transformMetadata, "Expected transformMetadata to be defined"); - const hasDropAction = droppedSpan!.transformMetadata.actions?.some( + const hasDropAction = droppedSpan!.transformMetadata?.actions?.some( (action) => action.type === "drop" && action.field === "entire_span", ); t.truthy( hasDropAction, - `Expected drop action in transformMetadata, got ${JSON.stringify(droppedSpan!.transformMetadata.actions)}`, + `Expected drop action in transformMetadata, got ${JSON.stringify(droppedSpan!.transformMetadata?.actions)}`, ); }); diff --git a/src/instrumentation/libraries/http/HttpTransformEngine.test.ts b/src/instrumentation/libraries/http/HttpTransformEngine.test.ts index fa0ea693..a2e655be 100644 --- a/src/instrumentation/libraries/http/HttpTransformEngine.test.ts +++ b/src/instrumentation/libraries/http/HttpTransformEngine.test.ts @@ -79,10 +79,10 @@ const exampleServerSpanData: HttpSpanData = { "user-agent": "test-client", }, httpVersion: "1.1", - body: { + body: Buffer.from(JSON.stringify({ username: "john@example.com", password: "secretPassword123", - }, + })).toString("base64"), bodySize: 100, } as HttpServerInputValue, outputValue: { @@ -112,7 +112,7 @@ const stripeClientSpanData: HttpSpanData = { "content-type": "application/json", }, protocol: "https", - body: { + body: Buffer.from(JSON.stringify({ amount: 1000, currency: "usd", source: { @@ -123,7 +123,7 @@ const stripeClientSpanData: HttpSpanData = { cvc: "123", }, }, - }, + })).toString("base64"), bodySize: 200, } as HttpClientInputValue, outputValue: { @@ -131,6 +131,11 @@ const stripeClientSpanData: HttpSpanData = { headers: { "content-type": "application/json", }, + httpVersion: "1.1", + httpVersionMajor: 1, + httpVersionMinor: 1, + complete: true, + readable: false, body: Buffer.from(JSON.stringify({ id: "ch_123456", amount: 1000, @@ -154,12 +159,12 @@ const multiTransformServerSpan: HttpSpanData = { "X-API-Key": "secret-api-key", }, httpVersion: "1.1", - body: { + body: Buffer.from(JSON.stringify({ user: { password: "userPassword", email: "user@example.com", }, - }, + })).toString("base64"), bodySize: 150, } as HttpServerInputValue, }; @@ -201,7 +206,9 @@ test("redacts sensitive fields on inbound login spans", (t) => { t.truthy(transformed); const span = transformed as HttpSpanData; - t.regex((span.inputValue as HttpServerInputValue).body.password, /^PWD_[0-9a-f]{12}\.\.\.$/); + const decodedBody = Buffer.from((span.inputValue as HttpServerInputValue).body, "base64").toString("utf-8"); + const parsedBody = JSON.parse(decodedBody); + t.regex(parsedBody.password, /^PWD_[0-9a-f]{12}\.\.\.$/); t.is(span.transformMetadata?.actions.length, 1); t.is(span.transformMetadata?.actions[0].type, "redact"); t.is(span.transformMetadata?.actions[0].field, "jsonPath:$.password"); @@ -244,7 +251,9 @@ test("applies multiple transforms to a single span", (t) => { const inputValue = span.inputValue as HttpServerInputValue; t.is(inputValue.url, "http://localhost:3000/api/user/lookup?ssn=XXXXXXXXXXX&email=user%40example.com"); t.is(inputValue.target, "/api/user/lookup?ssn=XXXXXXXXXXX&email=user%40example.com"); - t.is(inputValue.body.user.password, "HIDDEN"); + const decodedBody = Buffer.from(inputValue.body, "base64").toString("utf-8"); + const parsedBody = JSON.parse(decodedBody); + t.is(parsedBody.user.password, "HIDDEN"); t.is(span.transformMetadata?.actions.length, 2); const actionTypes = span.transformMetadata!.actions.map((a) => ({ type: a.type, field: a.field })); t.true(actionTypes.some((a) => a.type === "mask" && a.field === "queryParam:ssn")); @@ -269,7 +278,7 @@ test("identifies inbound requests that should be dropped", (t) => { const engine = new HttpTransformEngine(inboundDropConfig); t.true( - engine.shouldDropInboundRequest("POST", "http://localhost:3000/api/auth/login", "localhost", { + engine.shouldDropInboundRequest("POST", "http://localhost:3000/api/auth/login", { "content-type": "application/json", }), ); @@ -278,13 +287,12 @@ test("identifies inbound requests that should be dropped", (t) => { engine.shouldDropInboundRequest( "POST", "http://localhost:3000/api/other/endpoint", - "localhost", { "content-type": "application/json" }, ), ); t.false( - engine.shouldDropInboundRequest("GET", "http://localhost:3000/api/auth/login", "localhost", { + engine.shouldDropInboundRequest("GET", "http://localhost:3000/api/auth/login", { "content-type": "application/json", }), ); @@ -328,7 +336,8 @@ test("correctly matches inbound spans by extracting hostname from URL", (t) => { const engine = new HttpTransformEngine(inboundHostConfig); const result = engine.applyTransforms(cloneSpan(exampleServerSpanData)); - t.is((result.inputValue as HttpServerInputValue).body, "[REDACTED]"); + const decodedBody = Buffer.from((result.inputValue as HttpServerInputValue).body, "base64").toString("utf-8"); + t.is(decodedBody, "[REDACTED]"); }); test("correctly matches outbound spans using path field", (t) => { @@ -368,7 +377,8 @@ test("correctly matches inbound spans using url field", (t) => { const engine = new HttpTransformEngine(inboundPathConfig); const result = engine.applyTransforms(cloneSpan(exampleServerSpanData)); - t.is((result.inputValue as HttpServerInputValue).body, "[REDACTED]"); + const decodedBody = Buffer.from((result.inputValue as HttpServerInputValue).body, "base64").toString("utf-8"); + t.is(decodedBody, "[REDACTED]"); }); // edge cases and error handling @@ -406,10 +416,10 @@ test("handles spans with missing body", (t) => { const spanWithoutBody = cloneSpan(exampleServerSpanData); spanWithoutBody.inputValue = { ...(spanWithoutBody.inputValue as HttpServerInputValue), - body: undefined, + body: "", }; - const result = engine.applyTransforms(spanWithoutBody as any); + const result = engine.applyTransforms(spanWithoutBody); t.is(result.transformMetadata, undefined); }); @@ -430,7 +440,7 @@ test("handles spans with null/undefined headers", (t) => { const spanWithoutHeaders = cloneSpan(exampleServerSpanData); spanWithoutHeaders.inputValue = { ...(spanWithoutHeaders.inputValue as HttpServerInputValue), - headers: undefined, + headers: {} as any, }; const result = engine.applyTransforms(spanWithoutHeaders as any); @@ -454,7 +464,9 @@ test("applies mask transform with custom mask character", (t) => { const engine = new HttpTransformEngine(maskConfig); const result = engine.applyTransforms(cloneSpan(exampleServerSpanData)); - t.is((result.inputValue as HttpServerInputValue).body.password, "#################"); + const decodedBody = Buffer.from((result.inputValue as HttpServerInputValue).body, "base64").toString("utf-8"); + const parsedBody = JSON.parse(decodedBody); + t.is(parsedBody.password, "#################"); }); test("applies redact transform with custom hash prefix", (t) => { @@ -473,7 +485,9 @@ test("applies redact transform with custom hash prefix", (t) => { const engine = new HttpTransformEngine(redactConfig); const result = engine.applyTransforms(cloneSpan(exampleServerSpanData)); - t.regex((result.inputValue as HttpServerInputValue).body.password, /^HIDDEN_[0-9a-f]{12}\.\.\.$/); + const decodedBody = Buffer.from((result.inputValue as HttpServerInputValue).body, "base64").toString("utf-8"); + const parsedBody = JSON.parse(decodedBody); + t.regex(parsedBody.password, /^HIDDEN_[0-9a-f]{12}\.\.\.$/); }); test("applies replace transform", (t) => { @@ -495,7 +509,9 @@ test("applies replace transform", (t) => { const engine = new HttpTransformEngine(replaceConfig); const result = engine.applyTransforms(cloneSpan(exampleServerSpanData)); - t.is((result.inputValue as HttpServerInputValue).body.username, "anonymous@example.com"); + const decodedBody = Buffer.from((result.inputValue as HttpServerInputValue).body, "base64").toString("utf-8"); + const parsedBody = JSON.parse(decodedBody); + t.is(parsedBody.username, "anonymous@example.com"); }); // matcher combinations @@ -516,7 +532,9 @@ test("matches spans by method only", (t) => { const engine = new HttpTransformEngine(methodOnlyConfig); const result = engine.applyTransforms(cloneSpan(exampleServerSpanData)); - t.regex((result.inputValue as HttpServerInputValue).body.password, /^REDACTED_[0-9a-f]{12}\.\.\.$/); + const decodedBody = Buffer.from((result.inputValue as HttpServerInputValue).body, "base64").toString("utf-8"); + const parsedBody = JSON.parse(decodedBody); + t.regex(parsedBody.password, /^REDACTED_[0-9a-f]{12}\.\.\.$/); }); test("matches multiple methods when array is provided", (t) => { @@ -536,7 +554,9 @@ test("matches multiple methods when array is provided", (t) => { const engine = new HttpTransformEngine(multiMethodConfig); const postResult = engine.applyTransforms(cloneSpan(exampleServerSpanData)); - t.is((postResult.inputValue as HttpServerInputValue).body.username, "ARRAY_MATCHED"); + let decodedBody = Buffer.from((postResult.inputValue as HttpServerInputValue).body, "base64").toString("utf-8"); + let parsedBody = JSON.parse(decodedBody); + t.is(parsedBody.username, "ARRAY_MATCHED"); const putSpan = cloneSpan(exampleServerSpanData); putSpan.inputValue = { @@ -544,7 +564,9 @@ test("matches multiple methods when array is provided", (t) => { method: "PUT", }; const putResult = engine.applyTransforms(putSpan); - t.is((putResult.inputValue as HttpServerInputValue).body.username, "ARRAY_MATCHED"); + decodedBody = Buffer.from((putResult.inputValue as HttpServerInputValue).body, "base64").toString("utf-8"); + parsedBody = JSON.parse(decodedBody); + t.is(parsedBody.username, "ARRAY_MATCHED"); const getSpan = cloneSpan(exampleServerSpanData); getSpan.inputValue = { @@ -552,7 +574,9 @@ test("matches multiple methods when array is provided", (t) => { method: "GET", }; const getResult = engine.applyTransforms(getSpan); - t.is((getResult.inputValue as HttpServerInputValue).body.username, "john@example.com"); + decodedBody = Buffer.from((getResult.inputValue as HttpServerInputValue).body, "base64").toString("utf-8"); + parsedBody = JSON.parse(decodedBody); + t.is(parsedBody.username, "john@example.com"); t.is(getResult.transformMetadata, undefined); }); @@ -574,7 +598,9 @@ test("does not match when method doesn't match", (t) => { const mismatchSpan = cloneSpan(exampleServerSpanData); const result = engine.applyTransforms(mismatchSpan); - t.is((result.inputValue as HttpServerInputValue).body.password, "secretPassword123"); + const decodedBody = Buffer.from((result.inputValue as HttpServerInputValue).body, "base64").toString("utf-8"); + const parsedBody = JSON.parse(decodedBody); + t.is(parsedBody.password, "secretPassword123"); t.is(result.transformMetadata, undefined); }); @@ -596,7 +622,9 @@ test("does not match when direction doesn't match", (t) => { const outboundMismatchSpan = cloneSpan(exampleServerSpanData); const result = engine.applyTransforms(outboundMismatchSpan); - t.is((result.inputValue as HttpServerInputValue).body.password, "secretPassword123"); + const decodedBody = Buffer.from((result.inputValue as HttpServerInputValue).body, "base64").toString("utf-8"); + const parsedBody = JSON.parse(decodedBody); + t.is(parsedBody.password, "secretPassword123"); t.is(result.transformMetadata, undefined); }); @@ -674,7 +702,7 @@ test("transforms query parameter in server URL with existing params", (t) => { const inputValue = result.inputValue as HttpServerInputValue; t.regex(inputValue.url, /http:\/\/localhost:3000\/api\/test\?token=REDACTED_[0-9a-f]{12}\.\.\.&other=value/); - t.regex(inputValue.target, /\/api\/test\?token=REDACTED_[0-9a-f]{12}\.\.\.&other=value/); + t.regex(inputValue.target!, /\/api\/test\?token=REDACTED_[0-9a-f]{12}\.\.\.&other=value/); }); test("transforms query parameter in client path with existing params", (t) => { @@ -700,7 +728,7 @@ test("transforms query parameter in client path with existing params", (t) => { const result = engine.applyTransforms(spanWithQuery); const inputValue = result.inputValue as HttpClientInputValue; - t.regex(inputValue.path, /\/v1\/charges\?api_key=REDACTED_[0-9a-f]{12}\.\.\.&other=value/); + t.regex(inputValue.path!, /\/v1\/charges\?api_key=REDACTED_[0-9a-f]{12}\.\.\.&other=value/); }); test("handles query param that doesn't exist", (t) => { @@ -847,7 +875,8 @@ test("extracts hostname from inbound server URL for host matching", (t) => { const engine = new HttpTransformEngine(hostConfig); const result = engine.applyTransforms(spanWithApiHost); - t.is((result.inputValue as HttpServerInputValue).body, "[REDACTED]"); + const decodedBody = Buffer.from((result.inputValue as HttpServerInputValue).body, "base64").toString("utf-8"); + t.is(decodedBody, "[REDACTED]"); }); test("handles malformed URLs gracefully in hostname extraction", (t) => { diff --git a/src/instrumentation/libraries/http/integration-tests/outbound-drop.test.ts b/src/instrumentation/libraries/http/integration-tests/outbound-drop.test.ts index ffda61b8..a4812ee3 100644 --- a/src/instrumentation/libraries/http/integration-tests/outbound-drop.test.ts +++ b/src/instrumentation/libraries/http/integration-tests/outbound-drop.test.ts @@ -112,11 +112,11 @@ test("should drop outbound span data but keep span present when calling dropped // Should have transformMetadata indicating it was dropped t.truthy(droppedSpan!.transformMetadata, "Expected transformMetadata to be defined"); - const hasDropAction = droppedSpan!.transformMetadata.actions?.some( + const hasDropAction = droppedSpan!.transformMetadata?.actions?.some( (action) => action.type === "drop" && action.field === "entire_span", ); t.truthy( hasDropAction, - `Expected drop action in transformMetadata, got ${JSON.stringify(droppedSpan!.transformMetadata.actions)}`, + `Expected drop action in transformMetadata, got ${JSON.stringify(droppedSpan!.transformMetadata?.actions)}`, ); }); diff --git a/src/instrumentation/libraries/http/integration-tests/small.test.ts b/src/instrumentation/libraries/http/integration-tests/small.test.ts index 8644ce6e..4ca66aa8 100644 --- a/src/instrumentation/libraries/http/integration-tests/small.test.ts +++ b/src/instrumentation/libraries/http/integration-tests/small.test.ts @@ -37,7 +37,7 @@ const express = require("express"); const serviceAApp = express(); serviceAApp.use(express.json()); -serviceAApp.post("/api/sensitive", (req, res) => { +serviceAApp.post("/api/sensitive", (req: any, res: any) => { res.json({ status: "success", sensitiveData: "TOP_SECRET_123", @@ -45,7 +45,7 @@ serviceAApp.post("/api/sensitive", (req, res) => { }); }); -serviceAApp.get("/api/data", (req, res) => { +serviceAApp.get("/api/data", (req: any, res: any) => { res.json({ data: "public data from service A" }); }); @@ -71,8 +71,8 @@ test.after.always(() => { test("should be able to reach the server", async (t) => { await new Promise((resolve) => { - http.get(`http://127.0.0.1:${port}/api/data`, (res) => { - res.on("data", function (chunk) { + http.get(`http://127.0.0.1:${port}/api/data`, (res: any) => { + res.on("data", function (chunk: any) { console.log("BODY: " + chunk); }); diff --git a/src/instrumentation/libraries/http/types.ts b/src/instrumentation/libraries/http/types.ts index 773410c2..025320e4 100644 --- a/src/instrumentation/libraries/http/types.ts +++ b/src/instrumentation/libraries/http/types.ts @@ -7,7 +7,6 @@ import type { ServerResponse, IncomingHttpHeaders, } from "http"; -import { HttpBodyType } from "./utils"; export type HttpProtocol = "http" | "https"; @@ -21,7 +20,6 @@ export interface HttpClientInputValue { timeout?: number; body?: unknown; bodySize?: number; - bodyType?: HttpBodyType; hasBodyParsingError?: boolean; [key: string]: unknown; } diff --git a/src/instrumentation/libraries/http/utils/HttpUtils.test.ts b/src/instrumentation/libraries/http/utils/HttpUtils.test.ts index bb800007..459902d5 100644 --- a/src/instrumentation/libraries/http/utils/HttpUtils.test.ts +++ b/src/instrumentation/libraries/http/utils/HttpUtils.test.ts @@ -11,9 +11,9 @@ import { } from './binaryDataHandler'; import { httpBodyEncoder, - getDecodedType, - HttpBodyType + getDecodedType } from './httpBodyEncoder'; +import { DecodedType } from '@use-tusk/drift-schemas/core/json_schema'; // Body Decompression Tests test("decompressBuffer - should decompress gzip encoded data", async (t) => { @@ -161,14 +161,14 @@ test("httpBodyEncoder - should handle unsupported content encoding gracefully", }); test("getDecodedType - should return correct decoded type for JSON content types", (t) => { - t.is(getDecodedType('application/json'), HttpBodyType.JSON); - t.is(getDecodedType('application/json; charset=utf-8'), HttpBodyType.JSON); + t.is(getDecodedType('application/json'), DecodedType.JSON); + t.is(getDecodedType('application/json; charset=utf-8'), DecodedType.JSON); }); test("getDecodedType - should return correct decoded type for text content types", (t) => { // Based on the actual enum and mapping, text/plain maps to PLAIN_TEXT, not TEXT - t.is(getDecodedType('text/plain'), 'PLAIN_TEXT'); - t.is(getDecodedType('text/html'), 'HTML'); + t.is(getDecodedType('text/plain'), DecodedType.PLAIN_TEXT); + t.is(getDecodedType('text/html'), DecodedType.HTML); }); test("getDecodedType - should return undefined for unknown content types", (t) => { @@ -177,18 +177,11 @@ test("getDecodedType - should return undefined for unknown content types", (t) = }); test("getDecodedType - should handle array of content types", (t) => { - t.is(getDecodedType(['application/json', 'text/plain']), HttpBodyType.JSON); + t.is(getDecodedType(['application/json', 'text/plain']), DecodedType.JSON); }); test("getDecodedType - should handle case insensitive content types", (t) => { - t.is(getDecodedType('APPLICATION/JSON'), HttpBodyType.JSON); -}); - -test("HttpBodyType - should have correct enum values", (t) => { - t.is(HttpBodyType.JSON, 'JSON'); - t.is(HttpBodyType.TEXT, 'TEXT'); - t.is(HttpBodyType.RAW, 'RAW'); - t.is(HttpBodyType.NONE, 'NONE'); + t.is(getDecodedType('APPLICATION/JSON'), DecodedType.JSON); }); // Integration Tests @@ -208,7 +201,7 @@ test("Integration - should work together - decompress, detect encoding, and enco // Get type const type = getDecodedType('application/json'); - t.is(type, HttpBodyType.JSON); + t.is(type, DecodedType.JSON); }); test("Integration - should handle binary data flow", async (t) => { diff --git a/src/instrumentation/libraries/http/utils/httpBodyEncoder.test.ts b/src/instrumentation/libraries/http/utils/httpBodyEncoder.test.ts index 0e095e43..b0b4d163 100644 --- a/src/instrumentation/libraries/http/utils/httpBodyEncoder.test.ts +++ b/src/instrumentation/libraries/http/utils/httpBodyEncoder.test.ts @@ -1,7 +1,7 @@ import test from 'ava'; import * as zlib from 'zlib'; -import { httpBodyEncoder, getDecodedType, HttpBodyType } from './httpBodyEncoder'; -import { DecodedType } from '../../../../core/tracing/JsonSchemaHelper'; +import { httpBodyEncoder, getDecodedType } from './httpBodyEncoder'; +import { DecodedType } from '@use-tusk/drift-schemas/core/json_schema'; // httpBodyEncoder function test('should base64 encode a simple buffer', async (t) => { @@ -265,14 +265,3 @@ test('should handle content types with complex parameters', (t) => { const complexContentType = 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW; charset=utf-8'; t.is(getDecodedType(complexContentType), DecodedType.MULTIPART_FORM); }); - -// HttpBodyType enum -test('should have correct enum values', (t) => { - t.is(HttpBodyType.NONE, 'NONE'); - t.is(HttpBodyType.JSON, 'JSON'); - t.is(HttpBodyType.TEXT, 'TEXT'); - t.is(HttpBodyType.RAW, 'RAW'); - t.is(HttpBodyType.X_WWW_URL_FORM_URLENCODED, 'X_WWW_URL_FORM_URLENCODED'); - t.is(HttpBodyType.MULTIPART, 'MULTIPART'); - t.is(HttpBodyType.UNSPECIFIED, 'UNSPECIFIED'); -}); diff --git a/src/instrumentation/libraries/http/utils/httpBodyEncoder.ts b/src/instrumentation/libraries/http/utils/httpBodyEncoder.ts index 4eeafe5c..e829618d 100644 --- a/src/instrumentation/libraries/http/utils/httpBodyEncoder.ts +++ b/src/instrumentation/libraries/http/utils/httpBodyEncoder.ts @@ -1,17 +1,7 @@ import { decompressBuffer } from "./bodyDecompression"; -import { DecodedType } from "../../../../core/tracing/JsonSchemaHelper"; +import { DecodedType } from "@use-tusk/drift-schemas/core/json_schema"; import { logger } from "../../../../core/utils/logger"; -export enum HttpBodyType { - NONE = "NONE", - JSON = "JSON", - TEXT = "TEXT", - RAW = "RAW", - X_WWW_URL_FORM_URLENCODED = "X_WWW_URL_FORM_URLENCODED", - MULTIPART = "MULTIPART", - UNSPECIFIED = "UNSPECIFIED", -} - // Mapping from content-type to decoded type const CONTENT_TYPE_MAPPING: Record = { // JSON diff --git a/src/instrumentation/libraries/http/utils/index.test.ts b/src/instrumentation/libraries/http/utils/index.test.ts index e90a2baa..03006760 100644 --- a/src/instrumentation/libraries/http/utils/index.test.ts +++ b/src/instrumentation/libraries/http/utils/index.test.ts @@ -13,12 +13,9 @@ test("HTTP Utils Index - should export functions from binaryDataHandler", (t) => t.is(typeof httpUtils.combineChunks, "function"); }); -test("HTTP Utils Index - should export functions and enums from httpBodyEncoder", (t) => { +test("HTTP Utils Index - should export functions from httpBodyEncoder", (t) => { t.is(typeof httpUtils.httpBodyEncoder, "function"); t.is(typeof httpUtils.getDecodedType, "function"); - t.is(typeof httpUtils.HttpBodyType, "object"); - t.is(httpUtils.HttpBodyType.JSON, "JSON"); - t.is(httpUtils.HttpBodyType.TEXT, "TEXT"); }); test("HTTP Utils Index - should have all expected exports available", (t) => { @@ -35,8 +32,7 @@ test("HTTP Utils Index - should have all expected exports available", (t) => { // From httpBodyEncoder 'httpBodyEncoder', - 'getDecodedType', - 'HttpBodyType' + 'getDecodedType' ]; expectedExports.forEach(exportName => { @@ -44,16 +40,6 @@ test("HTTP Utils Index - should have all expected exports available", (t) => { }); }); -test("HTTP Utils Index - should export HttpBodyType enum with correct values", (t) => { - t.is(httpUtils.HttpBodyType.NONE, "NONE"); - t.is(httpUtils.HttpBodyType.JSON, "JSON"); - t.is(httpUtils.HttpBodyType.TEXT, "TEXT"); - t.is(httpUtils.HttpBodyType.RAW, "RAW"); - t.is(httpUtils.HttpBodyType.X_WWW_URL_FORM_URLENCODED, "X_WWW_URL_FORM_URLENCODED"); - t.is(httpUtils.HttpBodyType.MULTIPART, "MULTIPART"); - t.is(httpUtils.HttpBodyType.UNSPECIFIED, "UNSPECIFIED"); -}); - test("HTTP Utils Index - should work together - decompress, detect encoding, and encode", async (t) => { const originalData = 'Hello, World! This is integration test data.'; const originalBuffer = Buffer.from(originalData, 'utf8'); diff --git a/src/instrumentation/libraries/jsonwebtoken/integration-tests/jsonwebtoken.test.ts b/src/instrumentation/libraries/jsonwebtoken/integration-tests/jsonwebtoken.test.ts index 83fb7004..406d7aba 100644 --- a/src/instrumentation/libraries/jsonwebtoken/integration-tests/jsonwebtoken.test.ts +++ b/src/instrumentation/libraries/jsonwebtoken/integration-tests/jsonwebtoken.test.ts @@ -104,7 +104,7 @@ test.serial("should capture spans for asynchronous sign operation with callback" await new Promise((resolve, reject) => { withRootSpan(() => { - jwt.sign(payload, TEST_SECRET, {}, async (err, token) => { + jwt.sign(payload, TEST_SECRET, {}, async (err: any, token: any) => { try { t.is(err, null); t.truthy(token); @@ -177,7 +177,7 @@ test.serial("should capture spans for synchronous verify operation", async (t) = test.serial("should capture spans for asynchronous verify operation with callback", async (t) => { await new Promise((resolve, reject) => { withRootSpan(() => { - jwt.verify(validToken, TEST_SECRET, {}, async (err, decoded: any) => { + jwt.verify(validToken, TEST_SECRET, {}, async (err: any, decoded: any) => { try { t.is(err, null); t.truthy(decoded); diff --git a/src/instrumentation/libraries/pg/integration-tests/pg.test.ts b/src/instrumentation/libraries/pg/integration-tests/pg.test.ts index 74a6c71b..9fc802c7 100644 --- a/src/instrumentation/libraries/pg/integration-tests/pg.test.ts +++ b/src/instrumentation/libraries/pg/integration-tests/pg.test.ts @@ -55,7 +55,7 @@ function withRootSpan(fn: () => T): T { } let spanAdapter: InMemorySpanAdapter; -let client: Client; +let client: InstanceType; test.before(async (t) => { spanAdapter = new InMemorySpanAdapter(); @@ -204,7 +204,7 @@ test.serial("should capture spans for UPDATE operations", async (t) => { test.serial("should capture spans for callback-style queries", async (t) => { await new Promise((resolve, reject) => { withRootSpan(() => { - client.query("SELECT COUNT(*) as count FROM test_users", async (err, result) => { + client.query("SELECT COUNT(*) as count FROM test_users", async (err: any, result: any) => { try { t.is(err, null); t.true(parseInt(result.rows[0].count) >= 2); diff --git a/tsconfig.json b/tsconfig.json index 82c48a61..f52de3de 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,8 +35,6 @@ "exclude": [ "node_modules", "dist", - "**/*.test.ts", - "**/*.spec.ts", "**/e2e-tests/**" ] }