parent
268c871312
commit
ad8598a1d2
19 changed files with 1342 additions and 39 deletions
370
kafka-ui-react-app/package-lock.json
generated
370
kafka-ui-react-app/package-lock.json
generated
|
@ -1450,6 +1450,18 @@
|
|||
"strip-json-comments": "^3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
|
||||
|
@ -1465,6 +1477,12 @@
|
|||
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
|
||||
"dev": true
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
|
@ -3092,6 +3110,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz",
|
||||
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
|
||||
},
|
||||
"@types/yup": {
|
||||
"version": "0.29.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/yup/-/yup-0.29.13.tgz",
|
||||
"integrity": "sha512-qRyuv+P/1t1JK1rA+elmK1MmCL1BapEzKKfbEhDBV/LMMse4lmhZ/XbgETI39JveDJRpLjmToOI6uFtMW/WR2g=="
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.1.tgz",
|
||||
|
@ -3689,18 +3712,6 @@
|
|||
"indent-string": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"ajv-errors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
|
||||
|
@ -7527,6 +7538,18 @@
|
|||
"@babel/highlight": "^7.10.4"
|
||||
}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
|
||||
|
@ -7565,6 +7588,12 @@
|
|||
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
|
||||
"dev": true
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
|
@ -7962,6 +7991,12 @@
|
|||
"schema-utils": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
|
@ -7977,6 +8012,20 @@
|
|||
"@types/json-schema": "^7.0.6",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8478,6 +8527,11 @@
|
|||
"integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=",
|
||||
"dev": true
|
||||
},
|
||||
"faker": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz",
|
||||
"integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g=="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
|
@ -8633,6 +8687,12 @@
|
|||
"schema-utils": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
|
||||
|
@ -8642,6 +8702,20 @@
|
|||
"@types/json-schema": "^7.0.6",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8827,6 +8901,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"fn-name": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fn-name/-/fn-name-3.0.0.tgz",
|
||||
"integrity": "sha512-eNMNr5exLoavuAMhIUVsOKF79SWd/zG104ef6sxBTSw+cZc6BXdQXDvYcGvp0VbxVVSp1XDUNoz7mg1xMtSznA=="
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
|
||||
|
@ -9279,8 +9358,7 @@
|
|||
"get-own-enumerable-property-symbols": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
|
||||
"integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g=="
|
||||
},
|
||||
"get-package-type": {
|
||||
"version": "0.1.0",
|
||||
|
@ -9470,6 +9548,26 @@
|
|||
"requires": {
|
||||
"ajv": "^6.12.3",
|
||||
"har-schema": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"harmony-reflect": {
|
||||
|
@ -10576,8 +10674,7 @@
|
|||
"is-obj": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
|
||||
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
|
||||
},
|
||||
"is-path-cwd": {
|
||||
"version": "2.2.0",
|
||||
|
@ -10637,8 +10734,22 @@
|
|||
"is-regexp": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
|
||||
"integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=",
|
||||
"dev": true
|
||||
"integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk="
|
||||
},
|
||||
"is-relative-url": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-relative-url/-/is-relative-url-3.0.0.tgz",
|
||||
"integrity": "sha512-U1iSYRlY2GIMGuZx7gezlB5dp1Kheaym7zKzO1PV06mOihiWTXejLwm4poEJysPyXF+HtK/BEd0DVlcCh30pEA==",
|
||||
"requires": {
|
||||
"is-absolute-url": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-absolute-url": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
|
||||
"integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"is-resolvable": {
|
||||
"version": "1.1.0",
|
||||
|
@ -12437,11 +12548,32 @@
|
|||
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
|
||||
"dev": true
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
"json-schema-yup-transformer": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-yup-transformer/-/json-schema-yup-transformer-1.6.0.tgz",
|
||||
"integrity": "sha512-fxS3g2t+taJ8ZBlbmA8rK3YO9SyjeQxvCIIJxC8iDsaqnGn0SVI3DnFePdmtHmYmyor9mlKOKvk6Sc0YT2ZP7Q==",
|
||||
"requires": {
|
||||
"is-relative-url": "3.0.0",
|
||||
"lodash": "4.17.21",
|
||||
"stringify-object": "^3.3.0",
|
||||
"yup": "^0.29.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"yup": {
|
||||
"version": "0.29.3",
|
||||
"resolved": "https://registry.npmjs.org/yup/-/yup-0.29.3.tgz",
|
||||
"integrity": "sha512-RNUGiZ/sQ37CkhzKFoedkeMfJM0vNQyaz+wRZJzxdKE7VfDeVKH8bb4rr7XhRLbHJz5hSjoDNwMEIaKhuMZ8gQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.5",
|
||||
"fn-name": "~3.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"lodash-es": "^4.17.11",
|
||||
"property-expr": "^2.0.2",
|
||||
"synchronous-promise": "^2.0.13",
|
||||
"toposort": "^2.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"json-stable-stringify-without-jsonify": {
|
||||
"version": "1.0.1",
|
||||
|
@ -13338,6 +13470,12 @@
|
|||
"webpack-sources": "^1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
|
@ -13358,6 +13496,20 @@
|
|||
"ajv": "^6.1.0",
|
||||
"ajv-errors": "^1.0.0",
|
||||
"ajv-keywords": "^3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13607,6 +13759,16 @@
|
|||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"randexp": {
|
||||
"version": "0.4.6",
|
||||
"resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
|
||||
"integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"discontinuous-range": "1.0.0",
|
||||
"ret": "~0.1.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -15113,6 +15275,12 @@
|
|||
"schema-utils": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
|
@ -15133,6 +15301,20 @@
|
|||
"ajv": "^6.1.0",
|
||||
"ajv-errors": "^1.0.0",
|
||||
"ajv-keywords": "^3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16066,16 +16248,6 @@
|
|||
"integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=",
|
||||
"dev": true
|
||||
},
|
||||
"randexp": {
|
||||
"version": "0.4.6",
|
||||
"resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
|
||||
"integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"discontinuous-range": "1.0.0",
|
||||
"ret": "~0.1.10"
|
||||
}
|
||||
},
|
||||
"randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
|
@ -17777,6 +17949,12 @@
|
|||
"semver": "^7.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
|
||||
|
@ -17786,6 +17964,20 @@
|
|||
"@types/json-schema": "^7.0.6",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
|
@ -17832,6 +18024,26 @@
|
|||
"@types/json-schema": "^7.0.5",
|
||||
"ajv": "^6.12.4",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"scss-tokenizer": {
|
||||
|
@ -18792,7 +19004,6 @@
|
|||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
|
||||
"integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"get-own-enumerable-property-symbols": "^3.0.0",
|
||||
"is-obj": "^1.0.1",
|
||||
|
@ -19033,6 +19244,11 @@
|
|||
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
|
||||
"dev": true
|
||||
},
|
||||
"synchronous-promise": {
|
||||
"version": "2.0.15",
|
||||
"resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.15.tgz",
|
||||
"integrity": "sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg=="
|
||||
},
|
||||
"table": {
|
||||
"version": "6.7.1",
|
||||
"resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz",
|
||||
|
@ -19193,6 +19409,12 @@
|
|||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
|
@ -19270,6 +19492,20 @@
|
|||
"@types/json-schema": "^7.0.6",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
|
@ -19921,6 +20157,12 @@
|
|||
"schema-utils": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
|
||||
|
@ -19930,6 +20172,20 @@
|
|||
"@types/json-schema": "^7.0.6",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20452,6 +20708,18 @@
|
|||
"integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"braces": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
|
||||
|
@ -20569,6 +20837,12 @@
|
|||
"integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
|
||||
"dev": true
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
|
@ -20973,6 +21247,12 @@
|
|||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
|
||||
"dev": true
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
|
||||
|
@ -21141,6 +21421,20 @@
|
|||
"ajv": "^6.1.0",
|
||||
"ajv-errors": "^1.0.0",
|
||||
"ajv-keywords": "^3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
|
@ -21843,6 +22137,14 @@
|
|||
"property-expr": "^2.0.4",
|
||||
"toposort": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"yup-faker": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yup-faker/-/yup-faker-0.1.0.tgz",
|
||||
"integrity": "sha512-bQSVkIfW4Ny2Qay4i0rQU5eazsVgB1gi6nla6sHaMF92ouoTt+TSYwVbNbT7HqYslK3dg/VtypCAa5LUPVGliA==",
|
||||
"requires": {
|
||||
"faker": "^5.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"@hookform/error-message": "^2.0.0",
|
||||
"@hookform/resolvers": "^2.5.1",
|
||||
"@rooks/use-outside-click-ref": "^4.10.1",
|
||||
"@types/yup": "^0.29.13",
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"ace-builds": "^1.4.12",
|
||||
"bulma": "^0.9.3",
|
||||
|
@ -16,6 +17,7 @@
|
|||
"date-fns": "^2.19.0",
|
||||
"eslint-import-resolver-node": "^0.3.4",
|
||||
"eslint-import-resolver-typescript": "^2.4.0",
|
||||
"json-schema-yup-transformer": "^1.6.0",
|
||||
"lodash": "^4.17.21",
|
||||
"node-fetch": "^2.6.1",
|
||||
"pretty-ms": "^7.0.1",
|
||||
|
@ -35,7 +37,8 @@
|
|||
"typesafe-actions": "^5.1.0",
|
||||
"use-debounce": "^7.0.0",
|
||||
"uuid": "^8.3.1",
|
||||
"yup": "^0.32.9"
|
||||
"yup": "^0.32.9",
|
||||
"yup-faker": "^0.1.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts,jsx,tsx}": [
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
clusterTopicsPath,
|
||||
clusterTopicConsumerGroupsPath,
|
||||
clusterTopicEditPath,
|
||||
clusterTopicSendMessagePath,
|
||||
} from 'lib/paths';
|
||||
import ClusterContext from 'components/contexts/ClusterContext';
|
||||
import ConfirmationModal from 'components/common/ConfirmationModal/ConfirmationModal';
|
||||
|
@ -102,6 +103,13 @@ const Details: React.FC<Props> = ({
|
|||
Delete Topic
|
||||
</button>
|
||||
|
||||
<Link
|
||||
to={clusterTopicSendMessagePath(clusterName, topicName)}
|
||||
className="button"
|
||||
>
|
||||
Produce message
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to={clusterTopicEditPath(clusterName, topicName)}
|
||||
className="button"
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
import JSONEditor from 'components/common/JSONEditor/JSONEditor';
|
||||
import PageLoader from 'components/common/PageLoader/PageLoader';
|
||||
import {
|
||||
CreateTopicMessage,
|
||||
Partition,
|
||||
TopicMessageSchema,
|
||||
} from 'generated-sources';
|
||||
import React from 'react';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import convertToYup from 'json-schema-yup-transformer';
|
||||
import { getFakeData } from 'yup-faker';
|
||||
import { useHistory } from 'react-router';
|
||||
import { clusterTopicMessagesPath } from 'lib/paths';
|
||||
|
||||
import validateMessage from './validateMessage';
|
||||
|
||||
export interface Props {
|
||||
clusterName: string;
|
||||
topicName: string;
|
||||
fetchTopicMessageSchema: (clusterName: string, topicName: string) => void;
|
||||
sendTopicMessage: (
|
||||
clusterName: string,
|
||||
topicName: string,
|
||||
payload: CreateTopicMessage
|
||||
) => void;
|
||||
messageSchema: TopicMessageSchema | undefined;
|
||||
schemaIsFetched: boolean;
|
||||
messageIsSent: boolean;
|
||||
messageIsSending: boolean;
|
||||
partitions: Partition[];
|
||||
}
|
||||
|
||||
const SendMessage: React.FC<Props> = ({
|
||||
clusterName,
|
||||
topicName,
|
||||
fetchTopicMessageSchema,
|
||||
sendTopicMessage,
|
||||
messageSchema,
|
||||
schemaIsFetched,
|
||||
messageIsSent,
|
||||
messageIsSending,
|
||||
partitions,
|
||||
}) => {
|
||||
const [keyExampleValue, setKeyExampleValue] = React.useState('');
|
||||
const [contentExampleValue, setContentExampleValue] = React.useState('');
|
||||
const [schemaErrorString, setSchemaErrorString] = React.useState('');
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, isDirty },
|
||||
control,
|
||||
} = useForm({ mode: 'onChange' });
|
||||
const history = useHistory();
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchTopicMessageSchema(clusterName, topicName);
|
||||
}, []);
|
||||
React.useEffect(() => {
|
||||
if (schemaIsFetched && messageSchema) {
|
||||
const validateKey = convertToYup(JSON.parse(messageSchema.key.schema));
|
||||
if (validateKey) {
|
||||
setKeyExampleValue(
|
||||
JSON.stringify(getFakeData(validateKey), null, '\t')
|
||||
);
|
||||
}
|
||||
|
||||
const validateContent = convertToYup(
|
||||
JSON.parse(messageSchema.value.schema)
|
||||
);
|
||||
if (validateContent) {
|
||||
setContentExampleValue(
|
||||
JSON.stringify(getFakeData(validateContent), null, '\t')
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [schemaIsFetched]);
|
||||
React.useEffect(() => {
|
||||
if (messageIsSent) {
|
||||
history.push(clusterTopicMessagesPath(clusterName, topicName));
|
||||
}
|
||||
}, [messageIsSent]);
|
||||
|
||||
const onSubmit = async (data: {
|
||||
key: string;
|
||||
content: string;
|
||||
headers: string;
|
||||
partition: number;
|
||||
}) => {
|
||||
if (messageSchema) {
|
||||
const key = data.key || keyExampleValue;
|
||||
const content = data.content || contentExampleValue;
|
||||
const { partition } = data;
|
||||
const headers = data.headers ? JSON.parse(data.headers) : undefined;
|
||||
const messageIsValid = await validateMessage(
|
||||
key,
|
||||
content,
|
||||
messageSchema,
|
||||
setSchemaErrorString
|
||||
);
|
||||
|
||||
if (messageIsValid) {
|
||||
sendTopicMessage(clusterName, topicName, {
|
||||
key,
|
||||
content,
|
||||
headers,
|
||||
partition,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!keyExampleValue && !contentExampleValue) {
|
||||
return <PageLoader />;
|
||||
}
|
||||
return (
|
||||
<div className="box">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="columns">
|
||||
<div className="column is-one-third">
|
||||
<label className="label" htmlFor="select">
|
||||
Partition
|
||||
</label>
|
||||
<div className="select is-block">
|
||||
<select
|
||||
id="select"
|
||||
defaultValue={partitions[0].partition}
|
||||
disabled={isSubmitting || messageIsSending}
|
||||
{...register('partition')}
|
||||
>
|
||||
{partitions.map((partition) => (
|
||||
<option key={partition.partition} value={partition.partition}>
|
||||
{partition.partition}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="columns">
|
||||
<div className="column is-one-half">
|
||||
<label className="label">Key</label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="key"
|
||||
render={({ field: { name, onChange } }) => (
|
||||
<JSONEditor
|
||||
readOnly={isSubmitting || messageIsSending}
|
||||
defaultValue={keyExampleValue}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="column is-one-half">
|
||||
<label className="label">Content</label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="content"
|
||||
render={({ field: { name, onChange } }) => (
|
||||
<JSONEditor
|
||||
readOnly={isSubmitting || messageIsSending}
|
||||
defaultValue={contentExampleValue}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<label className="label">Headers</label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="headers"
|
||||
render={({ field: { name, onChange } }) => (
|
||||
<JSONEditor
|
||||
readOnly={isSubmitting || messageIsSending}
|
||||
defaultValue="{}"
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
height="200px"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{schemaErrorString && (
|
||||
<div className="mb-4">
|
||||
{schemaErrorString.split('-').map((err) => (
|
||||
<p className="help is-danger" key={err}>
|
||||
{err}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
type="submit"
|
||||
className="button is-primary"
|
||||
disabled={!isDirty || isSubmitting || messageIsSending}
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SendMessage;
|
|
@ -0,0 +1,46 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { RootState, ClusterName, TopicName } from 'redux/interfaces';
|
||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
import { fetchTopicMessageSchema, sendTopicMessage } from 'redux/actions';
|
||||
import {
|
||||
getMessageSchemaByTopicName,
|
||||
getPartitionsByTopicName,
|
||||
getTopicMessageSchemaFetched,
|
||||
getTopicMessageSending,
|
||||
getTopicMessageSent,
|
||||
} from 'redux/reducers/topics/selectors';
|
||||
|
||||
import SendMessage from './SendMessage';
|
||||
|
||||
interface RouteProps {
|
||||
clusterName: ClusterName;
|
||||
topicName: TopicName;
|
||||
}
|
||||
|
||||
type OwnProps = RouteComponentProps<RouteProps>;
|
||||
|
||||
const mapStateToProps = (
|
||||
state: RootState,
|
||||
{
|
||||
match: {
|
||||
params: { topicName, clusterName },
|
||||
},
|
||||
}: OwnProps
|
||||
) => ({
|
||||
clusterName,
|
||||
topicName,
|
||||
messageSchema: getMessageSchemaByTopicName(state, topicName),
|
||||
schemaIsFetched: getTopicMessageSchemaFetched(state),
|
||||
messageIsSent: getTopicMessageSent(state),
|
||||
messageIsSending: getTopicMessageSending(state),
|
||||
partitions: getPartitionsByTopicName(state, topicName),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchTopicMessageSchema,
|
||||
sendTopicMessage,
|
||||
};
|
||||
|
||||
export default withRouter(
|
||||
connect(mapStateToProps, mapDispatchToProps)(SendMessage)
|
||||
);
|
|
@ -0,0 +1,139 @@
|
|||
import React from 'react';
|
||||
import SendMessage, {
|
||||
Props,
|
||||
} from 'components/Topics/Topic/SendMessage/SendMessage';
|
||||
import { MessageSchemaSourceEnum } from 'generated-sources';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
|
||||
const mockConvertToYup = jest
|
||||
.fn()
|
||||
.mockReturnValue(() => ({ validate: () => true }));
|
||||
|
||||
jest.mock('yup-faker', () => ({
|
||||
getFakeData: () => ({
|
||||
f1: -93251214,
|
||||
schema: 'enim sit in fugiat dolor',
|
||||
f2: 'deserunt culpa sunt',
|
||||
}),
|
||||
}));
|
||||
|
||||
const setupWrapper = (props?: Partial<Props>) => (
|
||||
<SendMessage
|
||||
clusterName="testCluster"
|
||||
topicName="testTopic"
|
||||
fetchTopicMessageSchema={jest.fn()}
|
||||
sendTopicMessage={jest.fn()}
|
||||
messageSchema={{
|
||||
key: {
|
||||
name: 'key',
|
||||
source: MessageSchemaSourceEnum.SCHEMA_REGISTRY,
|
||||
schema: `{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://example.com/myURI.schema.json",
|
||||
"title": "TestRecord",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"f1": {
|
||||
"type": "integer"
|
||||
},
|
||||
"f2": {
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
value: {
|
||||
name: 'value',
|
||||
source: MessageSchemaSourceEnum.SCHEMA_REGISTRY,
|
||||
schema: `{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://example.com/myURI1.schema.json",
|
||||
"title": "TestRecord",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"f1": {
|
||||
"type": "integer"
|
||||
},
|
||||
"f2": {
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
}}
|
||||
schemaIsFetched={false}
|
||||
messageIsSent={false}
|
||||
messageIsSending={false}
|
||||
partitions={[
|
||||
{
|
||||
partition: 0,
|
||||
leader: 2,
|
||||
replicas: [
|
||||
{
|
||||
broker: 2,
|
||||
leader: false,
|
||||
inSync: true,
|
||||
},
|
||||
],
|
||||
offsetMax: 0,
|
||||
offsetMin: 0,
|
||||
},
|
||||
{
|
||||
partition: 1,
|
||||
leader: 1,
|
||||
replicas: [
|
||||
{
|
||||
broker: 1,
|
||||
leader: false,
|
||||
inSync: true,
|
||||
},
|
||||
],
|
||||
offsetMax: 0,
|
||||
offsetMin: 0,
|
||||
},
|
||||
]}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
describe('SendMessage', () => {
|
||||
it('calls fetchTopicMessageSchema on first render', () => {
|
||||
const fetchTopicMessageSchemaMock = jest.fn();
|
||||
render(
|
||||
setupWrapper({ fetchTopicMessageSchema: fetchTopicMessageSchemaMock })
|
||||
);
|
||||
expect(fetchTopicMessageSchemaMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
describe('when schema is fetched', () => {
|
||||
it('calls sendTopicMessage on submit', async () => {
|
||||
jest.mock('json-schema-yup-transformer', () => mockConvertToYup);
|
||||
jest.mock('../validateMessage', () => jest.fn().mockReturnValue(true));
|
||||
const mockSendTopicMessage = jest.fn();
|
||||
render(
|
||||
setupWrapper({
|
||||
schemaIsFetched: true,
|
||||
sendTopicMessage: mockSendTopicMessage,
|
||||
})
|
||||
);
|
||||
const select = await screen.findByLabelText('Partition');
|
||||
fireEvent.change(select, {
|
||||
target: { value: 2 },
|
||||
});
|
||||
await waitFor(async () => {
|
||||
fireEvent.click(await screen.findByText('Send'));
|
||||
expect(mockSendTopicMessage).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
import { MessageSchemaSourceEnum } from 'generated-sources';
|
||||
|
||||
export const testSchema = {
|
||||
key: {
|
||||
name: 'key',
|
||||
source: MessageSchemaSourceEnum.SCHEMA_REGISTRY,
|
||||
schema: `{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://example.com/myURI.schema.json",
|
||||
"title": "TestRecord",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"f1": {
|
||||
"type": "integer"
|
||||
},
|
||||
"f2": {
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
value: {
|
||||
name: 'value',
|
||||
source: MessageSchemaSourceEnum.SCHEMA_REGISTRY,
|
||||
schema: `{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://example.com/myURI1.schema.json",
|
||||
"title": "TestRecord",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"f1": {
|
||||
"type": "integer"
|
||||
},
|
||||
"f2": {
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
import validateMessage from 'components/Topics/Topic/SendMessage/validateMessage';
|
||||
|
||||
import { testSchema } from './fixtures';
|
||||
|
||||
describe('validateMessage', () => {
|
||||
it('returns true on correct input data', async () => {
|
||||
const mockSetError = jest.fn();
|
||||
expect(
|
||||
await validateMessage(
|
||||
`{
|
||||
"f1": 32,
|
||||
"f2": "multi-state",
|
||||
"schema": "Bedfordshire violet SAS"
|
||||
}`,
|
||||
`{
|
||||
"f1": 21128,
|
||||
"f2": "Health Berkshire Re-engineered",
|
||||
"schema": "Dynamic Greenland Beauty"
|
||||
}`,
|
||||
testSchema,
|
||||
mockSetError
|
||||
)
|
||||
).toBe(true);
|
||||
expect(mockSetError).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('returns false on incorrect input data', async () => {
|
||||
const mockSetError = jest.fn();
|
||||
expect(
|
||||
await validateMessage(
|
||||
`{
|
||||
"f1": "32",
|
||||
"f2": "multi-state",
|
||||
"schema": "Bedfordshire violet SAS"
|
||||
}`,
|
||||
`{
|
||||
"f1": "21128",
|
||||
"f2": "Health Berkshire Re-engineered",
|
||||
"schema": "Dynamic Greenland Beauty"
|
||||
}`,
|
||||
testSchema,
|
||||
mockSetError
|
||||
)
|
||||
).toBe(false);
|
||||
expect(mockSetError).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,67 @@
|
|||
import { TopicMessageSchema } from 'generated-sources';
|
||||
import convertToYup from 'json-schema-yup-transformer';
|
||||
|
||||
const validateMessage = async (
|
||||
key: string,
|
||||
content: string,
|
||||
messageSchema: TopicMessageSchema | undefined,
|
||||
setSchemaErrorString: React.Dispatch<React.SetStateAction<string>>
|
||||
): Promise<boolean> => {
|
||||
setSchemaErrorString('');
|
||||
try {
|
||||
if (messageSchema) {
|
||||
const validateKey = convertToYup(JSON.parse(messageSchema.key.schema));
|
||||
const validateContent = convertToYup(
|
||||
JSON.parse(messageSchema.value.schema)
|
||||
);
|
||||
let keyIsValid = false;
|
||||
let contentIsValid = false;
|
||||
|
||||
try {
|
||||
await validateKey?.validate(JSON.parse(key));
|
||||
keyIsValid = true;
|
||||
} catch (err) {
|
||||
let errorString = '';
|
||||
if (err.errors) {
|
||||
err.errors.forEach((e: string) => {
|
||||
errorString = errorString ? `${errorString}-Key ${e}` : `Key ${e}`;
|
||||
});
|
||||
} else {
|
||||
errorString = errorString
|
||||
? `${errorString}-Key ${err.message}`
|
||||
: `Key ${err.message}`;
|
||||
}
|
||||
|
||||
setSchemaErrorString((e) => (e ? `${e}-${errorString}` : errorString));
|
||||
}
|
||||
try {
|
||||
await validateContent?.validate(JSON.parse(content));
|
||||
contentIsValid = true;
|
||||
} catch (err) {
|
||||
let errorString = '';
|
||||
if (err.errors) {
|
||||
err.errors.forEach((e: string) => {
|
||||
errorString = errorString
|
||||
? `${errorString}-Content ${e}`
|
||||
: `Content ${e}`;
|
||||
});
|
||||
} else {
|
||||
errorString = errorString
|
||||
? `${errorString}-Content ${err.message}`
|
||||
: `Content ${err.message}`;
|
||||
}
|
||||
|
||||
setSchemaErrorString((e) => (e ? `${e}-${errorString}` : errorString));
|
||||
}
|
||||
|
||||
if (keyIsValid && contentIsValid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
setSchemaErrorString((e) => (e ? `${e}-${err.message}` : err.message));
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export default validateMessage;
|
|
@ -7,6 +7,8 @@ import EditContainer from 'components/Topics/Topic/Edit/EditContainer';
|
|||
import DetailsContainer from 'components/Topics/Topic/Details/DetailsContainer';
|
||||
import PageLoader from 'components/common/PageLoader/PageLoader';
|
||||
|
||||
import SendMessageContainer from './SendMessage/SendMessageContainer';
|
||||
|
||||
interface RouterParams {
|
||||
clusterName: ClusterName;
|
||||
topicName: TopicName;
|
||||
|
@ -49,6 +51,11 @@ const Topic: React.FC<TopicProps> = ({
|
|||
<div className="level">
|
||||
<div className="level-item level-left">
|
||||
<Switch>
|
||||
<Route exact path={`${topicPageUrl}/message`}>
|
||||
<Breadcrumb links={childBreadcrumbLinks}>
|
||||
Produce Message
|
||||
</Breadcrumb>
|
||||
</Route>
|
||||
<Route exact path={`${topicPageUrl}/edit`}>
|
||||
<Breadcrumb links={childBreadcrumbLinks}>Edit</Breadcrumb>
|
||||
</Route>
|
||||
|
@ -67,6 +74,11 @@ const Topic: React.FC<TopicProps> = ({
|
|||
path="/ui/clusters/:clusterName/topics/:topicName/edit"
|
||||
component={EditContainer}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/ui/clusters/:clusterName/topics/:topicName/message"
|
||||
component={SendMessageContainer}
|
||||
/>
|
||||
<Route
|
||||
path="/ui/clusters/:clusterName/topics/:topicName"
|
||||
component={DetailsContainer}
|
||||
|
|
|
@ -60,6 +60,10 @@ export const clusterTopicConsumerGroupsPath = (
|
|||
clusterName: ClusterName,
|
||||
topicName: TopicName
|
||||
) => `${clusterTopicsPath(clusterName)}/${topicName}/consumergroups`;
|
||||
export const clusterTopicSendMessagePath = (
|
||||
clusterName: ClusterName,
|
||||
topicName: TopicName
|
||||
) => `${clusterTopicsPath(clusterName)}/${topicName}/message`;
|
||||
|
||||
// Kafka Connect
|
||||
export const clusterConnectsPath = (clusterName: ClusterName) =>
|
||||
|
|
|
@ -3,7 +3,11 @@ import {
|
|||
schemaVersionsPayload,
|
||||
} from 'redux/reducers/schemas/__test__/fixtures';
|
||||
import * as actions from 'redux/actions';
|
||||
import { TopicColumnsToSort } from 'generated-sources';
|
||||
import {
|
||||
MessageSchemaSourceEnum,
|
||||
TopicColumnsToSort,
|
||||
TopicMessageSchema,
|
||||
} from 'generated-sources';
|
||||
import { FailurePayload } from 'redux/interfaces';
|
||||
|
||||
import { mockTopicsState } from './fixtures';
|
||||
|
@ -202,4 +206,89 @@ describe('Actions', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sending messages', () => {
|
||||
describe('fetchTopicMessageSchemaAction', () => {
|
||||
it('creates GET_TOPIC_SCHEMA__REQUEST', () => {
|
||||
expect(actions.fetchTopicMessageSchemaAction.request()).toEqual({
|
||||
type: 'GET_TOPIC_SCHEMA__REQUEST',
|
||||
});
|
||||
});
|
||||
it('creates GET_TOPIC_SCHEMA__SUCCESS', () => {
|
||||
const messageSchema: TopicMessageSchema = {
|
||||
key: {
|
||||
name: 'key',
|
||||
source: MessageSchemaSourceEnum.SCHEMA_REGISTRY,
|
||||
schema: `{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://example.com/myURI.schema.json",
|
||||
"title": "TestRecord",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"f1": {
|
||||
"type": "integer"
|
||||
},
|
||||
"f2": {
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
value: {
|
||||
name: 'value',
|
||||
source: MessageSchemaSourceEnum.SCHEMA_REGISTRY,
|
||||
schema: `{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://example.com/myURI1.schema.json",
|
||||
"title": "TestRecord",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"f1": {
|
||||
"type": "integer"
|
||||
},
|
||||
"f2": {
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
};
|
||||
expect(
|
||||
actions.fetchTopicMessageSchemaAction.success({
|
||||
topicName: 'test',
|
||||
schema: messageSchema,
|
||||
})
|
||||
).toEqual({
|
||||
type: 'GET_TOPIC_SCHEMA__SUCCESS',
|
||||
payload: {
|
||||
topicName: 'test',
|
||||
schema: messageSchema,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('creates GET_TOPIC_SCHEMA__FAILURE', () => {
|
||||
const alert: FailurePayload = {
|
||||
subject: ['message-chema', 'test'].join('-'),
|
||||
title: `Message Schema Test`,
|
||||
};
|
||||
expect(
|
||||
actions.fetchTopicMessageSchemaAction.failure({ alert })
|
||||
).toEqual({
|
||||
type: 'GET_TOPIC_SCHEMA__FAILURE',
|
||||
payload: { alert },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@ import * as actions from 'redux/actions/actions';
|
|||
import * as thunks from 'redux/actions/thunks';
|
||||
import mockStoreCreator from 'redux/store/configureStore/mockStoreCreator';
|
||||
import { mockTopicsState } from 'redux/actions/__test__/fixtures';
|
||||
import { MessageSchemaSourceEnum, TopicMessageSchema } from 'generated-sources';
|
||||
import { FailurePayload } from 'redux/interfaces';
|
||||
import { getResponse } from 'lib/errorHandling';
|
||||
|
||||
|
@ -135,6 +136,146 @@ describe('Thunks', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('fetchTopicMessageSchema', () => {
|
||||
it('creates GET_TOPIC_SCHEMA__FAILURE', async () => {
|
||||
fetchMock.getOnce(
|
||||
`/api/clusters/${clusterName}/topics/${topicName}/messages/schema`,
|
||||
404
|
||||
);
|
||||
try {
|
||||
await store.dispatch(
|
||||
thunks.fetchTopicMessageSchema(clusterName, topicName)
|
||||
);
|
||||
} catch (error) {
|
||||
expect(error.status).toEqual(404);
|
||||
expect(store.getActions()).toEqual([
|
||||
actions.fetchTopicMessageSchemaAction.request(),
|
||||
actions.fetchTopicMessageSchemaAction.failure({
|
||||
alert: {
|
||||
subject: ['topic', topicName].join('-'),
|
||||
title: `Topic Schema ${topicName}`,
|
||||
response: error,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
it('creates GET_TOPIC_SCHEMA__SUCCESS', async () => {
|
||||
const messageSchema: TopicMessageSchema = {
|
||||
key: {
|
||||
name: 'key',
|
||||
source: MessageSchemaSourceEnum.SCHEMA_REGISTRY,
|
||||
schema: `{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://example.com/myURI.schema.json",
|
||||
"title": "TestRecord",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"f1": {
|
||||
"type": "integer"
|
||||
},
|
||||
"f2": {
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
value: {
|
||||
name: 'value',
|
||||
source: MessageSchemaSourceEnum.SCHEMA_REGISTRY,
|
||||
schema: `{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://example.com/myURI1.schema.json",
|
||||
"title": "TestRecord",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"f1": {
|
||||
"type": "integer"
|
||||
},
|
||||
"f2": {
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
};
|
||||
fetchMock.getOnce(
|
||||
`/api/clusters/${clusterName}/topics/${topicName}/messages/schema`,
|
||||
messageSchema
|
||||
);
|
||||
await store.dispatch(
|
||||
thunks.fetchTopicMessageSchema(clusterName, topicName)
|
||||
);
|
||||
expect(store.getActions()).toEqual([
|
||||
actions.fetchTopicMessageSchemaAction.request(),
|
||||
actions.fetchTopicMessageSchemaAction.success({
|
||||
topicName,
|
||||
schema: messageSchema,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sendTopicMessage', () => {
|
||||
it('creates SEND_TOPIC_MESSAGE__FAILURE', async () => {
|
||||
fetchMock.postOnce(
|
||||
`/api/clusters/${clusterName}/topics/${topicName}/messages`,
|
||||
404
|
||||
);
|
||||
try {
|
||||
await store.dispatch(
|
||||
thunks.sendTopicMessage(clusterName, topicName, {
|
||||
key: '{}',
|
||||
content: '{}',
|
||||
headers: undefined,
|
||||
partition: 0,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
expect(error.status).toEqual(404);
|
||||
expect(store.getActions()).toEqual([
|
||||
actions.sendTopicMessageAction.request(),
|
||||
actions.sendTopicMessageAction.failure({
|
||||
alert: {
|
||||
subject: ['topic', topicName].join('-'),
|
||||
title: `Topic Message ${topicName}`,
|
||||
response: error,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
it('creates SEND_TOPIC_MESSAGE__SUCCESS', async () => {
|
||||
fetchMock.postOnce(
|
||||
`/api/clusters/${clusterName}/topics/${topicName}/messages`,
|
||||
200
|
||||
);
|
||||
await store.dispatch(
|
||||
thunks.sendTopicMessage(clusterName, topicName, {
|
||||
key: '{}',
|
||||
content: '{}',
|
||||
headers: undefined,
|
||||
partition: 0,
|
||||
})
|
||||
);
|
||||
expect(store.getActions()).toEqual([
|
||||
actions.sendTopicMessageAction.request(),
|
||||
actions.sendTopicMessageAction.success(),
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('increasing partitions count', () => {
|
||||
it('calls updateTopicPartitionsCountAction.success on success', async () => {
|
||||
fetchMock.patchOnce(
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
FullConnectorInfo,
|
||||
Connect,
|
||||
Task,
|
||||
TopicMessageSchema,
|
||||
} from 'generated-sources';
|
||||
|
||||
export const fetchClusterStatsAction = createAsyncAction(
|
||||
|
@ -254,6 +255,22 @@ export const fetchTopicConsumerGroupsAction = createAsyncAction(
|
|||
'GET_TOPIC_CONSUMER_GROUPS__FAILURE'
|
||||
)<undefined, TopicsState, undefined>();
|
||||
|
||||
export const fetchTopicMessageSchemaAction = createAsyncAction(
|
||||
'GET_TOPIC_SCHEMA__REQUEST',
|
||||
'GET_TOPIC_SCHEMA__SUCCESS',
|
||||
'GET_TOPIC_SCHEMA__FAILURE'
|
||||
)<
|
||||
undefined,
|
||||
{ topicName: string; schema: TopicMessageSchema },
|
||||
{ alert?: FailurePayload }
|
||||
>();
|
||||
|
||||
export const sendTopicMessageAction = createAsyncAction(
|
||||
'SEND_TOPIC_MESSAGE__REQUEST',
|
||||
'SEND_TOPIC_MESSAGE__SUCCESS',
|
||||
'SEND_TOPIC_MESSAGE__FAILURE'
|
||||
)<undefined, undefined, { alert?: FailurePayload }>();
|
||||
|
||||
export const updateTopicPartitionsCountAction = createAsyncAction(
|
||||
'UPDATE_PARTITIONS__REQUEST',
|
||||
'UPDATE_PARTITIONS__SUCCESS',
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
TopicConfig,
|
||||
TopicColumnsToSort,
|
||||
ConsumerGroupsApi,
|
||||
CreateTopicMessage,
|
||||
} from 'generated-sources';
|
||||
import {
|
||||
PromiseThunkResult,
|
||||
|
@ -342,6 +343,59 @@ export const fetchTopicConsumerGroups =
|
|||
}
|
||||
};
|
||||
|
||||
export const fetchTopicMessageSchema =
|
||||
(clusterName: ClusterName, topicName: TopicName): PromiseThunkResult =>
|
||||
async (dispatch) => {
|
||||
dispatch(actions.fetchTopicMessageSchemaAction.request());
|
||||
try {
|
||||
const schema = await messagesApiClient.getTopicSchema({
|
||||
clusterName,
|
||||
topicName,
|
||||
});
|
||||
dispatch(
|
||||
actions.fetchTopicMessageSchemaAction.success({ topicName, schema })
|
||||
);
|
||||
} catch (e) {
|
||||
const response = await getResponse(e);
|
||||
const alert: FailurePayload = {
|
||||
subject: ['topic', topicName].join('-'),
|
||||
title: `Topic Schema ${topicName}`,
|
||||
response,
|
||||
};
|
||||
dispatch(actions.fetchTopicMessageSchemaAction.failure({ alert }));
|
||||
}
|
||||
};
|
||||
|
||||
export const sendTopicMessage =
|
||||
(
|
||||
clusterName: ClusterName,
|
||||
topicName: TopicName,
|
||||
payload: CreateTopicMessage
|
||||
): PromiseThunkResult =>
|
||||
async (dispatch) => {
|
||||
dispatch(actions.sendTopicMessageAction.request());
|
||||
try {
|
||||
await messagesApiClient.sendTopicMessages({
|
||||
clusterName,
|
||||
topicName,
|
||||
createTopicMessage: {
|
||||
key: payload.key,
|
||||
content: payload.content,
|
||||
headers: payload.headers,
|
||||
partition: payload.partition,
|
||||
},
|
||||
});
|
||||
dispatch(actions.sendTopicMessageAction.success());
|
||||
} catch (e) {
|
||||
const response = await getResponse(e);
|
||||
const alert: FailurePayload = {
|
||||
subject: ['topic', topicName].join('-'),
|
||||
title: `Topic Message ${topicName}`,
|
||||
response,
|
||||
};
|
||||
dispatch(actions.sendTopicMessageAction.failure({ alert }));
|
||||
}
|
||||
};
|
||||
export const updateTopicPartitionsCount =
|
||||
(
|
||||
clusterName: ClusterName,
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
GetTopicMessagesRequest,
|
||||
ConsumerGroup,
|
||||
TopicColumnsToSort,
|
||||
TopicMessageSchema,
|
||||
} from 'generated-sources';
|
||||
|
||||
export type TopicName = Topic['name'];
|
||||
|
@ -42,6 +43,7 @@ export interface TopicFormCustomParams {
|
|||
export interface TopicWithDetailedInfo extends Topic, TopicDetails {
|
||||
config?: TopicConfig[];
|
||||
consumerGroups?: ConsumerGroup[];
|
||||
messageSchema?: TopicMessageSchema;
|
||||
}
|
||||
|
||||
export interface TopicsState {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { TopicColumnsToSort } from 'generated-sources';
|
||||
import { MessageSchemaSourceEnum, TopicColumnsToSort } from 'generated-sources';
|
||||
import {
|
||||
deleteTopicAction,
|
||||
clearMessagesTopicAction,
|
||||
setTopicsSearchAction,
|
||||
setTopicsOrderByAction,
|
||||
fetchTopicConsumerGroupsAction,
|
||||
fetchTopicMessageSchemaAction,
|
||||
} from 'redux/actions';
|
||||
import reducer from 'redux/reducers/topics/reducer';
|
||||
|
||||
|
@ -13,7 +14,56 @@ const topic = {
|
|||
id: 'id',
|
||||
};
|
||||
|
||||
const state = {
|
||||
const messageSchema = {
|
||||
key: {
|
||||
name: 'key',
|
||||
source: MessageSchemaSourceEnum.SCHEMA_REGISTRY,
|
||||
schema: `{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://example.com/myURI.schema.json",
|
||||
"title": "TestRecord",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"f1": {
|
||||
"type": "integer"
|
||||
},
|
||||
"f2": {
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
value: {
|
||||
name: 'value',
|
||||
source: MessageSchemaSourceEnum.SCHEMA_REGISTRY,
|
||||
schema: `{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://example.com/myURI1.schema.json",
|
||||
"title": "TestRecord",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"f1": {
|
||||
"type": "integer"
|
||||
},
|
||||
"f2": {
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
let state = {
|
||||
byName: {
|
||||
[topic.name]: topic,
|
||||
},
|
||||
|
@ -70,4 +120,31 @@ describe('topics reducer', () => {
|
|||
).toEqual(state);
|
||||
});
|
||||
});
|
||||
|
||||
describe('message sending', () => {
|
||||
it('adds message shema after fetching it', () => {
|
||||
state = {
|
||||
byName: {
|
||||
[topic.name]: topic,
|
||||
},
|
||||
allNames: [topic.name],
|
||||
messages: [],
|
||||
totalPages: 1,
|
||||
search: '',
|
||||
orderBy: null,
|
||||
consumerGroups: [],
|
||||
};
|
||||
expect(
|
||||
reducer(
|
||||
state,
|
||||
fetchTopicMessageSchemaAction.success({
|
||||
topicName: 'topic',
|
||||
schema: messageSchema,
|
||||
})
|
||||
).byName
|
||||
).toEqual({
|
||||
[topic.name]: { ...topic, messageSchema },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,6 +2,7 @@ import { TopicMessage } from 'generated-sources';
|
|||
import { Action, TopicsState } from 'redux/interfaces';
|
||||
import { getType } from 'typesafe-actions';
|
||||
import * as actions from 'redux/actions';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export const initialState: TopicsState = {
|
||||
byName: {},
|
||||
|
@ -26,7 +27,7 @@ const transformTopicMessages = (
|
|||
try {
|
||||
parsedContent =
|
||||
typeof content !== 'object' ? JSON.parse(content) : content;
|
||||
} catch (_) {
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +76,15 @@ const reducer = (state = initialState, action: Action): TopicsState => {
|
|||
orderBy: action.payload,
|
||||
};
|
||||
}
|
||||
case getType(actions.fetchTopicMessageSchemaAction.success): {
|
||||
const { topicName, schema } = action.payload;
|
||||
const newState = _.cloneDeep(state);
|
||||
newState.byName[topicName] = {
|
||||
...newState.byName[topicName],
|
||||
messageSchema: schema,
|
||||
};
|
||||
return newState;
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ const getTopicMessagesFetchingStatus =
|
|||
const getTopicConfigFetchingStatus = createFetchingSelector('GET_TOPIC_CONFIG');
|
||||
const getTopicCreationStatus = createFetchingSelector('POST_TOPIC');
|
||||
const getTopicUpdateStatus = createFetchingSelector('PATCH_TOPIC');
|
||||
const getTopicMessageSchemaFetchingStatus =
|
||||
createFetchingSelector('GET_TOPIC_SCHEMA');
|
||||
const getTopicMessageSendingStatus =
|
||||
createFetchingSelector('SEND_TOPIC_MESSAGE');
|
||||
const getPartitionsCountIncreaseStatus =
|
||||
createFetchingSelector('UPDATE_PARTITIONS');
|
||||
const getReplicationFactorUpdateStatus = createFetchingSelector(
|
||||
|
@ -71,6 +75,21 @@ export const getTopicUpdated = createSelector(
|
|||
(status) => status === 'fetched'
|
||||
);
|
||||
|
||||
export const getTopicMessageSchemaFetched = createSelector(
|
||||
getTopicMessageSchemaFetchingStatus,
|
||||
(status) => status === 'fetched'
|
||||
);
|
||||
|
||||
export const getTopicMessageSent = createSelector(
|
||||
getTopicMessageSendingStatus,
|
||||
(status) => status === 'fetched'
|
||||
);
|
||||
|
||||
export const getTopicMessageSending = createSelector(
|
||||
getTopicMessageSendingStatus,
|
||||
(status) => status === 'fetching'
|
||||
);
|
||||
|
||||
export const getTopicPartitionsCountIncreased = createSelector(
|
||||
getPartitionsCountIncreaseStatus,
|
||||
(status) => status === 'fetched'
|
||||
|
@ -157,3 +176,8 @@ export const getTopicConsumerGroups = createSelector(
|
|||
getTopicName,
|
||||
(topics, topicName) => topics[topicName].consumerGroups || []
|
||||
);
|
||||
|
||||
export const getMessageSchemaByTopicName = createSelector(
|
||||
getTopicByName,
|
||||
(topic) => topic.messageSchema
|
||||
);
|
||||
|
|
Loading…
Add table
Reference in a new issue