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.
- package/CHANGELOG.md +42 -1
- package/bin/avdlToAVSC.sh +1 -1
- package/dist/JsonSchema.js +1 -1
- package/dist/JsonSchema.js.map +1 -1
- package/dockest-error.json +3 -7
- package/jest.setup.ts +23 -13
- package/package.json +11 -17
- package/src/@types.ts +6 -13
- package/src/AvroHelper.ts +2 -3
- package/src/JsonSchema.ts +27 -15
- package/src/SchemaRegistry.json.spec.ts +18 -1
- package/src/SchemaRegistry.newApi.spec.ts +1 -1
- package/src/SchemaRegistry.spec.ts +15 -0
- package/src/SchemaRegistry.ts +16 -17
- package/src/api/index.spec.ts +24 -2
- package/src/api/index.ts +11 -2
- package/src/api/middleware/errorMiddleware.ts +1 -1
- package/src/constants.ts +1 -1
- package/src/index.ts +2 -1
- package/src/schemaTypeResolver.ts +2 -1
- package/src/utils/avdlToAVSC.spec.ts +28 -5
- package/src/utils/avdlToAVSC.ts +3 -3
- package/tmp/Array.avsc +22 -0
- package/tmp/Array1.avsc +28 -0
- package/tmp/Array2.avsc +17 -0
- package/tmp/Array3.avsc +17 -0
- package/tmp/ArrayUnion.avsc +23 -0
- package/tmp/Bam.avsc +9 -0
- package/tmp/Bar.avsc +46 -0
- package/tmp/Baz.avsc +39 -0
- package/tmp/Complex.avsc +81 -0
- package/tmp/Enum.avsc +13 -0
- package/tmp/EnumUnion.avsc +14 -0
- package/tmp/Foos.avsc +6 -0
- package/tmp/ImportMultipleNamespaces.avsc +35 -0
- package/tmp/KeyValue.avsc +12 -0
- package/tmp/Metadata.avsc +27 -0
- package/tmp/Multiple.avsc +26 -0
- package/tmp/MultipleNamespaces.avsc +44 -0
- package/tmp/MultipleUnion.avsc +28 -0
- package/tmp/Simple.avsc +9 -0
- package/tmp/Two.avsc +19 -0
- package/tmp/Union.avsc +20 -0
- 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
|
-
-
|
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 $
|
9
|
+
docker run --rm -v "$(pwd)":/avro kpnnl/avro-tools:1.12.0 idl2schemata ${avdl_path} tmp && cat tmp/${avsc_name}.avsc
|
package/dist/JsonSchema.js
CHANGED
@@ -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
|
45
|
+
opts.errorHook([err], err.data, err.schema);
|
46
46
|
}
|
47
47
|
}
|
48
48
|
return false;
|
package/dist/JsonSchema.js.map
CHANGED
@@ -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,
|
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"}
|
package/dockest-error.json
CHANGED
@@ -1,11 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"errorPayload": {
|
3
|
-
"trap": "
|
4
|
-
"
|
5
|
-
"payload": {},
|
6
|
-
"name": "DockestError"
|
7
|
-
},
|
8
|
-
"promise": {}
|
3
|
+
"trap": "SIGINT",
|
4
|
+
"signal": "SIGINT"
|
9
5
|
},
|
10
|
-
"timestamp": "2024-
|
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
|
5
|
-
|
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
|
-
|
24
|
-
|
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:
|
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
|
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
|
+
"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": "
|
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.
|
29
|
-
"protobufjs": "
|
28
|
+
"mappersmith": ">= 2.44.0 < 3",
|
29
|
+
"protobufjs": ">= 6.11.4 < 8"
|
30
30
|
},
|
31
31
|
"devDependencies": {
|
32
|
-
"@types/
|
33
|
-
"@types/
|
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
|
-
"
|
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": "^
|
45
|
+
"ts-jest": "^29.2.5",
|
52
46
|
"ts-node": "^8.3.0",
|
53
|
-
"typescript": "^
|
54
|
-
"uuid": "^
|
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
|
-
|
28
|
-
|
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
|
-
|
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:
|
45
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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([
|
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
|
+
})
|
package/src/SchemaRegistry.ts
CHANGED
@@ -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,
|
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:
|
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
|
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
|
-
|
376
|
-
|
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
|
-
|
378
|
+
const request = this.api.Schema.find({ id: registryId }).finally(() => {
|
379
|
+
delete this.cacheMissRequests[registryId]
|
380
|
+
})
|
383
381
|
|
384
|
-
|
385
|
-
|
382
|
+
this.cacheMissRequests[registryId] = request
|
383
|
+
|
384
|
+
return request
|
386
385
|
}
|
387
386
|
}
|
package/src/api/index.spec.ts
CHANGED
@@ -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
|
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, {
|
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:
|
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
package/src/index.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
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
|
}
|