confluent-schema-registry 3.3.5 → 3.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +42 -1
  2. package/bin/avdlToAVSC.sh +1 -1
  3. package/dist/JsonSchema.js +1 -1
  4. package/dist/JsonSchema.js.map +1 -1
  5. package/dockest-error.json +3 -7
  6. package/jest.setup.ts +23 -13
  7. package/package.json +11 -17
  8. package/src/@types.ts +6 -13
  9. package/src/AvroHelper.ts +2 -3
  10. package/src/JsonSchema.ts +27 -15
  11. package/src/SchemaRegistry.json.spec.ts +18 -1
  12. package/src/SchemaRegistry.newApi.spec.ts +1 -1
  13. package/src/SchemaRegistry.spec.ts +15 -0
  14. package/src/SchemaRegistry.ts +16 -17
  15. package/src/api/index.spec.ts +24 -2
  16. package/src/api/index.ts +11 -2
  17. package/src/api/middleware/errorMiddleware.ts +1 -1
  18. package/src/constants.ts +1 -1
  19. package/src/index.ts +2 -1
  20. package/src/schemaTypeResolver.ts +2 -1
  21. package/src/utils/avdlToAVSC.spec.ts +28 -5
  22. package/src/utils/avdlToAVSC.ts +3 -3
  23. package/tmp/Array.avsc +22 -0
  24. package/tmp/Array1.avsc +28 -0
  25. package/tmp/Array2.avsc +17 -0
  26. package/tmp/Array3.avsc +17 -0
  27. package/tmp/ArrayUnion.avsc +23 -0
  28. package/tmp/Bam.avsc +9 -0
  29. package/tmp/Bar.avsc +46 -0
  30. package/tmp/Baz.avsc +39 -0
  31. package/tmp/Complex.avsc +81 -0
  32. package/tmp/Enum.avsc +13 -0
  33. package/tmp/EnumUnion.avsc +14 -0
  34. package/tmp/Foos.avsc +6 -0
  35. package/tmp/ImportMultipleNamespaces.avsc +35 -0
  36. package/tmp/KeyValue.avsc +12 -0
  37. package/tmp/Metadata.avsc +27 -0
  38. package/tmp/Multiple.avsc +26 -0
  39. package/tmp/MultipleNamespaces.avsc +44 -0
  40. package/tmp/MultipleUnion.avsc +28 -0
  41. package/tmp/Simple.avsc +9 -0
  42. package/tmp/Two.avsc +19 -0
  43. package/tmp/Union.avsc +20 -0
  44. package/dockest.ts +0 -30
package/CHANGELOG.md CHANGED
@@ -9,7 +9,48 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
9
9
 
10
10
  ### Added
11
11
 
12
- -field for error validation , and the issue of failing
12
+ - Support [schema references](https://docs.confluent.io/platform/current/schema-registry/serdes-develop/index.html#schema-references) for Avro, Protocol Buffer, and JSON schema [#197](https://github.com/kafkajs/confluent-schema-registry/pull/197)
13
+
14
+ ### Fixed
15
+
16
+ - Fix Apicurio compatibility with register function [#201](https://github.com/kafkajs/confluent-schema-registry/pull/201)
17
+
18
+ ## [3.2.1] - 2022-01-28
19
+
20
+ ### Fixed
21
+
22
+ - Don't swallow error message from client-side errors from registry requests [#176](https://github.com/kafkajs/confluent-schema-registry/pull/176)
23
+
24
+ ## [3.2.0] - 2021-11-22
25
+
26
+ ### Added
27
+
28
+ - Add reader schema option when decoding Avro messages [#166](https://github.com/kafkajs/confluent-schema-registry/pull/166)
29
+
30
+ ## [3.1.1] - 2021-11-03
31
+
32
+ ### Fixed
33
+
34
+ - Support backwards incompatible changes in Ajv 8 when passing in Ajv instance in JSON Schema options [#163](https://github.com/kafkajs/confluent-schema-registry/pull/163)
35
+
36
+ ## [3.1.0] - 2021-11-03
37
+
38
+ ### Added
39
+
40
+ - Allow passing in Ajv instance in JSON Schema options [#133](https://github.com/kafkajs/confluent-schema-registry/pull/133)
41
+
42
+ ### Fixed
43
+
44
+ - Fix backwards compatibility with older Schema Registry versions [#158](https://github.com/kafkajs/confluent-schema-registry/pull/158)
45
+
46
+ ### Fixed
47
+
48
+ - Fix gateway config for when setting HTTP agent [#127](https://github.com/kafkajs/confluent-schema-registry/pull/127)
49
+
50
+ ## [3.0.1] - 2021-06-11
51
+ ### Fixed
52
+
53
+ - Fix gateway config for when setting HTTP agent [#127](https://github.com/kafkajs/confluent-schema-registry/pull/127)
13
54
 
14
55
  ## [3.0.0] - 2021-05-20
15
56
 
package/bin/avdlToAVSC.sh CHANGED
@@ -6,4 +6,4 @@ if [ -z "${avdl_path}" ]; then
6
6
  exit;
7
7
  fi
8
8
 
9
- docker run --rm -v ${PWD}:/share coderfi/avro-tools:1.7.7 idl2schemata ${avdl_path} tmp && cat tmp/${avsc_name}.avsc
9
+ docker run --rm -v "$(pwd)":/avro kpnnl/avro-tools:1.12.0 idl2schemata ${avdl_path} tmp && cat tmp/${avsc_name}.avsc
@@ -42,7 +42,7 @@ class JsonSchema {
42
42
  if (opts === null || opts === void 0 ? void 0 : opts.errorHook) {
43
43
  for (const err of this.validate.errors) {
44
44
  const path = this.isOldAjvValidationError(err) ? err.dataPath : err.instancePath;
45
- opts === null || opts === void 0 ? void 0 : opts.errorHook([path], err.data, err.schema);
45
+ opts.errorHook([err], err.data, err.schema);
46
46
  }
47
47
  }
48
48
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"JsonSchema.js","sourceRoot":"","sources":["../src/JsonSchema.ts"],"names":[],"mappings":";;;;;AACA,8CAAqB;AACrB,qCAAiE;AAoBjE,MAAqB,UAAU;IAG7B,YAAY,MAA2B,EAAE,IAAkB;QACzD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAClD,CAAC;IAEO,aAAa,CAAC,MAA2B,EAAE,IAAkB;;QACnE,MAAM,GAAG,SAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,WAAW,mCAAI,IAAI,aAAG,CAAC,IAAI,CAAC,CAAA;QAC9C,MAAM,iBAAiB,GAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,iBAAiB,CAAA;QACjD,IAAI,iBAAiB,EAAE;YACrB,iBAAiB,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;gBACpC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;gBAC5C,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;YACxC,CAAC,CAAC,CAAA;SACH;QACD,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;QACvD,OAAO,QAAQ,CAAA;IACjB,CAAC;IAEO,eAAe,CAAC,OAAY;QAClC,MAAM,KAAK,GAAe,EAAE,CAAA;QAC5B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;YACnE,MAAM,IAAI,+CAAsC,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAA;SAC3E;IACH,CAAC;IAEM,QAAQ,CAAC,OAAe;QAC7B,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;QAC7B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;IAC7C,CAAC;IAEM,UAAU,CAAC,MAAc;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC7C,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;QAC7B,OAAO,OAAO,CAAA;IAChB,CAAC;IACM,OAAO,CAAC,OAAe,EAAE,IAA2E;QACzG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;YAC3B,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE;gBAC9D,KAAK,MAAM,GAAG,IAAK,IAAI,CAAC,QAAQ,CAAC,MAAc,EAAE;oBAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC;oBACjF,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,SAAS,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE;iBAC/C;aACF;YACD,OAAO,KAAK,CAAC;SACd;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,uBAAuB,CAAC,KAAyB;QACvD,OAAQ,KAA+B,CAAC,QAAQ,IAAI,IAAI,CAAA;IAC1D,CAAC;CACF;AArDD,6BAqDC"}
1
+ {"version":3,"file":"JsonSchema.js","sourceRoot":"","sources":["../src/JsonSchema.ts"],"names":[],"mappings":";;;;;AACA,8CAAqB;AACrB,qCAAiE;AAoBjE,MAAqB,UAAU;IAG7B,YAAY,MAA2B,EAAE,IAAkB;QACzD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAClD,CAAC;IAEO,aAAa,CAAC,MAA2B,EAAE,IAAkB;;QACnE,MAAM,GAAG,SAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,WAAW,mCAAI,IAAI,aAAG,CAAC,IAAI,CAAC,CAAA;QAC9C,MAAM,iBAAiB,GAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,iBAAiB,CAAA;QACjD,IAAI,iBAAiB,EAAE;YACrB,iBAAiB,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;gBACpC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;gBAC5C,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;YACxC,CAAC,CAAC,CAAA;SACH;QACD,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;QACvD,OAAO,QAAQ,CAAA;IACjB,CAAC;IAEO,eAAe,CAAC,OAAY;QAClC,MAAM,KAAK,GAAe,EAAE,CAAA;QAC5B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;YACnE,MAAM,IAAI,+CAAsC,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAA;SAC3E;IACH,CAAC;IAEM,QAAQ,CAAC,OAAe;QAC7B,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;QAC7B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;IAC7C,CAAC;IAEM,UAAU,CAAC,MAAc;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC7C,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;QAC7B,OAAO,OAAO,CAAA;IAChB,CAAC;IACM,OAAO,CAAC,OAAe,EAAE,IAA2E;QACzG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;YAC3B,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE;gBAC9D,KAAK,MAAM,GAAG,IAAK,IAAI,CAAC,QAAQ,CAAC,MAAc,EAAE;oBAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC;oBACjF,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;iBAC7C;aACF;YACD,OAAO,KAAK,CAAC;SACd;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,uBAAuB,CAAC,KAAyB;QACvD,OAAQ,KAA+B,CAAC,QAAQ,IAAI,IAAI,CAAA;IAC1D,CAAC;CACF;AArDD,6BAqDC"}
@@ -1,11 +1,7 @@
1
1
  {
2
2
  "errorPayload": {
3
- "trap": "unhandledRejection",
4
- "reason": {
5
- "payload": {},
6
- "name": "DockestError"
7
- },
8
- "promise": {}
3
+ "trap": "SIGINT",
4
+ "signal": "SIGINT"
9
5
  },
10
- "timestamp": "2024-09-03T14:42:35.241Z"
6
+ "timestamp": "2024-12-18T22:39:59.117Z"
11
7
  }
package/jest.setup.ts CHANGED
@@ -1,8 +1,12 @@
1
1
  import { MAGIC_BYTE } from './src/wireEncoder'
2
2
  import decode from './src/wireDecoder'
3
+ import { MatcherFunction } from 'expect'
3
4
 
4
- const toMatchConfluentEncodedPayload = context => (received, { payload: expectedPayload }) => {
5
- const { printExpected, printReceived, printWithType } = context.utils
5
+ const toMatchConfluentEncodedPayload: MatcherFunction<[{ payload: Buffer }]> = function(
6
+ received,
7
+ { payload: expectedPayload },
8
+ ) {
9
+ const { printExpected, printReceived, printWithType } = this.utils
6
10
 
7
11
  if (!Buffer.isBuffer(expectedPayload)) {
8
12
  const error = [
@@ -13,16 +17,17 @@ const toMatchConfluentEncodedPayload = context => (received, { payload: expected
13
17
  throw new Error(error)
14
18
  }
15
19
 
16
- const { magicByte, payload } = decode(received)
20
+ const { magicByte, payload } = decode(received as Buffer)
17
21
  const expectedMessage = decode(expectedPayload)
18
22
 
19
23
  if (!Buffer.isBuffer(received)) {
20
24
  return {
21
25
  pass: false,
22
- message: () => [
23
- 'Received value must be a Buffer',
24
- printWithType('Received', received, printReceived),
25
- ],
26
+ message: () =>
27
+ [
28
+ 'Received value must be a Buffer',
29
+ printWithType('Received', received, printReceived),
30
+ ].join('\n'),
26
31
  }
27
32
  }
28
33
 
@@ -40,7 +45,7 @@ const toMatchConfluentEncodedPayload = context => (received, { payload: expected
40
45
  }
41
46
 
42
47
  return {
43
- pass: context.equals(payload, expectedMessage.payload),
48
+ pass: this.equals(payload, expectedMessage.payload),
44
49
  message: () =>
45
50
  [
46
51
  'expected payload',
@@ -52,9 +57,14 @@ const toMatchConfluentEncodedPayload = context => (received, { payload: expected
52
57
  }
53
58
 
54
59
  expect.extend({
55
- toMatchConfluentEncodedPayload(...args) {
56
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
57
- // @ts-ignore
58
- return toMatchConfluentEncodedPayload(this)(...args)
59
- },
60
+ toMatchConfluentEncodedPayload,
60
61
  })
62
+
63
+ declare global {
64
+ // eslint-disable-next-line @typescript-eslint/no-namespace
65
+ namespace jest {
66
+ interface Matchers<R, T = {}> {
67
+ toMatchConfluentEncodedPayload(args: { registryId: number; payload: Buffer }): R
68
+ }
69
+ }
70
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "confluent-schema-registry",
3
- "version": "3.3.5",
3
+ "version": "3.6.2",
4
4
  "main": "dist/index.js",
5
5
  "description": "ConfluentSchemaRegistry is a library that makes it easier to interact with the Confluent schema registry, it provides convenient methods to encode, decode and register new schemas using the Apache Avro serialization format.",
6
6
  "keywords": [
@@ -17,40 +17,34 @@
17
17
  "build:watch": "rm -rf ./dist && tsc --watch",
18
18
  "test:unit:watch": "yarn test:unit --watch",
19
19
  "test:unit": "jest",
20
- "test": "ts-node ./dockest.ts",
21
- "test:debug": "ts-node ./dockest.ts debug",
20
+ "test": "docker compose up -d --wait schemaRegistry && jest",
22
21
  "lint": "eslint './src/**/*.ts'",
22
+ "check:types": "tsc --noEmit",
23
23
  "format": "yarn lint --fix"
24
24
  },
25
25
  "dependencies": {
26
26
  "ajv": "^7.1.0",
27
27
  "avsc": ">= 5.4.13 < 6",
28
- "mappersmith": ">= 2.30.1 < 3",
29
- "protobufjs": "^6.11.4"
28
+ "mappersmith": ">= 2.44.0 < 3",
29
+ "protobufjs": ">= 6.11.4 < 8"
30
30
  },
31
31
  "devDependencies": {
32
- "@types/execa": "^2.0.0",
33
- "@types/fs-extra": "^8.0.0",
34
- "@types/jest": "^25.2.1",
35
- "@types/node": "^12.7.3",
32
+ "@types/jest": "^29.5.14",
33
+ "@types/node": "^18.19.70",
36
34
  "@types/prettier": "^1.18.2",
37
- "@types/uuid": "^3.4.5",
38
35
  "@typescript-eslint/eslint-plugin": "^2.1.0",
39
36
  "@typescript-eslint/parser": "^2.1.0",
40
37
  "@typescript-eslint/typescript-estree": "^2.1.0",
41
38
  "ajv8": "npm:ajv@^8.6.3",
42
- "dockest": "^2.1.0",
43
39
  "eslint": "^6.3.0",
44
40
  "eslint-config-prettier": "^6.1.0",
45
41
  "eslint-plugin-no-only-tests": "^2.3.1",
46
42
  "eslint-plugin-prettier": "^3.1.0",
47
- "execa": "^2.0.4",
48
- "fs-extra": "^8.1.0",
49
- "jest": "^25.2.7",
43
+ "jest": "^29.7.0",
50
44
  "prettier": "^1.18.2",
51
- "ts-jest": "^24.0.2",
45
+ "ts-jest": "^29.2.5",
52
46
  "ts-node": "^8.3.0",
53
- "typescript": "^3.6.2",
54
- "uuid": "^3.3.3"
47
+ "typescript": "^5.7.3",
48
+ "uuid": "^11.0.5"
55
49
  }
56
50
  }
package/src/@types.ts CHANGED
@@ -23,10 +23,12 @@ export type AvroOptions = Partial<ForSchemaOptions> & {
23
23
  }
24
24
 
25
25
  export type JsonOptions = ConstructorParameters<typeof Ajv>[0] & {
26
- ajvInstance?: {
27
- addSchema: Ajv['addSchema']
28
- compile: (schema: any) => ValidateFunction
29
- }
26
+ ajvInstance?:
27
+ | {
28
+ addSchema: Ajv['addSchema']
29
+ compile: (schema: any) => ValidateFunction
30
+ }
31
+ | Ajv
30
32
  referencedSchemas?: JsonConfluentSchema[]
31
33
  }
32
34
  export type ProtoOptions = { messageName?: string; referencedSchemas?: ProtoConfluentSchema[] }
@@ -94,12 +96,3 @@ export interface SchemaResponse {
94
96
  }
95
97
 
96
98
  export type ConfluentSchema = AvroConfluentSchema | ProtoConfluentSchema | JsonConfluentSchema
97
-
98
- declare global {
99
- // eslint-disable-next-line @typescript-eslint/no-namespace
100
- namespace jest {
101
- interface Matchers<R, T = {}> {
102
- toMatchConfluentEncodedPayload(args: { registryId: number; payload: Buffer }): R
103
- }
104
- }
105
- }
package/src/AvroHelper.ts CHANGED
@@ -12,7 +12,7 @@ import { ConfluentSchemaRegistryArgumentError } from './errors'
12
12
  import avro, { ForSchemaOptions, Schema, Type } from 'avsc'
13
13
  import { SchemaResponse, SchemaType } from './@types'
14
14
 
15
- type TypeHook = (schema: Schema, opts: ForSchemaOptions) => Type
15
+ type TypeHook = (schema: Schema, opts: ForSchemaOptions) => Type | undefined
16
16
  export default class AvroHelper implements SchemaHelper {
17
17
  private getRawAvroSchema(schema: ConfluentSchema): RawAvroSchema {
18
18
  return (typeof schema.schema === 'string'
@@ -57,8 +57,7 @@ export default class AvroHelper implements SchemaHelper {
57
57
 
58
58
  public getSubject(
59
59
  schema: AvroConfluentSchema,
60
- // @ts-ignore
61
- avroSchema: AvroSchema,
60
+ _avroSchema: AvroSchema,
62
61
  separator: string,
63
62
  ): ConfluentSubject {
64
63
  const rawSchema: RawAvroSchema = this.getRawAvroSchema(schema)
package/src/JsonSchema.ts CHANGED
@@ -5,6 +5,7 @@ import { ConfluentSchemaRegistryValidationError } from './errors'
5
5
  interface BaseAjvValidationError {
6
6
  data?: unknown
7
7
  schema?: unknown
8
+ message?: string
8
9
  }
9
10
  interface OldAjvValidationError extends BaseAjvValidationError {
10
11
  dataPath: string
@@ -41,8 +42,15 @@ export default class JsonSchema implements Schema {
41
42
  }
42
43
 
43
44
  private validatePayload(payload: any) {
44
- const paths: string[][] = []
45
- if (!this.isValid(payload, { errorHook: path => paths.push(path) })) {
45
+ const paths: any[] = []
46
+
47
+ if (
48
+ !this.isValid(payload, {
49
+ errorHook: (path, message) => {
50
+ paths.push({ path, message })
51
+ },
52
+ })
53
+ ) {
46
54
  throw new ConfluentSchemaRegistryValidationError('invalid payload', paths)
47
55
  }
48
56
  }
@@ -57,19 +65,23 @@ export default class JsonSchema implements Schema {
57
65
  this.validatePayload(payload)
58
66
  return payload
59
67
  }
60
- public isValid(payload: object, opts?: { errorHook: (path: Array<string>, value: any, type?: any) => void }): boolean {
61
- if (!this.validate(payload)) {
62
- if (opts === null || opts === void 0 ? void 0 : opts.errorHook) {
63
- for (const err of (this.validate.errors as any)) {
64
- const path = this.isOldAjvValidationError(err) ? err.dataPath : err.instancePath;
65
- opts?.errorHook([path], err.data, err.schema);
66
- }
67
- }
68
- return false;
69
- }
70
- return true;
71
- }
72
-
68
+
69
+ public isValid(
70
+ payload: object,
71
+ opts?: { errorHook: (path: Array<string>, value: any, type?: any) => void },
72
+ ): boolean {
73
+ if (!this.validate(payload)) {
74
+ if (opts?.errorHook) {
75
+ for (const err of this.validate.errors as AjvValidationError[]) {
76
+ const path = this.isOldAjvValidationError(err) ? err.dataPath : err.instancePath
77
+ opts.errorHook([path], err.message ?? err.data, err.schema)
78
+ }
79
+ return false
80
+ }
81
+ }
82
+ return true
83
+ }
84
+
73
85
  private isOldAjvValidationError(error: AjvValidationError): error is OldAjvValidationError {
74
86
  return (error as OldAjvValidationError).dataPath != null
75
87
  }
@@ -1,6 +1,8 @@
1
1
  import SchemaRegistry, { RegisteredSchema } from './SchemaRegistry'
2
2
  import API from './api'
3
3
  import { JsonConfluentSchema, SchemaType } from './@types'
4
+ import Ajv from 'ajv'
5
+ import { ConfluentSchemaRegistryValidationError } from './errors'
4
6
 
5
7
  const REGISTRY_HOST = 'http://localhost:8982'
6
8
  const schemaRegistryAPIClientArgs = { host: REGISTRY_HOST }
@@ -100,8 +102,13 @@ describe('SchemaRegistry', () => {
100
102
  let api
101
103
 
102
104
  beforeEach(async () => {
105
+ const options = {
106
+ [SchemaType.JSON]: {
107
+ allErrors: true,
108
+ },
109
+ }
103
110
  api = API(schemaRegistryAPIClientArgs)
104
- schemaRegistry = new SchemaRegistry(schemaRegistryArgs)
111
+ schemaRegistry = new SchemaRegistry(schemaRegistryArgs, options)
105
112
  })
106
113
 
107
114
  describe('when register', () => {
@@ -161,6 +168,16 @@ describe('SchemaRegistry', () => {
161
168
 
162
169
  expect(resultObj).toEqual(obj)
163
170
  })
171
+
172
+ it('should return error message', async () => {
173
+ const obj = { id2a: 'sdfsdfsdf', level2a: 1 }
174
+ try {
175
+ await schemaRegistry.encode(registeredSchema.id, obj)
176
+ } catch (ex) {
177
+ expect(ex.paths[0].message).toBeDefined()
178
+ expect(ex.paths[0].message).toEqual('should be number')
179
+ }
180
+ })
164
181
  })
165
182
 
166
183
  describe('with multiple reference', () => {
@@ -587,7 +587,7 @@ describe('SchemaRegistry - new Api', () => {
587
587
  } catch (error) {
588
588
  expect(error).toBeInstanceOf(ConfluentSchemaRegistryValidationError)
589
589
  expect(error.message).toEqual('invalid payload')
590
- expect(error.paths).toEqual([['/fullName']])
590
+ expect(error.paths[0].path).toEqual(['/fullName'])
591
591
  }
592
592
  },
593
593
  )
@@ -250,3 +250,18 @@ describe('SchemaRegistry - old AVRO api', () => {
250
250
  })
251
251
  })
252
252
  })
253
+
254
+ describe('SchemaRegistry - Custom Middleware', () => {
255
+ const customMiddleware = jest.fn()
256
+
257
+ const schemaRegistry = new SchemaRegistry({
258
+ ...schemaRegistryArgs,
259
+ middlewares: [customMiddleware],
260
+ })
261
+
262
+ it('should have called the custom middleware', async () => {
263
+ await schemaRegistry.register(personSchema)
264
+
265
+ expect(customMiddleware).toHaveBeenCalled()
266
+ })
267
+ })
@@ -3,7 +3,7 @@ import { Response } from 'mappersmith'
3
3
 
4
4
  import { encode, MAGIC_BYTE } from './wireEncoder'
5
5
  import decode from './wireDecoder'
6
- import { COMPATIBILITY, DEFAULT_SEPERATOR } from './constants'
6
+ import { COMPATIBILITY, DEFAULT_SEPARATOR } from './constants'
7
7
  import API, { SchemaRegistryAPIClientArgs, SchemaRegistryAPIClient } from './api'
8
8
  import Cache from './cache'
9
9
  import {
@@ -46,13 +46,13 @@ interface Opts {
46
46
  interface AvroDecodeOptions {
47
47
  readerSchema?: RawAvroSchema | AvroSchema | Schema
48
48
  }
49
- interface DecodeOptions {
49
+ export interface DecodeOptions {
50
50
  [SchemaType.AVRO]?: AvroDecodeOptions
51
51
  }
52
52
 
53
53
  const DEFAULT_OPTS = {
54
54
  compatibility: COMPATIBILITY.BACKWARD,
55
- separator: DEFAULT_SEPERATOR,
55
+ separator: DEFAULT_SEPARATOR,
56
56
  }
57
57
  export default class SchemaRegistry {
58
58
  private api: SchemaRegistryAPIClient
@@ -62,10 +62,10 @@ export default class SchemaRegistry {
62
62
  public cache: Cache
63
63
 
64
64
  constructor(
65
- { auth, clientId, host, retry, agent }: SchemaRegistryAPIClientArgs,
65
+ { auth, clientId, host, retry, agent, middlewares }: SchemaRegistryAPIClientArgs,
66
66
  options?: SchemaRegistryAPIClientOptions,
67
67
  ) {
68
- this.api = API({ auth, clientId, host, retry, agent })
68
+ this.api = API({ auth, clientId, host, retry, agent, middlewares })
69
69
  this.cache = new Cache()
70
70
  this.options = options
71
71
  }
@@ -138,7 +138,7 @@ export default class SchemaRegistry {
138
138
  )
139
139
  }
140
140
  } catch (error) {
141
- if (error.status !== 404) {
141
+ if (!error || typeof error !== 'object' || !('status' in error) || error.status !== 404) {
142
142
  throw error
143
143
  } else {
144
144
  isFirstTimeRegistration = true
@@ -355,7 +355,7 @@ export default class SchemaRegistry {
355
355
 
356
356
  return id
357
357
  } catch (error) {
358
- if (error.status && error.status === 404) {
358
+ if (error && typeof error === 'object' && 'status' in error && error.status === 404) {
359
359
  throw new ConfluentSchemaRegistryError(error)
360
360
  }
361
361
 
@@ -370,18 +370,17 @@ export default class SchemaRegistry {
370
370
  return id
371
371
  }
372
372
 
373
- private getSchemaOriginRequest(registryId: number) {
373
+ private async getSchemaOriginRequest(registryId: number): Promise<Response> {
374
374
  // ensure that cache-misses result in a single origin request
375
- if (this.cacheMissRequests[registryId]) {
376
- return this.cacheMissRequests[registryId]
377
- } else {
378
- const request = this.api.Schema.find({ id: registryId }).finally(() => {
379
- delete this.cacheMissRequests[registryId]
380
- })
375
+ const req = this.cacheMissRequests[registryId]
376
+ if (req) return req
381
377
 
382
- this.cacheMissRequests[registryId] = request
378
+ const request = this.api.Schema.find({ id: registryId }).finally(() => {
379
+ delete this.cacheMissRequests[registryId]
380
+ })
383
381
 
384
- return request
385
- }
382
+ this.cacheMissRequests[registryId] = request
383
+
384
+ return request
386
385
  }
387
386
  }
@@ -1,7 +1,27 @@
1
+ import { Middleware } from 'mappersmith'
1
2
  import API from '.'
2
3
  import { mockClient, install, uninstall } from 'mappersmith/test'
3
4
 
4
- const client = API({ clientId: 'test-client', host: 'http://example.com' })
5
+ const customMiddleware: Middleware = jest.fn(() => {
6
+ return {
7
+ async request(request) {
8
+ return request.enhance({
9
+ headers: {
10
+ Authorization: 'Bearer Random',
11
+ },
12
+ })
13
+ },
14
+ async response(next) {
15
+ return next()
16
+ },
17
+ }
18
+ })
19
+
20
+ const client = API({
21
+ clientId: 'test-client',
22
+ host: 'http://example.com',
23
+ middlewares: [customMiddleware],
24
+ })
5
25
  const mock = mockClient<typeof client>(client)
6
26
  .resource('Schema')
7
27
  .method('find')
@@ -14,10 +34,12 @@ describe('API Client', () => {
14
34
 
15
35
  afterEach(() => uninstall())
16
36
 
17
- it('should include a user agent header', async () => {
37
+ it('should include a user agent header and call custom middleware', async () => {
18
38
  const response = await client.Schema.find({ id: 'abc' })
19
39
 
20
40
  expect(mock.callsCount()).toBe(1)
21
41
  expect(response.request().header('User-Agent')).not.toBeUndefined()
42
+ expect(response.request().header('Authorization')).toBe('Bearer Random')
43
+ expect(customMiddleware).toHaveBeenCalled()
22
44
  })
23
45
  })
package/src/api/index.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  import { Agent } from 'http'
2
- import forge, { Authorization, Client, Options, GatewayConfiguration } from 'mappersmith'
2
+ import forge, {
3
+ Authorization,
4
+ Client,
5
+ GatewayConfiguration,
6
+ Middleware,
7
+ ManifestOptions,
8
+ } from 'mappersmith'
3
9
  import RetryMiddleware, { RetryMiddlewareOptions } from 'mappersmith/middleware/retry/v2'
4
10
  import BasicAuthMiddleware from 'mappersmith/middleware/basic-auth'
5
11
 
@@ -23,6 +29,7 @@ export interface SchemaRegistryAPIClientArgs {
23
29
  retry?: Partial<RetryMiddlewareOptions>
24
30
  /** HTTP Agent that will be passed to underlying API calls */
25
31
  agent?: Agent
32
+ middlewares?: Middleware[]
26
33
  }
27
34
 
28
35
  // TODO: Improve typings
@@ -48,10 +55,11 @@ export default ({
48
55
  host,
49
56
  retry = {},
50
57
  agent,
58
+ middlewares = [],
51
59
  }: SchemaRegistryAPIClientArgs): SchemaRegistryAPIClient => {
52
60
  const clientId = userClientId || DEFAULT_API_CLIENT_ID
53
61
  // FIXME: ResourcesType typings is not exposed by mappersmith
54
- const manifest: Options<any> = {
62
+ const manifest: ManifestOptions<any> = {
55
63
  clientId,
56
64
  ignoreGlobalMiddleware: true,
57
65
  host,
@@ -61,6 +69,7 @@ export default ({
61
69
  RetryMiddleware(Object.assign(DEFAULT_RETRY, retry)),
62
70
  errorMiddleware,
63
71
  ...(auth ? [BasicAuthMiddleware(auth)] : []),
72
+ ...middlewares,
64
73
  ],
65
74
  resources: {
66
75
  Schema: {
@@ -30,7 +30,7 @@ const errorMiddleware: Middleware = ({ clientId }) => ({
30
30
  new Promise((resolve, reject) =>
31
31
  next()
32
32
  .then(resolve)
33
- .catch((response: Response) => reject(new ResponseError(clientId, response))),
33
+ .catch((response: Response) => reject(new ResponseError(clientId ?? '', response))),
34
34
  ),
35
35
  })
36
36
 
package/src/constants.ts CHANGED
@@ -8,6 +8,6 @@ export enum COMPATIBILITY {
8
8
  FULL_TRANSITIVE = 'FULL_TRANSITIVE',
9
9
  }
10
10
 
11
- export const DEFAULT_SEPERATOR = '.'
11
+ export const DEFAULT_SEPARATOR = '.'
12
12
 
13
13
  export const DEFAULT_API_CLIENT_ID = 'Confluent_Schema_Registry'
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
- export { default as SchemaRegistry } from './SchemaRegistry'
1
+ import { default as SchemaRegistry, DecodeOptions } from './SchemaRegistry'
2
+ export { SchemaRegistry, DecodeOptions }
2
3
  export * from './utils'
3
4
  export { SchemaType } from './@types'
4
5
  export { COMPATIBILITY } from './constants'
@@ -96,6 +96,7 @@ export const schemaFromConfluentSchema = (
96
96
 
97
97
  return schema
98
98
  } catch (err) {
99
- throw new ConfluentSchemaRegistryArgumentError(err.message)
99
+ if (err instanceof Error) throw new ConfluentSchemaRegistryArgumentError(err.message)
100
+ throw err
100
101
  }
101
102
  }