mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-21 16:09:12 +03:00
UBERF-7690: Trigger improvements (#6340)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
d26a03f814
commit
c01a274cef
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -77,7 +77,7 @@
|
||||
"ACCOUNT_PORT": "3000",
|
||||
"FRONT_URL": "http://localhost:8080",
|
||||
"outputCapture": "std",
|
||||
"SES_URL": "http://localhost:8091",
|
||||
"SES_URL": "",
|
||||
"MINIO_ACCESS_KEY": "minioadmin",
|
||||
"MINIO_SECRET_KEY": "minioadmin",
|
||||
"MINIO_ENDPOINT": "localhost"
|
||||
|
@ -1377,8 +1377,8 @@ dependencies:
|
||||
specifier: ^1.20.2
|
||||
version: 1.20.2
|
||||
browserslist:
|
||||
specifier: 4.21.5
|
||||
version: 4.21.5
|
||||
specifier: ^4.23.3
|
||||
version: 4.23.3
|
||||
bson:
|
||||
specifier: ^6.8.0
|
||||
version: 6.8.0
|
||||
@ -1815,8 +1815,8 @@ dependencies:
|
||||
specifier: ^5.3.3
|
||||
version: 5.3.3
|
||||
update-browserslist-db:
|
||||
specifier: ~1.0.11
|
||||
version: 1.0.13(browserslist@4.21.5)
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0(browserslist@4.23.3)
|
||||
url-loader:
|
||||
specifier: ~4.1.1
|
||||
version: 4.1.1(file-loader@6.2.0)(webpack@5.90.3)
|
||||
@ -2657,7 +2657,7 @@ packages:
|
||||
dependencies:
|
||||
'@babel/compat-data': 7.23.5
|
||||
'@babel/helper-validator-option': 7.23.5
|
||||
browserslist: 4.23.0
|
||||
browserslist: 4.23.3
|
||||
lru-cache: 5.1.1
|
||||
semver: 6.3.1
|
||||
dev: false
|
||||
@ -10805,7 +10805,7 @@ packages:
|
||||
peerDependencies:
|
||||
postcss: ^8.1.0
|
||||
dependencies:
|
||||
browserslist: 4.23.0
|
||||
browserslist: 4.23.3
|
||||
caniuse-lite: 1.0.30001589
|
||||
fraction.js: 4.3.7
|
||||
normalize-range: 0.1.2
|
||||
@ -11196,26 +11196,15 @@ packages:
|
||||
pako: 0.2.9
|
||||
dev: false
|
||||
|
||||
/browserslist@4.21.5:
|
||||
resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==}
|
||||
/browserslist@4.23.3:
|
||||
resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==}
|
||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001589
|
||||
electron-to-chromium: 1.4.679
|
||||
node-releases: 2.0.14
|
||||
update-browserslist-db: 1.0.13(browserslist@4.21.5)
|
||||
dev: false
|
||||
|
||||
/browserslist@4.23.0:
|
||||
resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
|
||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001589
|
||||
electron-to-chromium: 1.4.679
|
||||
node-releases: 2.0.14
|
||||
update-browserslist-db: 1.0.13(browserslist@4.23.0)
|
||||
caniuse-lite: 1.0.30001651
|
||||
electron-to-chromium: 1.5.11
|
||||
node-releases: 2.0.18
|
||||
update-browserslist-db: 1.1.0(browserslist@4.23.3)
|
||||
dev: false
|
||||
|
||||
/bs-logger@0.2.6:
|
||||
@ -11468,6 +11457,10 @@ packages:
|
||||
resolution: {integrity: sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg==}
|
||||
dev: false
|
||||
|
||||
/caniuse-lite@1.0.30001651:
|
||||
resolution: {integrity: sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==}
|
||||
dev: false
|
||||
|
||||
/case-anything@2.1.13:
|
||||
resolution: {integrity: sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==}
|
||||
engines: {node: '>=12.13'}
|
||||
@ -12019,7 +12012,7 @@ packages:
|
||||
/core-js-compat@3.36.0:
|
||||
resolution: {integrity: sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==}
|
||||
dependencies:
|
||||
browserslist: 4.23.0
|
||||
browserslist: 4.23.3
|
||||
dev: false
|
||||
|
||||
/core-js@3.36.0:
|
||||
@ -13121,8 +13114,8 @@ packages:
|
||||
type-fest: 2.19.0
|
||||
dev: false
|
||||
|
||||
/electron-to-chromium@1.4.679:
|
||||
resolution: {integrity: sha512-NhQMsz5k0d6m9z3qAxnsOR/ebal4NAGsrNVRwcDo4Kc/zQ7KdsTKZUxZoygHcVRb0QDW3waEDIcE3isZ79RP6g==}
|
||||
/electron-to-chromium@1.5.11:
|
||||
resolution: {integrity: sha512-R1CccCDYqndR25CaXFd6hp/u9RaaMcftMkphmvuepXr5b1vfLkRml6aWVeBhXJ7rbevHkKEMJtz8XqPf7ffmew==}
|
||||
dev: false
|
||||
|
||||
/electron-updater@6.2.1:
|
||||
@ -18443,8 +18436,8 @@ packages:
|
||||
write-file-atomic: 1.3.4
|
||||
dev: false
|
||||
|
||||
/node-releases@2.0.14:
|
||||
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
|
||||
/node-releases@2.0.18:
|
||||
resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
|
||||
dev: false
|
||||
|
||||
/nopt@1.0.10:
|
||||
@ -19229,6 +19222,10 @@ packages:
|
||||
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
||||
dev: false
|
||||
|
||||
/picocolors@1.0.1:
|
||||
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
|
||||
dev: false
|
||||
|
||||
/picomatch@2.3.1:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
engines: {node: '>=8.6'}
|
||||
@ -22785,26 +22782,15 @@ packages:
|
||||
path-exists: 5.0.0
|
||||
dev: false
|
||||
|
||||
/update-browserslist-db@1.0.13(browserslist@4.21.5):
|
||||
resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
|
||||
/update-browserslist-db@1.1.0(browserslist@4.23.3):
|
||||
resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
browserslist: '>= 4.21.0'
|
||||
dependencies:
|
||||
browserslist: 4.21.5
|
||||
browserslist: 4.23.3
|
||||
escalade: 3.1.2
|
||||
picocolors: 1.0.0
|
||||
dev: false
|
||||
|
||||
/update-browserslist-db@1.0.13(browserslist@4.23.0):
|
||||
resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
browserslist: '>= 4.21.0'
|
||||
dependencies:
|
||||
browserslist: 4.23.0
|
||||
escalade: 3.1.2
|
||||
picocolors: 1.0.0
|
||||
picocolors: 1.0.1
|
||||
dev: false
|
||||
|
||||
/uri-js@4.4.1:
|
||||
@ -23282,7 +23268,7 @@ packages:
|
||||
'@webassemblyjs/wasm-parser': 1.11.6
|
||||
acorn: 8.11.3
|
||||
acorn-import-assertions: 1.9.0(acorn@8.11.3)
|
||||
browserslist: 4.23.0
|
||||
browserslist: 4.23.3
|
||||
chrome-trace-event: 1.0.3
|
||||
enhanced-resolve: 5.15.0
|
||||
es-module-lexer: 1.4.1
|
||||
@ -23323,7 +23309,7 @@ packages:
|
||||
'@webassemblyjs/wasm-parser': 1.11.6
|
||||
acorn: 8.11.3
|
||||
acorn-import-assertions: 1.9.0(acorn@8.11.3)
|
||||
browserslist: 4.23.0
|
||||
browserslist: 4.23.3
|
||||
chrome-trace-event: 1.0.3
|
||||
enhanced-resolve: 5.15.0
|
||||
es-module-lexer: 1.4.1
|
||||
@ -24298,7 +24284,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/attachment-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-ZEkxtUt4tXWqiEQOGDSFHr65Zw9PtVeCQJ5zS8zdDdpF+vh1BAaLW7OydhYafKYw5RheG50HOK/R6wPDcNd3Lw==, tarball: file:projects/attachment-resources.tgz}
|
||||
resolution: {integrity: sha512-yEPWonMOvXhIk5+IaNQVsyw6wnfqdcB/4UsEh8BLCQwhpepBeVrNSXgS+mLspQaL9g15E81PHwyrWyF1OcqQOQ==, tarball: file:projects/attachment-resources.tgz}
|
||||
id: file:projects/attachment-resources.tgz
|
||||
name: '@rush-temp/attachment-resources'
|
||||
version: 0.0.0
|
||||
@ -25127,7 +25113,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/contact-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-iVkm89qU3Sx3zlhJRYkBsMy03G536YvuMT1tc6WTLMzklcpqYyCOtklXRWUGGiF/+a56Dog4hu7ai10QUIajrA==, tarball: file:projects/contact-resources.tgz}
|
||||
resolution: {integrity: sha512-Vlw7yfM/X0ViZ1mzmcJFVPAZk/2hp4l768lxErLF/sA2HKsRUCAvkjbzfHmImsK5e2WycFWOWwnUJu9p1irQWQ==, tarball: file:projects/contact-resources.tgz}
|
||||
id: file:projects/contact-resources.tgz
|
||||
name: '@rush-temp/contact-resources'
|
||||
version: 0.0.0
|
||||
@ -25320,7 +25306,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/core.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-BgJt/g8bufJyz3tFDrdM6Y3GHHTzKs7DUVMylyP2kyPCyrq3TrBDOGajz6QJ36kObLH/KWWau/LRmB3ZWIFU7Q==, tarball: file:projects/core.tgz}
|
||||
resolution: {integrity: sha512-qorGEpUYJYJMlHr0WkKpS8sUfQt3GQtF7xCdUSU5K9B/aRgAL6z0LGRJoUmn/kYHHV+r6DITWhOiP6zIHcKmdg==, tarball: file:projects/core.tgz}
|
||||
id: file:projects/core.tgz
|
||||
name: '@rush-temp/core'
|
||||
version: 0.0.0
|
||||
@ -25515,7 +25501,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/desktop.tgz(bufferutil@4.0.8)(sass@1.71.1)(utf-8-validate@6.0.4):
|
||||
resolution: {integrity: sha512-j8onQxy51K29b/9q8g+lOG8e8dOFO2WpQQTdtnhP2vy2EcvIuSgckenQpg++0r1AtJ1G7FC7hr5QYNsrTPWkBA==, tarball: file:projects/desktop.tgz}
|
||||
resolution: {integrity: sha512-+dbbvdgHJD76pLjOUSctj1tb9kC3hWa4CisFJxvSmlObAUII4NI+4EmE4u1x8OWaGEDqRCPCmrGFk1aSiPF5fQ==, tarball: file:projects/desktop.tgz}
|
||||
id: file:projects/desktop.tgz
|
||||
name: '@rush-temp/desktop'
|
||||
version: 0.0.0
|
||||
@ -25525,7 +25511,7 @@ packages:
|
||||
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
|
||||
'@vercel/webpack-asset-relocator-loader': 1.7.4
|
||||
autoprefixer: 10.4.17(postcss@8.4.35)
|
||||
browserslist: 4.21.5
|
||||
browserslist: 4.23.3
|
||||
commander: 8.3.0
|
||||
compression-webpack-plugin: 10.0.0(webpack@5.90.3)
|
||||
copy-webpack-plugin: 11.0.0(webpack@5.90.3)
|
||||
@ -25567,7 +25553,7 @@ packages:
|
||||
ts-node: 10.9.2(@types/node@20.11.19)(typescript@5.3.3)
|
||||
ts-node-dev: 2.0.0(@types/node@20.11.19)(typescript@5.3.3)
|
||||
typescript: 5.3.3
|
||||
update-browserslist-db: 1.0.13(browserslist@4.21.5)
|
||||
update-browserslist-db: 1.1.0(browserslist@4.23.3)
|
||||
webpack: 5.90.3(esbuild@0.20.1)(webpack-cli@5.1.4)
|
||||
webpack-bundle-analyzer: 4.10.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack-dev-server@4.15.1)(webpack@5.90.3)
|
||||
@ -25812,7 +25798,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/document-resources.tgz(@tiptap/pm@2.2.4)(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-paT/sxyRFUxsWTwzgk98+gGZZkT1a5EyFzSKE4UPyW9sqjM4IiyyIIrr57YZEpILDApfg51PF3qhjEtWV9kf1Q==, tarball: file:projects/document-resources.tgz}
|
||||
resolution: {integrity: sha512-YK9gh+kYmMkJHyLvlo94mEr3QznTo4V7TMypOcnHxg4ZKTfRl1mrDNI/dSAW566wmbmm7Eln0WRboX30yRxPTA==, tarball: file:projects/document-resources.tgz}
|
||||
id: file:projects/document-resources.tgz
|
||||
name: '@rush-temp/document-resources'
|
||||
version: 0.0.0
|
||||
@ -26438,7 +26424,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/hr-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-85fy55EMpfKKMiNAp0tW70KLnbppfEp9cDx93jYlteFoPpsi05GRZ2ZNYM9rLWaZ1wMYF1vsX42M0opDIX6KOQ==, tarball: file:projects/hr-resources.tgz}
|
||||
resolution: {integrity: sha512-O0jhwaTcNjWwviO2wUzfwpH7AroMYBCAbgsRsLwBsDdW08fnp1LqKswgViDL/ZOsQ5ZvboCwRAOaR8VYVfojZg==, tarball: file:projects/hr-resources.tgz}
|
||||
id: file:projects/hr-resources.tgz
|
||||
name: '@rush-temp/hr-resources'
|
||||
version: 0.0.0
|
||||
@ -26776,7 +26762,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/lead-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-DIL0rCz2r3nKYgE2fOrEdVSwLGq1Zz2MjsU1HvDxnIs4IE1Q4s8oNMBtvtunERIQ2fNnJHcE0XXijawtYilrjQ==, tarball: file:projects/lead-resources.tgz}
|
||||
resolution: {integrity: sha512-TJVh5S1o+GvRWeeNWwXveGOpMtsQTR0n5RMGjK3kXsuAUYCQRPzgMh3noJxe2n4vvijK8IUNjAR9+AjeSPo5kw==, tarball: file:projects/lead-resources.tgz}
|
||||
id: file:projects/lead-resources.tgz
|
||||
name: '@rush-temp/lead-resources'
|
||||
version: 0.0.0
|
||||
@ -27182,7 +27168,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-analytics-collector.tgz:
|
||||
resolution: {integrity: sha512-OcczFEesVXSXUuJ/HpZ880slAZeV8Y/MhZArlxBlO90Tm3jO42cyQLAi0kO36ZAkO/gGVpAHUpuZT0QB70ovvw==, tarball: file:projects/model-analytics-collector.tgz}
|
||||
resolution: {integrity: sha512-ht/R4aa2RvdT8odpbznVfFTed3dJGToS1q5tKHqJIF8BQhjNcVh9ZUmAJNnNZNTeD8J6yuKIPq7n/Io2CuTYdg==, tarball: file:projects/model-analytics-collector.tgz}
|
||||
name: '@rush-temp/model-analytics-collector'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -27391,7 +27377,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-drive.tgz:
|
||||
resolution: {integrity: sha512-V1Zaxs9HItgN9Zp8M2rdPjTUS1ZSkNaDUCeRcW5xuOdD0YY8xijRwZ+d2HF5G6xYgRwTpfqIw0O1cUBmqJAY8A==, tarball: file:projects/model-drive.tgz}
|
||||
resolution: {integrity: sha512-JG62skvnh8n7bBwsriHH6TE71W/gVm287OVnmy4VRJVf/tDC0/sCKlg+75qvcSu3CmAjaYqnnJz5q1Lz9EH3xw==, tarball: file:projects/model-drive.tgz}
|
||||
name: '@rush-temp/model-drive'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -28389,7 +28375,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-telegram.tgz:
|
||||
resolution: {integrity: sha512-WjCLxLQvcXBwLNQQnCbClBIum033r+fwS3nwB7RmCTgERzxT7lqedPlCwsd1TUs7PF6C0Y6cp5O/Lrnn+7Z46g==, tarball: file:projects/model-telegram.tgz}
|
||||
resolution: {integrity: sha512-uXXH0LQVtX+rJ0+XLPft4NLphBmtXs9ZY+g16hC9x7j5JToKwsYsBHR/rktANxYU3CyAHeoqA7S7CiYbGLrDVw==, tarball: file:projects/model-telegram.tgz}
|
||||
name: '@rush-temp/model-telegram'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -28674,7 +28660,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/notification-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-D5FLFfyRUTqKy52JGk4IMGhROrTRq+YZ+34+y2awY2U7JBUbpjcxhMkFBMgr/9TrZhSTvZrw7VvL5FCruoEN4Q==, tarball: file:projects/notification-resources.tgz}
|
||||
resolution: {integrity: sha512-xpvJbpFBrKB3RPG8SgT9+cJj2IkysBNcl4jQAV69ACUvnTQMh4RS9P31zpaO7CObYXrm9/ImRF0fXbH+AkiJsg==, tarball: file:projects/notification-resources.tgz}
|
||||
id: file:projects/notification-resources.tgz
|
||||
name: '@rush-temp/notification-resources'
|
||||
version: 0.0.0
|
||||
@ -29099,7 +29085,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/pod-analytics-collector.tgz(bufferutil@4.0.8)(utf-8-validate@6.0.4):
|
||||
resolution: {integrity: sha512-Sx9mTOCsgDTcoC8h9VXeemVHerKLMDzdW4aJ08fdsF9UA35tLWmbDXw+fn3gPUNj2l+QT5ExEF9rV1Iez1FIbw==, tarball: file:projects/pod-analytics-collector.tgz}
|
||||
resolution: {integrity: sha512-15hQZSiqnsgeZDg3n3AE2D2mx6xBToMB+2z+nDKLAKbaa+XAjMyj7J/Z9nLwfjs2EX6Z8x10JoPvs4au/SXxlA==, tarball: file:projects/pod-analytics-collector.tgz}
|
||||
id: file:projects/pod-analytics-collector.tgz
|
||||
name: '@rush-temp/pod-analytics-collector'
|
||||
version: 0.0.0
|
||||
@ -30043,7 +30029,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/prod.tgz(bufferutil@4.0.8)(sass@1.71.1)(ts-node@10.9.2)(utf-8-validate@6.0.4):
|
||||
resolution: {integrity: sha512-U0i5q0YgmqM+29OoNKE5F7SYHLenVyjGDEus4MgN7CqMIC1n6n2gtztzRm2O6/j8hba5E6asgHgGF+ljESlbGw==, tarball: file:projects/prod.tgz}
|
||||
resolution: {integrity: sha512-lYEzH4pmYid/iB0WNGOZXt3y9nyDbs70r3jYZJ6qIQEyvQVqWXnAd8Rw+nqcEbI/QtTou81FrvAK2Fnp5Imeww==, tarball: file:projects/prod.tgz}
|
||||
id: file:projects/prod.tgz
|
||||
name: '@rush-temp/prod'
|
||||
version: 0.0.0
|
||||
@ -30051,7 +30037,7 @@ packages:
|
||||
'@sentry/svelte': 7.101.1(svelte@4.2.12)
|
||||
'@types/node': 20.11.19
|
||||
autoprefixer: 10.4.17(postcss@8.4.35)
|
||||
browserslist: 4.21.5
|
||||
browserslist: 4.23.3
|
||||
compression-webpack-plugin: 10.0.0(webpack@5.90.3)
|
||||
cross-env: 7.0.3
|
||||
css-loader: 5.2.7(webpack@5.90.3)
|
||||
@ -30074,7 +30060,7 @@ packages:
|
||||
svgo-loader: 3.0.3
|
||||
ts-loader: 9.5.1(typescript@5.3.3)(webpack@5.90.3)
|
||||
typescript: 5.3.3
|
||||
update-browserslist-db: 1.0.13(browserslist@4.21.5)
|
||||
update-browserslist-db: 1.1.0(browserslist@4.23.3)
|
||||
webpack: 5.90.3(esbuild@0.20.1)(webpack-cli@5.1.4)
|
||||
webpack-bundle-analyzer: 4.10.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack-dev-server@4.15.1)(webpack@5.90.3)
|
||||
@ -31700,7 +31686,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server-gmail-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-jc5tLhz7YJs4Nw0dwPgOpZnZA+wMstl6Vt3BuL5BOE8iMTeUTZP1fKJXf6pGVeZAGEaYzayHM9Jmw+S45/l9MQ==, tarball: file:projects/server-gmail-resources.tgz}
|
||||
resolution: {integrity: sha512-V9M3+rquV4MPbPtmwWPdekguzA5WKJRDvU7xtpYKQSJ7J7hkLPKNah4GtSjin0opg6lTgtoAJUQaYLKheeCCbA==, tarball: file:projects/server-gmail-resources.tgz}
|
||||
id: file:projects/server-gmail-resources.tgz
|
||||
name: '@rush-temp/server-gmail-resources'
|
||||
version: 0.0.0
|
||||
@ -33412,7 +33398,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/tags-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-LuFrjyWlBTgRhkuiQvvLLtUh4kw9sRrUH7kZaJYjXeHh6J4PDfbrLl1cNLCTKXZiCP4X3GIeMz0eK0eUqzdeVg==, tarball: file:projects/tags-resources.tgz}
|
||||
resolution: {integrity: sha512-5ldBcfo7wMt0BzAf8fA0FMc2ICyqweNug+4KPSisBcFJRvqUysw+x97qpqBosCl2BI2ro/MTnUEwYpN/3m5oVQ==, tarball: file:projects/tags-resources.tgz}
|
||||
id: file:projects/tags-resources.tgz
|
||||
name: '@rush-temp/tags-resources'
|
||||
version: 0.0.0
|
||||
@ -33520,7 +33506,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/task-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-BXlz1rd2Pp3cTgW0USdUS2Ha+hDUYnwAvT9uzlZY1Nn0zOxt6rSC/Qb4e6N1n0gNxpI63ZZl+zyhTkR100yL+g==, tarball: file:projects/task-resources.tgz}
|
||||
resolution: {integrity: sha512-GxlxQIm65Qvb6rIh4tqIgtNxEhom+Q8bxQeclK4J+elxv3/YoQdQrS6pKiX+9ueY/TSz+ulPbgpNfk6y1cgSUA==, tarball: file:projects/task-resources.tgz}
|
||||
id: file:projects/task-resources.tgz
|
||||
name: '@rush-temp/task-resources'
|
||||
version: 0.0.0
|
||||
@ -34127,7 +34113,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/time-resources.tgz(@tiptap/core@2.2.4)(@tiptap/pm@2.2.4)(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-YHDZu9vfplV3yHTWsp3czuSbb59RRHGIYpYvMZKVG7mnk52y121rzcxiu1W6j8vh69xFXgxMowO6TQEwTEFfKQ==, tarball: file:projects/time-resources.tgz}
|
||||
resolution: {integrity: sha512-S0T2DEhL7CMoryh+gjH7KorQwrUvwu96LH2VUaSZTADN69oSN9XQZb05ab1zhn5MbwNoRr2aLnMDqRmhuxT1Ag==, tarball: file:projects/time-resources.tgz}
|
||||
id: file:projects/time-resources.tgz
|
||||
name: '@rush-temp/time-resources'
|
||||
version: 0.0.0
|
||||
|
@ -42,8 +42,8 @@
|
||||
"compression-webpack-plugin": "^10.0.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"fork-ts-checker-webpack-plugin": "~7.3.0",
|
||||
"update-browserslist-db": "~1.0.11",
|
||||
"browserslist": "4.21.5",
|
||||
"update-browserslist-db": "^1.1.0",
|
||||
"browserslist": "^4.23.3",
|
||||
"typescript": "^5.3.3",
|
||||
"ts-node": "^10.8.0",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
|
@ -62,8 +62,8 @@ services:
|
||||
- LAST_NAME_FIRST=true
|
||||
- ACCOUNTS_URL=http://localhost:3000
|
||||
- BRANDING_PATH=/var/cfg/branding.json
|
||||
- INIT_SCRIPT_URL=https://raw.githubusercontent.com/hcengineering/init/main/script.yaml
|
||||
- INIT_WORKSPACE=onboarding
|
||||
# - INIT_SCRIPT_URL=https://raw.githubusercontent.com/hcengineering/init/main/script.yaml
|
||||
# - INIT_WORKSPACE=onboarding
|
||||
restart: unless-stopped
|
||||
collaborator:
|
||||
image: hardcoreeng/collaborator
|
||||
|
@ -43,8 +43,8 @@
|
||||
"compression-webpack-plugin": "^10.0.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"fork-ts-checker-webpack-plugin": "~7.3.0",
|
||||
"update-browserslist-db": "~1.0.11",
|
||||
"browserslist": "4.21.5",
|
||||
"update-browserslist-db": "^1.1.0",
|
||||
"browserslist": "^4.23.3",
|
||||
"esbuild": "^0.20.0",
|
||||
"esbuild-loader": "^4.0.3",
|
||||
"typescript": "^5.3.3",
|
||||
|
@ -37,8 +37,8 @@ async function createEmployeeEmail (client: TxOperations): Promise<void> {
|
||||
).filter((it) => it.provider === contact.channelProvider.Email)
|
||||
const channelsMap = new Map(channels.map((p) => [p.attachedTo, p]))
|
||||
for (const employee of employees) {
|
||||
const acc = await client.findOne(contact.class.PersonAccount, { person: employee._id })
|
||||
if (acc === undefined) continue
|
||||
const acc = client.getModel().getAccountByPersonId(employee._id)
|
||||
if (acc.length === 0) continue
|
||||
const current = channelsMap.get(employee._id)
|
||||
if (current === undefined) {
|
||||
await client.addCollection(
|
||||
@ -49,13 +49,13 @@ async function createEmployeeEmail (client: TxOperations): Promise<void> {
|
||||
'channels',
|
||||
{
|
||||
provider: contact.channelProvider.Email,
|
||||
value: acc.email.trim()
|
||||
value: acc[0].email.trim()
|
||||
},
|
||||
undefined,
|
||||
employee.modifiedOn
|
||||
)
|
||||
} else if (current.value !== acc.email.trim()) {
|
||||
await client.update(current, { value: acc.email.trim() }, false, current.modifiedOn)
|
||||
} else if (current.value !== acc[0].email.trim()) {
|
||||
await client.update(current, { value: acc[0].email.trim() }, false, current.modifiedOn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import core from '@hcengineering/core/src/component'
|
||||
import serverActivity from '@hcengineering/server-activity'
|
||||
import serverNotification from '@hcengineering/server-notification'
|
||||
import activity from '@hcengineering/activity'
|
||||
import notification from '@hcengineering/notification'
|
||||
|
||||
export { activityServerOperation } from './migration'
|
||||
export { serverActivityId } from '@hcengineering/server-activity'
|
||||
@ -43,6 +44,9 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverActivity.trigger.ActivityMessagesHandler,
|
||||
txMatch: {
|
||||
'tx.objectClass': { $nin: [activity.class.ActivityMessage, notification.class.DocNotifyContext] }
|
||||
},
|
||||
isAsync: true
|
||||
})
|
||||
|
||||
@ -52,6 +56,10 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverActivity.trigger.ReferenceTrigger,
|
||||
txMatch: {
|
||||
'tx.objectClass': { $ne: activity.class.ActivityMessage },
|
||||
objectClass: { $nin: [notification.class.InboxNotification, notification.class.DocNotifyContext] }
|
||||
},
|
||||
isAsync: true
|
||||
})
|
||||
}
|
||||
|
@ -49,6 +49,6 @@ export function createModel (builder: Builder): void {
|
||||
})
|
||||
|
||||
builder.mixin(gmail.ids.EmailNotification, notification.class.NotificationType, serverNotification.mixin.TypeMatch, {
|
||||
func: serverGmail.function.IsIncomingMessage
|
||||
func: serverGmail.function.IsIncomingMessageTypeMatch
|
||||
})
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ export function createModel (builder: Builder): void {
|
||||
notification.class.NotificationType,
|
||||
serverNotification.mixin.TypeMatch,
|
||||
{
|
||||
func: serverNotification.function.IsUserEmployeeInFieldValue
|
||||
func: serverNotification.function.IsUserEmployeeInFieldValueTypeMatch
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -16,21 +16,22 @@
|
||||
|
||||
import { type Builder, Mixin, Model } from '@hcengineering/model'
|
||||
|
||||
import core, { type Account, type Doc, type Ref, type Tx } from '@hcengineering/core'
|
||||
import core, { type Ref } from '@hcengineering/core'
|
||||
import { TClass, TDoc } from '@hcengineering/model-core'
|
||||
import { TNotificationType } from '@hcengineering/model-notification'
|
||||
import notification, { type NotificationProvider, type NotificationType } from '@hcengineering/notification'
|
||||
import notification, { type NotificationProvider } from '@hcengineering/notification'
|
||||
import { type Resource } from '@hcengineering/platform'
|
||||
import serverCore, { type TriggerControl } from '@hcengineering/server-core'
|
||||
import serverCore from '@hcengineering/server-core'
|
||||
import serverNotification, {
|
||||
type HTMLPresenter,
|
||||
type NotificationContentProvider,
|
||||
type NotificationPresenter,
|
||||
type NotificationProviderFunc,
|
||||
type NotificationProviderResources,
|
||||
type Presenter,
|
||||
type TextPresenter,
|
||||
type TypeMatch,
|
||||
type NotificationContentProvider,
|
||||
type NotificationProviderResources,
|
||||
type NotificationProviderFunc
|
||||
type TypeMatchFunc
|
||||
} from '@hcengineering/server-notification'
|
||||
|
||||
export { serverNotificationId } from '@hcengineering/server-notification'
|
||||
@ -52,9 +53,7 @@ export class TNotificationPresenter extends TClass implements NotificationPresen
|
||||
|
||||
@Mixin(serverNotification.mixin.TypeMatch, notification.class.NotificationType)
|
||||
export class TTypeMatch extends TNotificationType implements TypeMatch {
|
||||
func!: Resource<
|
||||
(tx: Tx, doc: Doc, user: Ref<Account>, type: NotificationType, control: TriggerControl) => Promise<boolean>
|
||||
>
|
||||
func!: TypeMatchFunc
|
||||
}
|
||||
|
||||
@Model(serverNotification.class.NotificationProviderResources, core.class.Doc)
|
||||
|
@ -93,7 +93,7 @@ export function createModel (builder: Builder): void {
|
||||
notification.class.NotificationType,
|
||||
serverNotification.mixin.TypeMatch,
|
||||
{
|
||||
func: serverNotification.function.IsUserEmployeeInFieldValue
|
||||
func: serverNotification.function.IsUserEmployeeInFieldValueTypeMatch
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ export function createModel (builder: Builder): void {
|
||||
notification.class.NotificationType,
|
||||
serverNotification.mixin.TypeMatch,
|
||||
{
|
||||
func: serverTelegram.function.IsIncomingMessage
|
||||
func: serverTelegram.function.IsIncomingMessageTypeMatch
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -77,7 +77,7 @@ export function createModel (builder: Builder): void {
|
||||
notification.class.NotificationType,
|
||||
serverNotification.mixin.TypeMatch,
|
||||
{
|
||||
func: serverNotification.function.IsUserEmployeeInFieldValue
|
||||
func: serverNotification.function.IsUserEmployeeInFieldValueTypeMatch
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ export function genMinModel (): TxCUD<Doc>[] {
|
||||
txes.push(
|
||||
createClass(core.class.Blob, {
|
||||
label: 'Blob' as IntlString,
|
||||
extends: core.class.Blob,
|
||||
extends: core.class.Doc,
|
||||
kind: ClassifierKind.CLASS
|
||||
})
|
||||
)
|
||||
|
@ -440,6 +440,8 @@ export interface Permission extends Doc {
|
||||
export interface Account extends Doc {
|
||||
email: string
|
||||
role: AccountRole
|
||||
|
||||
person?: Ref<Doc>
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,7 +30,7 @@ export class Hierarchy {
|
||||
private readonly attributes = new Map<Ref<Classifier>, Map<string, AnyAttribute>>()
|
||||
private readonly attributesById = new Map<Ref<AnyAttribute>, AnyAttribute>()
|
||||
private readonly descendants = new Map<Ref<Classifier>, Ref<Classifier>[]>()
|
||||
private readonly ancestors = new Map<Ref<Classifier>, Ref<Classifier>[]>()
|
||||
private readonly ancestors = new Map<Ref<Classifier>, { ordered: Ref<Classifier>[], set: Set<Ref<Classifier>> }>()
|
||||
private readonly proxies = new Map<Ref<Mixin<Doc>>, ProxyHandler<Doc>>()
|
||||
|
||||
private readonly classifierProperties = new Map<Ref<Classifier>, Record<string, any>>()
|
||||
@ -166,7 +166,7 @@ export class Hierarchy {
|
||||
if (result === undefined) {
|
||||
throw new Error('ancestors not found: ' + _class)
|
||||
}
|
||||
return result
|
||||
return result.ordered
|
||||
}
|
||||
|
||||
getClass<T extends Obj = Obj>(_class: Ref<Class<T>>): Class<T> {
|
||||
@ -301,17 +301,7 @@ export class Hierarchy {
|
||||
* It will iterate over parents.
|
||||
*/
|
||||
isDerived<T extends Obj>(_class: Ref<Class<T>>, from: Ref<Class<T>>): boolean {
|
||||
let cl: Ref<Class<T>> | undefined = _class
|
||||
while (cl !== undefined) {
|
||||
if (cl === from) return true
|
||||
|
||||
const cll = this.classifiers.get(cl)
|
||||
if (cll === undefined || this.isInterface(cll)) {
|
||||
return false
|
||||
}
|
||||
cl = (cll as Class<T>).extends
|
||||
}
|
||||
return false
|
||||
return this.ancestors.get(_class)?.set?.has(from) ?? false
|
||||
}
|
||||
|
||||
/**
|
||||
@ -398,15 +388,19 @@ export class Hierarchy {
|
||||
const list = this.ancestors.get(_class)
|
||||
if (list === undefined) {
|
||||
if (add) {
|
||||
this.ancestors.set(_class, [classifier])
|
||||
this.ancestors.set(_class, { ordered: [classifier], set: new Set([classifier]) })
|
||||
}
|
||||
} else {
|
||||
if (add) {
|
||||
addIf(list, classifier)
|
||||
if (!list.set.has(classifier)) {
|
||||
list.ordered.push(classifier)
|
||||
list.set.add(classifier)
|
||||
}
|
||||
} else {
|
||||
const pos = list.indexOf(classifier)
|
||||
const pos = list.ordered.indexOf(classifier)
|
||||
if (pos !== -1) {
|
||||
list.splice(pos, 1)
|
||||
list.ordered.splice(pos, 1)
|
||||
list.set.delete(classifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
import { PlatformError, Severity, Status } from '@hcengineering/platform'
|
||||
import { Lookup, MeasureContext, ReverseLookups, getObjectValue } from '.'
|
||||
import type { AttachedDoc, Class, Doc, Ref } from './classes'
|
||||
import type { Account, AttachedDoc, Class, Doc, Ref } from './classes'
|
||||
import core from './component'
|
||||
import { Hierarchy } from './hierarchy'
|
||||
import { checkMixinKey, matchQuery, resultSort } from './query'
|
||||
@ -31,6 +31,8 @@ export abstract class MemDb extends TxProcessor implements Storage {
|
||||
private readonly objectsByClass = new Map<Ref<Class<Doc>>, Map<Ref<Doc>, Doc>>()
|
||||
private readonly objectById = new Map<Ref<Doc>, Doc>()
|
||||
|
||||
private readonly accountByPersonId = new Map<Ref<Doc>, Account[]>()
|
||||
|
||||
constructor (protected readonly hierarchy: Hierarchy) {
|
||||
super()
|
||||
}
|
||||
@ -75,6 +77,10 @@ export abstract class MemDb extends TxProcessor implements Storage {
|
||||
return doc as T
|
||||
}
|
||||
|
||||
getAccountByPersonId (ref: Ref<Doc>): Account[] {
|
||||
return this.accountByPersonId.get(ref) ?? []
|
||||
}
|
||||
|
||||
findObject<T extends Doc>(_id: Ref<T>): T | undefined {
|
||||
const doc = this.objectById.get(_id)
|
||||
return doc as T
|
||||
@ -214,6 +220,12 @@ export abstract class MemDb extends TxProcessor implements Storage {
|
||||
const arr = this.getObjectsByClass(_class)
|
||||
arr.set(doc._id, doc)
|
||||
})
|
||||
if (this.hierarchy.isDerived(doc._class, core.class.Account)) {
|
||||
const account = doc as Account
|
||||
if (account.person !== undefined) {
|
||||
this.accountByPersonId.set(account.person, [...(this.accountByPersonId.get(account.person) ?? []), account])
|
||||
}
|
||||
}
|
||||
this.objectById.set(doc._id, doc)
|
||||
}
|
||||
|
||||
@ -226,6 +238,37 @@ export abstract class MemDb extends TxProcessor implements Storage {
|
||||
this.hierarchy.getAncestors(doc._class).forEach((_class) => {
|
||||
this.cleanObjectByClass(_class, _id)
|
||||
})
|
||||
if (this.hierarchy.isDerived(doc._class, core.class.Account)) {
|
||||
const account = doc as Account
|
||||
if (account.person !== undefined) {
|
||||
const acc = this.accountByPersonId.get(account.person) ?? []
|
||||
this.accountByPersonId.set(
|
||||
account.person,
|
||||
acc.filter((it) => it._id !== _id)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateDoc (_id: Ref<Doc>, doc: Doc, update: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>): void {
|
||||
if (
|
||||
this.hierarchy.isDerived(doc._class, core.class.Account) &&
|
||||
update._class === core.class.TxUpdateDoc &&
|
||||
(update as TxUpdateDoc<Account>).operations.person !== undefined
|
||||
) {
|
||||
const account = doc as Account
|
||||
if (account.person !== undefined) {
|
||||
const acc = this.accountByPersonId.get(account.person) ?? []
|
||||
this.accountByPersonId.set(
|
||||
account.person,
|
||||
acc.filter((it) => it._id !== _id)
|
||||
)
|
||||
}
|
||||
const newPerson = (update as TxUpdateDoc<Account>).operations.person
|
||||
if (newPerson !== undefined) {
|
||||
this.accountByPersonId.set(newPerson, [...(this.accountByPersonId.get(newPerson) ?? []), account])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,6 +340,7 @@ export class ModelDb extends MemDb {
|
||||
const cud = tx as TxUpdateDoc<Doc>
|
||||
const doc = this.findObject(cud.objectId)
|
||||
if (doc !== undefined) {
|
||||
this.updateDoc(cud.objectId, doc, cud)
|
||||
TxProcessor.updateDoc2Doc(doc, cud)
|
||||
} else {
|
||||
ctx.error('no document found, failed to apply model transaction, skipping', {
|
||||
@ -320,9 +364,10 @@ export class ModelDb extends MemDb {
|
||||
break
|
||||
case core.class.TxMixin: {
|
||||
const mix = tx as TxMixin<Doc, Doc>
|
||||
const obj = this.findObject(mix.objectId)
|
||||
if (obj !== undefined) {
|
||||
TxProcessor.updateMixin4Doc(obj, mix)
|
||||
const doc = this.findObject(mix.objectId)
|
||||
if (doc !== undefined) {
|
||||
this.updateDoc(mix.objectId, doc, mix)
|
||||
TxProcessor.updateMixin4Doc(doc, mix)
|
||||
} else {
|
||||
ctx.error('no document found, failed to apply model transaction, skipping', {
|
||||
_id: tx._id,
|
||||
@ -339,6 +384,7 @@ export class ModelDb extends MemDb {
|
||||
protected async txUpdateDoc (tx: TxUpdateDoc<Doc>): Promise<TxResult> {
|
||||
try {
|
||||
const doc = this.getObject(tx.objectId) as any
|
||||
this.updateDoc(tx.objectId, doc, tx)
|
||||
TxProcessor.updateDoc2Doc(doc, tx)
|
||||
return tx.retrieve === true ? { object: doc } : {}
|
||||
} catch (err: any) {}
|
||||
@ -354,8 +400,9 @@ export class ModelDb extends MemDb {
|
||||
|
||||
// TODO: process ancessor mixins
|
||||
protected async txMixin (tx: TxMixin<Doc, Doc>): Promise<TxResult> {
|
||||
const obj = this.getObject(tx.objectId) as any
|
||||
TxProcessor.updateMixin4Doc(obj, tx)
|
||||
const doc = this.getObject(tx.objectId) as any
|
||||
this.updateDoc(tx.objectId, doc, tx)
|
||||
TxProcessor.updateMixin4Doc(doc, tx)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,10 @@ import { Editor, Extensions, getSchema } from '@tiptap/core'
|
||||
import { generateHTML, generateJSON } from '@tiptap/html'
|
||||
import { Node as ProseMirrorNode, Schema } from '@tiptap/pm/model'
|
||||
|
||||
import { defaultExtensions } from '../extensions'
|
||||
import { MarkupMark, MarkupNode, MarkupNodeType, emptyMarkupNode } from './model'
|
||||
import { nodeDoc, nodeParagraph, nodeText } from './dsl'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { defaultExtensions } from '../extensions'
|
||||
import { nodeDoc, nodeParagraph, nodeText } from './dsl'
|
||||
import { MarkupMark, MarkupNode, MarkupNodeType, emptyMarkupNode } from './model'
|
||||
|
||||
/** @public */
|
||||
export const EmptyMarkup: Markup = jsonToMarkup(emptyMarkupNode())
|
||||
@ -226,9 +226,11 @@ export function pmNodeToHTML (node: ProseMirrorNode, extensions?: Extensions): s
|
||||
const ELLIPSIS_CHAR = '…'
|
||||
const WHITESPACE = ' '
|
||||
|
||||
const defaultSchema = getSchema(defaultExtensions)
|
||||
|
||||
/** @public */
|
||||
export function stripTags (markup: Markup, textLimit = 0, extensions: Extensions | undefined = undefined): string {
|
||||
const schema = getSchema(extensions ?? defaultExtensions)
|
||||
const schema = extensions === undefined ? defaultSchema : getSchema(extensions)
|
||||
const parsed = markupToPmNode(markup, schema)
|
||||
|
||||
const textParts: string[] = []
|
||||
|
@ -56,13 +56,15 @@ export function yDocContentToNode (
|
||||
return yDocToNode(ydoc, field, schema, extensions)
|
||||
}
|
||||
|
||||
const defaultSchema = getSchema(defaultExtensions)
|
||||
|
||||
/**
|
||||
* Get ProseMirror node from Y.Doc
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function yDocToNode (ydoc: YDoc, field?: string, schema?: Schema, extensions?: Extensions): Node {
|
||||
schema ??= getSchema(extensions ?? defaultExtensions)
|
||||
schema ??= extensions === undefined ? defaultSchema : getSchema(extensions ?? defaultExtensions)
|
||||
|
||||
try {
|
||||
const body = yDocToProsemirrorJSON(ydoc, field)
|
||||
@ -79,7 +81,7 @@ export function yDocToNode (ydoc: YDoc, field?: string, schema?: Schema, extensi
|
||||
* @public
|
||||
*/
|
||||
export function yDocContentToNodes (content: ArrayBuffer, schema?: Schema, extensions?: Extensions): Node[] {
|
||||
schema ??= getSchema(extensions ?? defaultExtensions)
|
||||
schema ??= extensions === undefined ? defaultSchema : getSchema(extensions ?? defaultExtensions)
|
||||
|
||||
const nodes: Node[] = []
|
||||
|
||||
@ -112,7 +114,7 @@ export function updateYDocContent (
|
||||
schema?: Schema,
|
||||
extensions?: Extensions
|
||||
): YDoc | undefined {
|
||||
schema ??= getSchema(extensions ?? defaultExtensions)
|
||||
schema ??= extensions === undefined ? defaultSchema : getSchema(extensions ?? defaultExtensions)
|
||||
|
||||
try {
|
||||
const ydoc = new YDoc()
|
||||
@ -140,7 +142,7 @@ export function updateYDocContent (
|
||||
* @public
|
||||
*/
|
||||
export function YDocFromContent (content: MarkupNode, field: string, schema?: Schema, extensions?: Extensions): YDoc {
|
||||
schema ??= getSchema(extensions ?? defaultExtensions)
|
||||
schema ??= extensions === undefined ? defaultSchema : getSchema(extensions ?? defaultExtensions)
|
||||
|
||||
const res = new YDoc({ gc: false })
|
||||
|
||||
|
@ -268,8 +268,8 @@ async function kickEmployee (doc: Person): Promise<void> {
|
||||
const client = getClient()
|
||||
|
||||
const employee = client.getHierarchy().as(doc, contact.mixin.Employee)
|
||||
const email = await client.findOne(contact.class.PersonAccount, { person: doc._id })
|
||||
if (email === undefined) {
|
||||
const accounts = client.getModel().getAccountByPersonId(doc._id)
|
||||
if (accounts.length === 0) {
|
||||
await client.update(employee, { active: false })
|
||||
} else {
|
||||
showPopup(
|
||||
@ -282,9 +282,12 @@ async function kickEmployee (doc: Person): Promise<void> {
|
||||
(res?: boolean) => {
|
||||
if (res === true) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
getResource(login.function.LeaveWorkspace).then(async (f) => {
|
||||
await f(email.email)
|
||||
})
|
||||
const p = getResource(login.function.LeaveWorkspace)
|
||||
for (const i of accounts) {
|
||||
void p.then(async (f) => {
|
||||
await f(i.email)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -320,7 +320,7 @@ function fillStores (): void {
|
||||
const accountPersonQuery = createQuery(true)
|
||||
|
||||
const query = createQuery(true)
|
||||
query.query(contact.mixin.Employee, { [contact.mixin.Employee + '.active']: { $in: [true, false] } }, (res) => {
|
||||
query.query(contact.mixin.Employee, { active: { $in: [true, false] } }, (res) => {
|
||||
employeesStore.set(res)
|
||||
employeeByIdStore.set(toIdMap(res))
|
||||
})
|
||||
|
@ -13,7 +13,9 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import activity, { ActivityMessage, ActivityReference, UserMentionInfo } from '@hcengineering/activity'
|
||||
import { loadCollaborativeDoc, yDocToBuffer } from '@hcengineering/collaboration'
|
||||
import contact, { Employee, Person, PersonAccount } from '@hcengineering/contact'
|
||||
import core, {
|
||||
Account,
|
||||
AttachedDoc,
|
||||
@ -34,41 +36,30 @@ import core, {
|
||||
TxProcessor,
|
||||
TxRemoveDoc,
|
||||
TxUpdateDoc,
|
||||
Type
|
||||
Type,
|
||||
type MeasureContext
|
||||
} from '@hcengineering/core'
|
||||
import notification, { CommonInboxNotification, MentionInboxNotification } from '@hcengineering/notification'
|
||||
import { StorageAdapter, TriggerControl } from '@hcengineering/server-core'
|
||||
import {
|
||||
applyNotificationProviders,
|
||||
getCommonNotificationTxes,
|
||||
getNotificationContent,
|
||||
getNotificationProviderControl,
|
||||
getPushCollaboratorTx,
|
||||
isShouldNotifyTx,
|
||||
NotifyResult,
|
||||
shouldNotifyCommon,
|
||||
toReceiverInfo,
|
||||
type NotificationProviderControl
|
||||
} from '@hcengineering/server-notification-resources'
|
||||
import {
|
||||
areEqualJson,
|
||||
extractReferences,
|
||||
markupToPmNode,
|
||||
pmNodeToMarkup,
|
||||
yDocContentToNodes,
|
||||
areEqualJson
|
||||
yDocContentToNodes
|
||||
} from '@hcengineering/text'
|
||||
import { StorageAdapter, TriggerControl } from '@hcengineering/server-core'
|
||||
import activity, { ActivityMessage, ActivityReference, UserMentionInfo } from '@hcengineering/activity'
|
||||
import contact, { Employee, Person, PersonAccount } from '@hcengineering/contact'
|
||||
import {
|
||||
getCommonNotificationTxes,
|
||||
getPushCollaboratorTx,
|
||||
shouldNotifyCommon,
|
||||
isShouldNotifyTx,
|
||||
NotifyResult,
|
||||
applyNotificationProviders,
|
||||
getNotificationContent,
|
||||
toReceiverInfo
|
||||
} from '@hcengineering/server-notification-resources'
|
||||
|
||||
async function getPersonAccount (person: Ref<Person>, control: TriggerControl): Promise<PersonAccount | undefined> {
|
||||
return (
|
||||
await control.modelDb.findAll(
|
||||
contact.class.PersonAccount,
|
||||
{
|
||||
person
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
}
|
||||
|
||||
export function isDocMentioned (doc: Ref<Doc>, content: string | Buffer): boolean {
|
||||
const references = []
|
||||
@ -93,20 +84,22 @@ export function isDocMentioned (doc: Ref<Doc>, content: string | Buffer): boolea
|
||||
}
|
||||
|
||||
export async function getPersonNotificationTxes (
|
||||
ctx: MeasureContext,
|
||||
reference: Data<ActivityReference>,
|
||||
control: TriggerControl,
|
||||
senderId: Ref<Account>,
|
||||
space: Ref<Space>,
|
||||
originTx: TxCUD<Doc>
|
||||
originTx: TxCUD<Doc>,
|
||||
notificationControl: NotificationProviderControl
|
||||
): Promise<Tx[]> {
|
||||
const receiverPersonId = reference.attachedTo as Ref<Person>
|
||||
const receiver = await getPersonAccount(receiverPersonId, control)
|
||||
const receiver = control.modelDb.getAccountByPersonId(receiverPersonId) as PersonAccount[]
|
||||
|
||||
if (receiver === undefined) {
|
||||
if (receiver.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (receiver._id === senderId) {
|
||||
if (receiver.some((it) => it._id === senderId)) {
|
||||
return []
|
||||
}
|
||||
|
||||
@ -120,14 +113,21 @@ export async function getPersonNotificationTxes (
|
||||
const doc = (await control.findAll(reference.srcDocClass, { _id: reference.srcDocId }))[0]
|
||||
|
||||
const receiverPerson = (
|
||||
await control.findAll(contact.mixin.Employee, { _id: receiver.person as Ref<Employee>, active: true }, { limit: 1 })
|
||||
await control.findAll(
|
||||
contact.mixin.Employee,
|
||||
{ _id: receiverPersonId as Ref<Employee>, active: true },
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
if (receiverPerson === undefined) return res
|
||||
|
||||
const receiverSpace = (await control.findAll(contact.class.PersonSpace, { person: receiver.person }, { limit: 1 }))[0]
|
||||
const receiverSpace = (
|
||||
await control.findAll(contact.class.PersonSpace, { person: receiverPersonId }, { limit: 1 })
|
||||
)[0]
|
||||
if (receiverSpace === undefined) return res
|
||||
|
||||
const collaboratorsTx = await getCollaboratorsTxes(reference, control, receiver, doc)
|
||||
// TODO: Do we need for all or just one?
|
||||
const collaboratorsTx = await getCollaboratorsTxes(reference, control, receiver[0], doc)
|
||||
|
||||
res.push(...collaboratorsTx)
|
||||
|
||||
@ -159,13 +159,13 @@ export async function getPersonNotificationTxes (
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: Select a proper reciever
|
||||
const data: Omit<Data<MentionInboxNotification>, 'docNotifyContext'> = {
|
||||
header: activity.string.MentionedYouIn,
|
||||
messageHtml: reference.message,
|
||||
mentionedIn: reference.attachedDocId ?? reference.srcDocId,
|
||||
mentionedInClass: reference.attachedDocClass ?? reference.srcDocClass,
|
||||
user: receiver._id,
|
||||
user: receiver[0]._id,
|
||||
isViewed: false,
|
||||
archived: false
|
||||
}
|
||||
@ -180,8 +180,8 @@ export async function getPersonNotificationTxes (
|
||||
: undefined
|
||||
|
||||
const receiverInfo = toReceiverInfo(control.hierarchy, {
|
||||
_id: receiver._id,
|
||||
account: receiver,
|
||||
_id: receiver[0]._id,
|
||||
account: receiver[0],
|
||||
person: receiverPerson,
|
||||
space: receiverSpace._id
|
||||
})
|
||||
@ -193,8 +193,20 @@ export async function getPersonNotificationTxes (
|
||||
person: senderPerson
|
||||
}
|
||||
|
||||
const notifyResult = await shouldNotifyCommon(control, receiver._id, notification.ids.MentionCommonNotificationType)
|
||||
const messageNotifyResult = await getMessageNotifyResult(reference, receiver, control, originTx, doc)
|
||||
const notifyResult = await shouldNotifyCommon(
|
||||
control,
|
||||
receiver.map((it) => it._id),
|
||||
notification.ids.MentionCommonNotificationType,
|
||||
notificationControl
|
||||
)
|
||||
const messageNotifyResult = await getMessageNotifyResult(
|
||||
reference,
|
||||
receiver,
|
||||
control,
|
||||
originTx,
|
||||
doc,
|
||||
notificationControl
|
||||
)
|
||||
|
||||
for (const [provider] of messageNotifyResult.entries()) {
|
||||
if (notifyResult.has(provider)) {
|
||||
@ -204,6 +216,7 @@ export async function getPersonNotificationTxes (
|
||||
|
||||
if (notifyResult.has(notification.providers.InboxNotificationProvider)) {
|
||||
const txes = await getCommonNotificationTxes(
|
||||
ctx,
|
||||
control,
|
||||
doc,
|
||||
data,
|
||||
@ -221,7 +234,7 @@ export async function getPersonNotificationTxes (
|
||||
const context = (
|
||||
await control.findAll(
|
||||
notification.class.DocNotifyContext,
|
||||
{ objectId: reference.srcDocId, user: receiver._id },
|
||||
{ objectId: reference.srcDocId, user: { $in: receiver.map((it) => it._id) } },
|
||||
{ projection: { _id: 1 } }
|
||||
)
|
||||
)[0]
|
||||
@ -255,21 +268,24 @@ export async function getPersonNotificationTxes (
|
||||
}
|
||||
|
||||
async function checkSpace (
|
||||
user: PersonAccount,
|
||||
users: PersonAccount[],
|
||||
spaceId: Ref<Space>,
|
||||
control: TriggerControl,
|
||||
res: Tx[]
|
||||
): Promise<boolean> {
|
||||
const space = (await control.findAll<Space>(core.class.Space, { _id: spaceId }, { limit: 1 }))[0]
|
||||
const isMember = space.members.includes(user._id)
|
||||
const toAdd = users.filter((user) => !space.members.includes(user._id))
|
||||
const isMember = toAdd.length === 0
|
||||
if (space.private) {
|
||||
return isMember
|
||||
}
|
||||
|
||||
if (!isMember) {
|
||||
res.push(
|
||||
control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, { $push: { members: user._id } })
|
||||
)
|
||||
for (const user of toAdd) {
|
||||
res.push(
|
||||
control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, { $push: { members: user._id } })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
@ -327,10 +343,11 @@ async function getCollaboratorsTxes (
|
||||
|
||||
async function getMessageNotifyResult (
|
||||
reference: Data<ActivityReference>,
|
||||
account: PersonAccount,
|
||||
account: PersonAccount[],
|
||||
control: TriggerControl,
|
||||
originTx: TxCUD<Doc>,
|
||||
doc: Doc
|
||||
doc: Doc,
|
||||
notificationControl: NotificationProviderControl
|
||||
): Promise<NotifyResult> {
|
||||
const { hierarchy } = control
|
||||
const tx = TxProcessor.extractTx(originTx) as TxCUD<Doc>
|
||||
@ -345,7 +362,7 @@ async function getMessageNotifyResult (
|
||||
|
||||
const mixin = control.hierarchy.as(doc, notification.mixin.Collaborators)
|
||||
|
||||
if (mixin === undefined || !mixin.collaborators.includes(account._id)) {
|
||||
if (mixin === undefined || !account.some((account) => mixin.collaborators.includes(account._id))) {
|
||||
return new Map()
|
||||
}
|
||||
|
||||
@ -353,7 +370,7 @@ async function getMessageNotifyResult (
|
||||
return new Map()
|
||||
}
|
||||
|
||||
return await isShouldNotifyTx(control, tx, originTx, doc, account, false, false, undefined)
|
||||
return await isShouldNotifyTx(control, tx, originTx, doc, account, false, false, notificationControl, undefined)
|
||||
}
|
||||
|
||||
function isMarkupType (type: Ref<Class<Type<any>>>): boolean {
|
||||
@ -365,6 +382,7 @@ function isCollaborativeType (type: Ref<Class<Type<any>>>): boolean {
|
||||
}
|
||||
|
||||
async function getCreateReferencesTxes (
|
||||
ctx: MeasureContext,
|
||||
control: TriggerControl,
|
||||
storage: StorageAdapter,
|
||||
txFactory: TxFactory,
|
||||
@ -410,10 +428,11 @@ async function getCreateReferencesTxes (
|
||||
? (srcDocId as Ref<Space>)
|
||||
: srcDocSpace
|
||||
|
||||
return await getReferencesTxes(control, txFactory, refs, refSpace, [], [], originTx)
|
||||
return await getReferencesTxes(ctx, control, txFactory, refs, refSpace, [], [], originTx)
|
||||
}
|
||||
|
||||
async function getUpdateReferencesTxes (
|
||||
ctx: MeasureContext,
|
||||
control: TriggerControl,
|
||||
storage: StorageAdapter,
|
||||
txFactory: TxFactory,
|
||||
@ -474,7 +493,7 @@ async function getUpdateReferencesTxes (
|
||||
? (srcDocId as Ref<Space>)
|
||||
: srcDocSpace
|
||||
|
||||
return await getReferencesTxes(control, txFactory, references, refSpace, current, userMentions, originTx)
|
||||
return await getReferencesTxes(ctx, control, txFactory, references, refSpace, current, userMentions, originTx)
|
||||
}
|
||||
|
||||
return []
|
||||
@ -519,14 +538,16 @@ export function getReferencesData (
|
||||
}
|
||||
|
||||
async function createReferenceTxes (
|
||||
ctx: MeasureContext,
|
||||
control: TriggerControl,
|
||||
txFactory: TxFactory,
|
||||
ref: Data<ActivityReference>,
|
||||
space: Ref<Space>,
|
||||
originTx: TxCUD<Doc>
|
||||
originTx: TxCUD<Doc>,
|
||||
notificationControl: NotificationProviderControl
|
||||
): Promise<Tx[]> {
|
||||
if (control.hierarchy.isDerived(ref.attachedToClass, contact.class.Person)) {
|
||||
return await getPersonNotificationTxes(ref, control, txFactory.account, space, originTx)
|
||||
return await getPersonNotificationTxes(ctx, ref, control, txFactory.account, space, originTx, notificationControl)
|
||||
}
|
||||
|
||||
const refTx = control.txFactory.createTxCreateDoc(activity.class.ActivityReference, space, ref)
|
||||
@ -536,6 +557,7 @@ async function createReferenceTxes (
|
||||
}
|
||||
|
||||
async function getReferencesTxes (
|
||||
ctx: MeasureContext,
|
||||
control: TriggerControl,
|
||||
txFactory: TxFactory,
|
||||
references: Data<ActivityReference>[],
|
||||
@ -568,6 +590,8 @@ async function getReferencesTxes (
|
||||
}
|
||||
}
|
||||
|
||||
const notificationControl = await getNotificationProviderControl(ctx, control)
|
||||
|
||||
for (const mention of mentions) {
|
||||
const refIndex = references.findIndex(
|
||||
(r) => mention.user === r.attachedTo && mention.attachedTo === r.attachedDocId
|
||||
@ -588,7 +612,7 @@ async function getReferencesTxes (
|
||||
|
||||
// Add missing references
|
||||
for (const ref of references) {
|
||||
txes.push(...(await createReferenceTxes(control, txFactory, ref, space, originTx)))
|
||||
txes.push(...(await createReferenceTxes(ctx, control, txFactory, ref, space, originTx, notificationControl)))
|
||||
}
|
||||
|
||||
return txes
|
||||
@ -659,6 +683,7 @@ async function ActivityReferenceCreate (tx: TxCUD<Doc>, etx: TxCUD<Doc>, control
|
||||
const targetTx = guessReferenceTx(control.hierarchy, tx)
|
||||
|
||||
const txes: Tx[] = await getCreateReferencesTxes(
|
||||
control.ctx,
|
||||
control,
|
||||
control.storageAdapter,
|
||||
txFactory,
|
||||
@ -706,6 +731,7 @@ async function ActivityReferenceUpdate (tx: TxCUD<Doc>, etx: TxCUD<Doc>, control
|
||||
const targetTx = guessReferenceTx(control.hierarchy, tx)
|
||||
|
||||
const txes: Tx[] = await getUpdateReferencesTxes(
|
||||
control.ctx,
|
||||
control,
|
||||
control.storageAdapter,
|
||||
txFactory,
|
||||
|
@ -22,7 +22,7 @@ import chunter, {
|
||||
ChunterSpace,
|
||||
ThreadMessage
|
||||
} from '@hcengineering/chunter'
|
||||
import contact, { Person, PersonAccount } from '@hcengineering/contact'
|
||||
import { Person, PersonAccount } from '@hcengineering/contact'
|
||||
import core, {
|
||||
Account,
|
||||
AttachedDoc,
|
||||
@ -56,7 +56,7 @@ import {
|
||||
import { markupToHTML, markupToText, stripTags } from '@hcengineering/text'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
|
||||
import { NOTIFICATION_BODY_SIZE } from '@hcengineering/server-notification'
|
||||
import { getPersonAccountById, NOTIFICATION_BODY_SIZE } from '@hcengineering/server-notification'
|
||||
import { encodeObjectURI } from '@hcengineering/view'
|
||||
|
||||
const updateChatInfoDelay = 12 * 60 * 60 * 1000 // 12 hours
|
||||
@ -121,13 +121,8 @@ export async function CommentRemove (
|
||||
}
|
||||
|
||||
async function OnThreadMessageCreated (originTx: TxCUD<Doc>, control: TriggerControl): Promise<Tx[]> {
|
||||
const hierarchy = control.hierarchy
|
||||
const tx = TxProcessor.extractTx(originTx) as TxCreateDoc<ThreadMessage>
|
||||
|
||||
if (tx._class !== core.class.TxCreateDoc || !hierarchy.isDerived(tx.objectClass, chunter.class.ThreadMessage)) {
|
||||
return []
|
||||
}
|
||||
|
||||
const threadMessage = TxProcessor.createDoc2Doc(tx)
|
||||
const message = (await control.findAll(activity.class.ActivityMessage, { _id: threadMessage.attachedTo }))[0]
|
||||
|
||||
@ -166,13 +161,6 @@ async function OnChatMessageCreated (tx: TxCUD<Doc>, control: TriggerControl): P
|
||||
const hierarchy = control.hierarchy
|
||||
const actualTx = TxProcessor.extractTx(tx) as TxCreateDoc<ChatMessage>
|
||||
|
||||
if (
|
||||
actualTx._class !== core.class.TxCreateDoc ||
|
||||
!hierarchy.isDerived(actualTx.objectClass, chunter.class.ChatMessage)
|
||||
) {
|
||||
return []
|
||||
}
|
||||
|
||||
const message = TxProcessor.createDoc2Doc(actualTx)
|
||||
const mixin = hierarchy.classHierarchyMixin(message.attachedToClass, notification.mixin.ClassCollaborators)
|
||||
|
||||
@ -244,13 +232,8 @@ function joinChannel (control: TriggerControl, channel: Channel, user: Ref<Accou
|
||||
}
|
||||
|
||||
async function OnThreadMessageDeleted (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const hierarchy = control.hierarchy
|
||||
const removeTx = TxProcessor.extractTx(tx) as TxRemoveDoc<ThreadMessage>
|
||||
|
||||
if (!hierarchy.isDerived(removeTx.objectClass, chunter.class.ThreadMessage)) {
|
||||
return []
|
||||
}
|
||||
|
||||
const message = control.removedMap.get(removeTx.objectId) as ThreadMessage
|
||||
|
||||
if (message === undefined) {
|
||||
@ -286,15 +269,40 @@ async function OnThreadMessageDeleted (tx: Tx, control: TriggerControl): Promise
|
||||
*/
|
||||
export async function ChunterTrigger (tx: TxCUD<Doc>, control: TriggerControl): Promise<Tx[]> {
|
||||
const res: Tx[] = []
|
||||
res.push(
|
||||
...(await control.ctx.with('OnThreadMessageCreated', {}, async (ctx) => await OnThreadMessageCreated(tx, control)))
|
||||
)
|
||||
res.push(
|
||||
...(await control.ctx.with('OnThreadMessageDeleted', {}, async (ctx) => await OnThreadMessageDeleted(tx, control)))
|
||||
)
|
||||
res.push(
|
||||
...(await control.ctx.with('OnChatMessageCreated', {}, async (ctx) => await OnChatMessageCreated(tx, control)))
|
||||
)
|
||||
const actualTx = TxProcessor.extractTx(tx) as TxCreateDoc<ChatMessage>
|
||||
|
||||
if (
|
||||
actualTx._class === core.class.TxCreateDoc &&
|
||||
control.hierarchy.isDerived(actualTx.objectClass, chunter.class.ThreadMessage)
|
||||
) {
|
||||
res.push(
|
||||
...(await control.ctx.with(
|
||||
'OnThreadMessageCreated',
|
||||
{},
|
||||
async (ctx) => await OnThreadMessageCreated(tx, control)
|
||||
))
|
||||
)
|
||||
}
|
||||
if (
|
||||
actualTx._class === core.class.TxRemoveDoc &&
|
||||
control.hierarchy.isDerived(actualTx.objectClass, chunter.class.ThreadMessage)
|
||||
) {
|
||||
res.push(
|
||||
...(await control.ctx.with(
|
||||
'OnThreadMessageDeleted',
|
||||
{},
|
||||
async (ctx) => await OnThreadMessageDeleted(tx, control)
|
||||
))
|
||||
)
|
||||
}
|
||||
if (
|
||||
actualTx._class === core.class.TxCreateDoc &&
|
||||
control.hierarchy.isDerived(actualTx.objectClass, chunter.class.ChatMessage)
|
||||
) {
|
||||
res.push(
|
||||
...(await control.ctx.with('OnChatMessageCreated', {}, async (ctx) => await OnChatMessageCreated(tx, control)))
|
||||
)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@ -455,7 +463,7 @@ async function hideOldChannels (
|
||||
}
|
||||
|
||||
export async function updateChatInfo (control: TriggerControl, status: UserStatus, date: Timestamp): Promise<void> {
|
||||
const account = await control.modelDb.findOne(contact.class.PersonAccount, { _id: status.user as Ref<PersonAccount> })
|
||||
const account = getPersonAccountById(status.user as Ref<PersonAccount>, control)
|
||||
if (account === undefined) return
|
||||
|
||||
const update = (await control.findAll(chunter.class.ChatInfo, { user: account.person })).shift()
|
||||
@ -559,14 +567,14 @@ async function OnContextUpdate (tx: TxUpdateDoc<DocNotifyContext>, control: Trig
|
||||
return []
|
||||
}
|
||||
|
||||
async function JoinChannelTypeMatch (originTx: Tx, _: Doc, user: Ref<Account>): Promise<boolean> {
|
||||
if (originTx.modifiedBy === user) return false
|
||||
function JoinChannelTypeMatch (originTx: Tx, _: Doc, user: Ref<Account>[]): boolean {
|
||||
if (user.some((it) => originTx.modifiedBy === it)) return false
|
||||
if (originTx._class !== core.class.TxUpdateDoc) return false
|
||||
|
||||
const tx = originTx as TxUpdateDoc<Channel>
|
||||
const added = combineAttributes([tx.operations], 'members', '$push', '$each')
|
||||
|
||||
return added.includes(user)
|
||||
return user.some((it) => added.includes(it))
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
|
@ -21,13 +21,13 @@ import contact, {
|
||||
Organization,
|
||||
Person,
|
||||
PersonAccount,
|
||||
PersonSpace,
|
||||
contactId,
|
||||
formatContactName,
|
||||
formatName,
|
||||
getFirstName,
|
||||
getLastName,
|
||||
getName,
|
||||
PersonSpace
|
||||
getName
|
||||
} from '@hcengineering/contact'
|
||||
import core, {
|
||||
Account,
|
||||
@ -37,13 +37,13 @@ import core, {
|
||||
Ref,
|
||||
SpaceType,
|
||||
Tx,
|
||||
TxCUD,
|
||||
TxCreateDoc,
|
||||
TxMixin,
|
||||
TxProcessor,
|
||||
TxRemoveDoc,
|
||||
TxUpdateDoc,
|
||||
concatLink,
|
||||
TxCUD
|
||||
concatLink
|
||||
} from '@hcengineering/core'
|
||||
import notification, { Collaborators } from '@hcengineering/notification'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
@ -85,41 +85,49 @@ export async function OnSpaceTypeMembers (tx: Tx, control: TriggerControl): Prom
|
||||
export async function OnEmployeeCreate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const mixinTx = tx as TxMixin<Person, Employee>
|
||||
if (mixinTx.attributes.active !== true) return []
|
||||
const acc = await control.modelDb.findOne(contact.class.PersonAccount, { person: mixinTx.objectId })
|
||||
if (acc === undefined) return []
|
||||
const acc = control.modelDb.getAccountByPersonId(mixinTx.objectId)
|
||||
if (acc.length === 0) return []
|
||||
const spaces = await control.findAll(core.class.Space, { autoJoin: true })
|
||||
const result: Tx[] = []
|
||||
|
||||
const txes = await createPersonSpace(acc._id, mixinTx.objectId, control)
|
||||
const txes = await createPersonSpace(
|
||||
acc.map((it) => it._id),
|
||||
mixinTx.objectId,
|
||||
control
|
||||
)
|
||||
result.push(...txes)
|
||||
|
||||
for (const space of spaces) {
|
||||
if (space.members.includes(acc._id)) continue
|
||||
const pushTx = control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, {
|
||||
$push: {
|
||||
members: acc._id
|
||||
}
|
||||
})
|
||||
result.push(pushTx)
|
||||
const toAdd = acc.filter((it) => !space.members.includes(it._id))
|
||||
if (toAdd.length === 0) continue
|
||||
for (const a of toAdd) {
|
||||
const pushTx = control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, {
|
||||
$push: {
|
||||
members: a._id
|
||||
}
|
||||
})
|
||||
result.push(pushTx)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function createPersonSpace (
|
||||
account: Ref<Account>,
|
||||
account: Ref<Account>[],
|
||||
person: Ref<Person>,
|
||||
control: TriggerControl
|
||||
): Promise<TxCUD<PersonSpace>[]> {
|
||||
const personSpace = (await control.findAll(contact.class.PersonSpace, { person }, { limit: 1 })).shift()
|
||||
if (personSpace !== undefined) {
|
||||
if (personSpace.members.includes(account)) return []
|
||||
return [
|
||||
const toAdd = account.filter((it) => !personSpace.members.includes(it))
|
||||
if (toAdd.length === 0) return []
|
||||
return toAdd.map((it) =>
|
||||
control.txFactory.createTxUpdateDoc(personSpace._class, personSpace.space, personSpace._id, {
|
||||
$push: {
|
||||
members: account
|
||||
members: it
|
||||
}
|
||||
})
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
return [
|
||||
@ -129,7 +137,7 @@ async function createPersonSpace (
|
||||
private: true,
|
||||
archived: false,
|
||||
person,
|
||||
members: [account]
|
||||
members: account
|
||||
})
|
||||
]
|
||||
}
|
||||
@ -143,7 +151,7 @@ export async function OnPersonAccountCreate (tx: Tx, control: TriggerControl): P
|
||||
const spaces = await control.findAll(core.class.Space, { autoJoin: true })
|
||||
|
||||
const result: Tx[] = []
|
||||
const txes = await createPersonSpace(acc._id, person._id, control)
|
||||
const txes = await createPersonSpace([acc._id], person._id, control)
|
||||
|
||||
result.push(...txes)
|
||||
|
||||
|
@ -88,13 +88,13 @@ export async function OnMessageCreate (tx: Tx, control: TriggerControl): Promise
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function IsIncomingMessage (
|
||||
export function IsIncomingMessageTypeMatch (
|
||||
tx: Tx,
|
||||
doc: Doc,
|
||||
user: Ref<Account>,
|
||||
type: NotificationType,
|
||||
control: TriggerControl
|
||||
): Promise<boolean> {
|
||||
): boolean {
|
||||
const message = TxProcessor.createDoc2Doc(TxProcessor.extractTx(tx) as TxCreateDoc<Message>)
|
||||
return message.incoming && message.sendOn > (doc.createdOn ?? doc.modifiedOn)
|
||||
}
|
||||
@ -183,7 +183,7 @@ export default async () => ({
|
||||
OnMessageCreate
|
||||
},
|
||||
function: {
|
||||
IsIncomingMessage,
|
||||
IsIncomingMessageTypeMatch,
|
||||
FindMessages,
|
||||
SendEmailNotifications
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ export default plugin(serverGmailId, {
|
||||
OnMessageCreate: '' as Resource<TriggerFunc>
|
||||
},
|
||||
function: {
|
||||
IsIncomingMessage: '' as TypeMatchFunc,
|
||||
IsIncomingMessageTypeMatch: '' as TypeMatchFunc,
|
||||
FindMessages: '' as Resource<ObjectDDParticipantFunc>,
|
||||
SendEmailNotifications: '' as Resource<NotificationProviderFunc>
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import contact, { Contact, Employee, Person, PersonAccount, formatName, getName } from '@hcengineering/contact'
|
||||
import contact, { Contact, Employee, formatName, getName, Person, PersonAccount } from '@hcengineering/contact'
|
||||
import core, {
|
||||
Doc,
|
||||
Ref,
|
||||
@ -27,6 +27,7 @@ import core, {
|
||||
TxRemoveDoc,
|
||||
TxUpdateDoc
|
||||
} from '@hcengineering/core'
|
||||
import gmail from '@hcengineering/gmail'
|
||||
import hr, {
|
||||
Department,
|
||||
DepartmentMember,
|
||||
@ -39,10 +40,13 @@ import hr, {
|
||||
import notification, { NotificationType } from '@hcengineering/notification'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import { TriggerControl } from '@hcengineering/server-core'
|
||||
import { getEmployee, getPersonAccountById } from '@hcengineering/server-notification'
|
||||
import { getContentByTemplate, isAllowed } from '@hcengineering/server-notification-resources'
|
||||
import gmail from '@hcengineering/gmail'
|
||||
import { sendEmailNotification } from '@hcengineering/server-gmail-resources'
|
||||
import { getEmployee, getPersonAccountById } from '@hcengineering/server-notification'
|
||||
import {
|
||||
getContentByTemplate,
|
||||
getNotificationProviderControl,
|
||||
isAllowed
|
||||
} from '@hcengineering/server-notification-resources'
|
||||
|
||||
async function getOldDepartment (
|
||||
currentTx: TxMixin<Employee, Staff> | TxUpdateDoc<Employee>,
|
||||
@ -97,21 +101,29 @@ function exlude (first: Ref<Department>[], second: Ref<Department>[]): Ref<Depar
|
||||
|
||||
function getTxes (
|
||||
factory: TxFactory,
|
||||
account: Ref<DepartmentMember>,
|
||||
account: Ref<DepartmentMember>[],
|
||||
added: Ref<Department>[],
|
||||
removed?: Ref<Department>[]
|
||||
): Tx[] {
|
||||
const pushTxes = added.map((dep) =>
|
||||
factory.createTxUpdateDoc(hr.class.Department, core.space.Workspace, dep, {
|
||||
$push: { members: account }
|
||||
})
|
||||
)
|
||||
const pushTxes = added
|
||||
.map((dep) =>
|
||||
account.map((it) =>
|
||||
factory.createTxUpdateDoc(hr.class.Department, core.space.Workspace, dep, {
|
||||
$push: { members: it }
|
||||
})
|
||||
)
|
||||
)
|
||||
.flat()
|
||||
if (removed === undefined) return pushTxes
|
||||
const pullTxes = removed.map((dep) =>
|
||||
factory.createTxUpdateDoc(hr.class.Department, core.space.Workspace, dep, {
|
||||
$pull: { members: account }
|
||||
})
|
||||
)
|
||||
const pullTxes = removed
|
||||
.map((dep) =>
|
||||
account.map((it) =>
|
||||
factory.createTxUpdateDoc(hr.class.Department, core.space.Workspace, dep, {
|
||||
$pull: { members: it }
|
||||
})
|
||||
)
|
||||
)
|
||||
.flat()
|
||||
return [...pullTxes, ...pushTxes]
|
||||
}
|
||||
|
||||
@ -121,12 +133,8 @@ function getTxes (
|
||||
export async function OnDepartmentStaff (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const ctx = TxProcessor.extractTx(tx) as TxMixin<Employee, Staff>
|
||||
|
||||
const targetAccount = (
|
||||
await control.modelDb.findAll(contact.class.PersonAccount, {
|
||||
person: ctx.objectId
|
||||
})
|
||||
)[0]
|
||||
if (targetAccount === undefined) return []
|
||||
const targetAccount = control.modelDb.getAccountByPersonId(ctx.objectId) as PersonAccount[]
|
||||
if (targetAccount.length === 0) return []
|
||||
|
||||
if (ctx.attributes.department !== undefined) {
|
||||
const lastDepartment = await getOldDepartment(ctx, control)
|
||||
@ -137,7 +145,7 @@ export async function OnDepartmentStaff (tx: Tx, control: TriggerControl): Promi
|
||||
const removed = await buildHierarchy(lastDepartment, control)
|
||||
return getTxes(
|
||||
control.txFactory,
|
||||
targetAccount._id,
|
||||
targetAccount.map((it) => it._id),
|
||||
[],
|
||||
removed.map((p) => p._id)
|
||||
)
|
||||
@ -146,13 +154,22 @@ export async function OnDepartmentStaff (tx: Tx, control: TriggerControl): Promi
|
||||
const push = (await buildHierarchy(departmentId, control)).map((p) => p._id)
|
||||
|
||||
if (lastDepartment === undefined) {
|
||||
return getTxes(control.txFactory, targetAccount._id, push)
|
||||
return getTxes(
|
||||
control.txFactory,
|
||||
targetAccount.map((it) => it._id),
|
||||
push
|
||||
)
|
||||
}
|
||||
|
||||
let removed = (await buildHierarchy(lastDepartment, control)).map((p) => p._id)
|
||||
const added = exlude(removed, push)
|
||||
removed = exlude(push, removed)
|
||||
return getTxes(control.txFactory, targetAccount._id, added, removed)
|
||||
return getTxes(
|
||||
control.txFactory,
|
||||
targetAccount.map((it) => it._id),
|
||||
added,
|
||||
removed
|
||||
)
|
||||
}
|
||||
|
||||
return []
|
||||
@ -183,16 +200,14 @@ export async function OnDepartmentRemove (tx: Tx, control: TriggerControl): Prom
|
||||
employee.forEach((em) => {
|
||||
res.push(control.txFactory.createTxMixin(em._id, em._class, em.space, hr.mixin.Staff, { department: undefined }))
|
||||
})
|
||||
targetAccounts.forEach((acc) => {
|
||||
res.push(
|
||||
...getTxes(
|
||||
control.txFactory,
|
||||
acc._id,
|
||||
[],
|
||||
removed.map((p) => p._id)
|
||||
)
|
||||
res.push(
|
||||
...getTxes(
|
||||
control.txFactory,
|
||||
targetAccounts.map((it) => it._id),
|
||||
[],
|
||||
removed.map((p) => p._id)
|
||||
)
|
||||
})
|
||||
)
|
||||
return res
|
||||
}
|
||||
|
||||
@ -232,19 +247,15 @@ export async function OnEmployeeDeactivate (tx: Tx, control: TriggerControl): Pr
|
||||
return []
|
||||
}
|
||||
|
||||
const targetAccount = (
|
||||
await control.modelDb.findAll(contact.class.PersonAccount, {
|
||||
person: ctx.objectId
|
||||
})
|
||||
)[0]
|
||||
if (targetAccount === undefined) return []
|
||||
const targetAccount = control.modelDb.getAccountByPersonId(ctx.objectId) as PersonAccount[]
|
||||
if (targetAccount.length === 0) return []
|
||||
const lastDepartment = await getOldDepartment(ctx, control)
|
||||
if (lastDepartment === undefined) return []
|
||||
|
||||
const removed = await buildHierarchy(lastDepartment, control)
|
||||
return getTxes(
|
||||
control.txFactory,
|
||||
targetAccount._id,
|
||||
targetAccount.map((it) => it._id),
|
||||
[],
|
||||
removed.map((p) => p._id)
|
||||
)
|
||||
@ -268,19 +279,21 @@ async function sendEmailNotifications (
|
||||
}
|
||||
|
||||
// should respect employee settings
|
||||
const accounts = await control.modelDb.findAll(contact.class.PersonAccount, {
|
||||
person: { $in: Array.from(contacts.values()) as Ref<Employee>[] }
|
||||
})
|
||||
const type = await control.modelDb.findOne(notification.class.NotificationType, { _id: typeId })
|
||||
if (type === undefined) return
|
||||
const provider = await control.modelDb.findOne(notification.class.NotificationProvider, {
|
||||
_id: gmail.providers.EmailNotificationProvider
|
||||
})
|
||||
if (provider === undefined) return
|
||||
for (const account of accounts) {
|
||||
const allowed = await isAllowed(control, account._id, type, provider)
|
||||
if (!allowed) {
|
||||
contacts.delete(account.person)
|
||||
|
||||
const notificationControl = await getNotificationProviderControl(control.ctx, control)
|
||||
for (const accountId of contacts.values()) {
|
||||
const accounts = control.modelDb.getAccountByPersonId(accountId) as PersonAccount[]
|
||||
for (const account of accounts) {
|
||||
const allowed = isAllowed(control, account._id, type, provider, notificationControl)
|
||||
if (!allowed) {
|
||||
contacts.delete(account.person)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,7 +319,7 @@ async function sendEmailNotifications (
|
||||
export async function OnRequestCreate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const ctx = TxProcessor.extractTx(tx) as TxCreateDoc<Request>
|
||||
|
||||
const sender = await getPersonAccountById(ctx.modifiedBy, control)
|
||||
const sender = getPersonAccountById(ctx.modifiedBy, control)
|
||||
if (sender === undefined) return []
|
||||
|
||||
const request = TxProcessor.createDoc2Doc(ctx)
|
||||
@ -321,7 +334,7 @@ export async function OnRequestCreate (tx: Tx, control: TriggerControl): Promise
|
||||
export async function OnRequestUpdate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const ctx = TxProcessor.extractTx(tx) as TxUpdateDoc<Request>
|
||||
|
||||
const sender = await getPersonAccountById(ctx.modifiedBy, control)
|
||||
const sender = getPersonAccountById(ctx.modifiedBy, control)
|
||||
if (sender === undefined) return []
|
||||
|
||||
const request = (await control.findAll(hr.class.Request, { _id: ctx.objectId }))[0] as Request
|
||||
@ -337,7 +350,7 @@ export async function OnRequestUpdate (tx: Tx, control: TriggerControl): Promise
|
||||
export async function OnRequestRemove (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const ctx = TxProcessor.extractTx(tx) as TxCreateDoc<Request>
|
||||
|
||||
const sender = await getPersonAccountById(ctx.modifiedBy, control)
|
||||
const sender = getPersonAccountById(ctx.modifiedBy, control)
|
||||
if (sender === undefined) return []
|
||||
|
||||
const request = control.removedMap.get(ctx.objectId) as Request
|
||||
@ -389,7 +402,7 @@ export async function RequestTextPresenter (doc: Doc, control: TriggerControl):
|
||||
export async function OnPublicHolidayCreate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const ctx = TxProcessor.extractTx(tx) as TxCreateDoc<PublicHoliday>
|
||||
|
||||
const sender = await getPersonAccountById(ctx.modifiedBy, control)
|
||||
const sender = getPersonAccountById(ctx.modifiedBy, control)
|
||||
if (sender === undefined) return []
|
||||
const employee = await getEmployee(sender.person as Ref<Employee>, control)
|
||||
if (employee === undefined) return []
|
||||
@ -410,7 +423,7 @@ export async function OnPublicHolidayCreate (tx: Tx, control: TriggerControl): P
|
||||
*/
|
||||
export async function PublicHolidayHTMLPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
||||
const holiday = doc as PublicHoliday
|
||||
const sender = await getPersonAccountById(holiday.modifiedBy, control)
|
||||
const sender = getPersonAccountById(holiday.modifiedBy, control)
|
||||
if (sender === undefined) return ''
|
||||
const employee = await getEmployee(sender.person as Ref<Employee>, control)
|
||||
if (employee === undefined) return ''
|
||||
@ -426,7 +439,7 @@ export async function PublicHolidayHTMLPresenter (doc: Doc, control: TriggerCont
|
||||
*/
|
||||
export async function PublicHolidayTextPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
||||
const holiday = doc as PublicHoliday
|
||||
const sender = await getPersonAccountById(holiday.modifiedBy, control)
|
||||
const sender = getPersonAccountById(holiday.modifiedBy, control)
|
||||
if (sender === undefined) return ''
|
||||
const employee = await getEmployee(sender.person as Ref<Employee>, control)
|
||||
if (employee === undefined) return ''
|
||||
|
@ -37,7 +37,11 @@ import love, {
|
||||
import notification from '@hcengineering/notification'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import { TriggerControl } from '@hcengineering/server-core'
|
||||
import { createPushNotification, isAllowed } from '@hcengineering/server-notification-resources'
|
||||
import {
|
||||
createPushNotification,
|
||||
getNotificationProviderControl,
|
||||
isAllowed
|
||||
} from '@hcengineering/server-notification-resources'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
|
||||
export async function OnEmployee (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
@ -267,16 +271,19 @@ export async function OnKnock (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
_id: notification.providers.PushNotificationProvider
|
||||
})
|
||||
if (provider === undefined) return []
|
||||
|
||||
const notificationControl = await getNotificationProviderControl(control.ctx, control)
|
||||
for (const user of roomInfo.persons) {
|
||||
const userAcc = await control.modelDb.findOne(contact.class.PersonAccount, { person: user })
|
||||
if (userAcc === undefined) continue
|
||||
if (await isAllowed(control, userAcc._id, type, provider)) {
|
||||
const userAcc = control.modelDb.getAccountByPersonId(user) as PersonAccount[]
|
||||
if (userAcc.length === 0) continue
|
||||
if (userAcc.some((it) => isAllowed(control, it._id, type, provider, notificationControl))) {
|
||||
const path = [workbenchId, control.workspace.workspaceUrl, loveId]
|
||||
const title = await translate(love.string.KnockingLabel, {})
|
||||
const body = await translate(love.string.IsKnocking, {
|
||||
name: formatName(from.name, control.branding?.lastNameFirst)
|
||||
})
|
||||
await createPushNotification(control, userAcc._id, title, body, request._id, from, path)
|
||||
// TODO: Select proper account target
|
||||
await createPushNotification(control, userAcc[0]._id, title, body, request._id, from, path)
|
||||
}
|
||||
}
|
||||
return res
|
||||
@ -293,8 +300,8 @@ export async function OnInvite (tx: Tx, control: TriggerControl): Promise<Tx[]>
|
||||
if (invite.status === RequestStatus.Pending) {
|
||||
const target = (await control.findAll(contact.class.Person, { _id: invite.target }))[0]
|
||||
if (target === undefined) return []
|
||||
const userAcc = await control.modelDb.findOne(contact.class.PersonAccount, { person: target._id })
|
||||
if (userAcc === undefined) return []
|
||||
const userAcc = control.modelDb.getAccountByPersonId(target._id) as PersonAccount[]
|
||||
if (userAcc.length === 0) return []
|
||||
const from = (await control.findAll(contact.class.Person, { _id: invite.from }))[0]
|
||||
const type = await control.modelDb.findOne(notification.class.NotificationType, {
|
||||
_id: love.ids.InviteNotification
|
||||
@ -304,7 +311,8 @@ export async function OnInvite (tx: Tx, control: TriggerControl): Promise<Tx[]>
|
||||
_id: notification.providers.PushNotificationProvider
|
||||
})
|
||||
if (provider === undefined) return []
|
||||
if (await isAllowed(control, userAcc._id, type, provider)) {
|
||||
const notificationControl = await getNotificationProviderControl(control.ctx, control)
|
||||
if (userAcc.some((it) => isAllowed(control, it._id, type, provider, notificationControl))) {
|
||||
const path = [workbenchId, control.workspace.workspaceUrl, loveId]
|
||||
const title = await translate(love.string.InivitingLabel, {})
|
||||
const body =
|
||||
@ -313,7 +321,8 @@ export async function OnInvite (tx: Tx, control: TriggerControl): Promise<Tx[]>
|
||||
name: formatName(from.name, control.branding?.lastNameFirst)
|
||||
})
|
||||
: await translate(love.string.InivitingLabel, {})
|
||||
await createPushNotification(control, userAcc._id, title, body, invite._id, from, path)
|
||||
// TODO: Select a proper user
|
||||
await createPushNotification(control, userAcc[0]._id, title, body, invite._id, from, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,11 +17,11 @@
|
||||
import activity, { ActivityMessage, DocUpdateMessage } from '@hcengineering/activity'
|
||||
import chunter, { ChatMessage } from '@hcengineering/chunter'
|
||||
import contact, {
|
||||
type AvatarInfo,
|
||||
getAvatarProviderId,
|
||||
getGravatarUrl,
|
||||
Person,
|
||||
PersonAccount
|
||||
PersonAccount,
|
||||
type AvatarInfo
|
||||
} from '@hcengineering/contact'
|
||||
import core, {
|
||||
Account,
|
||||
@ -72,7 +72,6 @@ import { getMetadata, getResource, translate } from '@hcengineering/platform'
|
||||
import { type TriggerControl } from '@hcengineering/server-core'
|
||||
import serverCore from '@hcengineering/server-core'
|
||||
import serverNotification, {
|
||||
getPersonAccount,
|
||||
getPersonAccountById,
|
||||
NOTIFICATION_BODY_SIZE,
|
||||
PUSH_NOTIFICATION_TITLE_SIZE,
|
||||
@ -92,17 +91,19 @@ import {
|
||||
getHTMLPresenter,
|
||||
getNotificationContent,
|
||||
getNotificationLink,
|
||||
getNotificationProviderControl,
|
||||
getTextPresenter,
|
||||
getUsersInfo,
|
||||
isAllowed,
|
||||
isMixinTx,
|
||||
isShouldNotifyTx,
|
||||
isUserEmployeeInFieldValue,
|
||||
isUserInFieldValue,
|
||||
isUserEmployeeInFieldValueTypeMatch,
|
||||
isUserInFieldValueTypeMatch,
|
||||
messageToMarkup,
|
||||
replaceAll,
|
||||
toReceiverInfo,
|
||||
updateNotifyContextsSpace
|
||||
updateNotifyContextsSpace,
|
||||
type NotificationProviderControl
|
||||
} from './utils'
|
||||
|
||||
export function getPushCollaboratorTx (
|
||||
@ -124,6 +125,7 @@ export function getPushCollaboratorTx (
|
||||
}
|
||||
|
||||
export async function getCommonNotificationTxes (
|
||||
ctx: MeasureContext,
|
||||
control: TriggerControl,
|
||||
doc: Doc,
|
||||
data: Partial<Data<CommonInboxNotification>>,
|
||||
@ -144,6 +146,7 @@ export async function getCommonNotificationTxes (
|
||||
const notifyContexts = await control.findAll(notification.class.DocNotifyContext, { objectId: attachedTo })
|
||||
|
||||
const notificationTx = await pushInboxNotifications(
|
||||
ctx,
|
||||
control,
|
||||
res,
|
||||
receiver,
|
||||
@ -255,15 +258,14 @@ export async function getContentByTemplate (
|
||||
}
|
||||
}
|
||||
|
||||
async function getValueCollaborators (value: any, attr: AnyAttribute, control: TriggerControl): Promise<Ref<Account>[]> {
|
||||
function getValueCollaborators (value: any, attr: AnyAttribute, control: TriggerControl): Ref<Account>[] {
|
||||
const hierarchy = control.hierarchy
|
||||
if (attr.type._class === core.class.RefTo) {
|
||||
const to = (attr.type as RefTo<Doc>).to
|
||||
if (hierarchy.isDerived(to, contact.class.Person)) {
|
||||
const acc = await getPersonAccount(value, control)
|
||||
return acc !== undefined ? [acc._id] : []
|
||||
return (control.modelDb.getAccountByPersonId(value) as PersonAccount[]).map((it) => it._id)
|
||||
} else if (hierarchy.isDerived(to, core.class.Account)) {
|
||||
const acc = await getPersonAccountById(value, control)
|
||||
const acc = getPersonAccountById(value, control)
|
||||
return acc !== undefined ? [acc._id] : []
|
||||
}
|
||||
} else if (attr.type._class === core.class.ArrOf) {
|
||||
@ -271,12 +273,12 @@ async function getValueCollaborators (value: any, attr: AnyAttribute, control: T
|
||||
if (arrOf._class === core.class.RefTo) {
|
||||
const to = (arrOf as RefTo<Doc>).to
|
||||
if (hierarchy.isDerived(to, contact.class.Person)) {
|
||||
const employeeAccounts = await control.modelDb.findAll(contact.class.PersonAccount, {
|
||||
const employeeAccounts = control.modelDb.findAllSync(contact.class.PersonAccount, {
|
||||
person: { $in: Array.isArray(value) ? value : [value] }
|
||||
})
|
||||
return employeeAccounts.map((p) => p._id)
|
||||
} else if (hierarchy.isDerived(to, core.class.Account)) {
|
||||
const employeeAccounts = await control.modelDb.findAll(contact.class.PersonAccount, {
|
||||
const employeeAccounts = control.modelDb.findAllSync(contact.class.PersonAccount, {
|
||||
_id: { $in: Array.isArray(value) ? value : [value] }
|
||||
})
|
||||
return employeeAccounts.map((p) => p._id)
|
||||
@ -295,7 +297,7 @@ async function getKeyCollaborators (
|
||||
if (value !== undefined && value !== null) {
|
||||
const attr = control.hierarchy.findAttribute(docClass, field)
|
||||
if (attr !== undefined) {
|
||||
return await getValueCollaborators(value, attr, control)
|
||||
return getValueCollaborators(value, attr, control)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -315,7 +317,7 @@ export async function getDocCollaborators (
|
||||
const newCollaborators = await ctx.with(
|
||||
'getKeyCollaborators',
|
||||
{},
|
||||
async () => await getKeyCollaborators(doc._class, value, field, control)
|
||||
async (ctx) => await getKeyCollaborators(doc._class, value, field, control)
|
||||
)
|
||||
if (newCollaborators !== undefined) {
|
||||
for (const newCollaborator of newCollaborators) {
|
||||
@ -327,6 +329,7 @@ export async function getDocCollaborators (
|
||||
}
|
||||
|
||||
export async function pushInboxNotifications (
|
||||
ctx: MeasureContext,
|
||||
control: TriggerControl,
|
||||
res: Tx[],
|
||||
receiver: ReceiverInfo,
|
||||
@ -344,6 +347,7 @@ export async function pushInboxNotifications (
|
||||
|
||||
if (context === undefined) {
|
||||
docNotifyContextId = await createNotifyContext(
|
||||
ctx,
|
||||
control,
|
||||
objectId,
|
||||
objectClass,
|
||||
@ -584,6 +588,7 @@ async function sendPushToSubscription (
|
||||
* @public
|
||||
*/
|
||||
export async function pushActivityInboxNotifications (
|
||||
ctx: MeasureContext,
|
||||
originTx: TxCUD<Doc>,
|
||||
control: TriggerControl,
|
||||
res: Tx[],
|
||||
@ -594,7 +599,7 @@ export async function pushActivityInboxNotifications (
|
||||
activityMessage: ActivityMessage,
|
||||
shouldUpdateTimestamp: boolean
|
||||
): Promise<TxCreateDoc<InboxNotification> | undefined> {
|
||||
const content = await getNotificationContent(originTx, receiver.account, sender, object, control)
|
||||
const content = await getNotificationContent(originTx, [receiver.account], sender, object, control)
|
||||
const data: Partial<Data<ActivityInboxNotification>> = {
|
||||
...content,
|
||||
attachedTo: activityMessage._id,
|
||||
@ -602,6 +607,7 @@ export async function pushActivityInboxNotifications (
|
||||
}
|
||||
|
||||
return await pushInboxNotifications(
|
||||
ctx,
|
||||
control,
|
||||
res,
|
||||
receiver,
|
||||
@ -628,7 +634,7 @@ export async function applyNotificationProviders (
|
||||
sender: SenderInfo,
|
||||
message?: ActivityMessage
|
||||
): Promise<void> {
|
||||
const resources = await control.modelDb.findAll(serverNotification.class.NotificationProviderResources, {})
|
||||
const resources = control.modelDb.findAllSync(serverNotification.class.NotificationProviderResources, {})
|
||||
for (const [provider, types] of notifyResult.entries()) {
|
||||
if (provider === notification.providers.PushNotificationProvider) {
|
||||
// const now = Date.now()
|
||||
@ -663,6 +669,7 @@ export async function applyNotificationProviders (
|
||||
}
|
||||
|
||||
async function createNotifyContext (
|
||||
ctx: MeasureContext,
|
||||
control: TriggerControl,
|
||||
objectId: Ref<Doc>,
|
||||
objectClass: Ref<Class<Doc>>,
|
||||
@ -678,7 +685,7 @@ async function createNotifyContext (
|
||||
isPinned: false,
|
||||
lastUpdateTimestamp: updateTimestamp
|
||||
})
|
||||
await control.apply([createTx])
|
||||
await ctx.with('apply', {}, () => control.apply([createTx]))
|
||||
if (receiver.account?.email !== undefined) {
|
||||
control.operationContext.derived.targets['docNotifyContext' + createTx._id] = (it) => {
|
||||
if (it._id === createTx._id) {
|
||||
@ -690,6 +697,7 @@ async function createNotifyContext (
|
||||
}
|
||||
|
||||
export async function getNotificationTxes (
|
||||
ctx: MeasureContext,
|
||||
control: TriggerControl,
|
||||
object: Doc,
|
||||
tx: TxCUD<Doc>,
|
||||
@ -698,7 +706,8 @@ export async function getNotificationTxes (
|
||||
sender: SenderInfo,
|
||||
params: NotifyParams,
|
||||
docNotifyContexts: DocNotifyContext[],
|
||||
activityMessages: ActivityMessage[]
|
||||
activityMessages: ActivityMessage[],
|
||||
settings: NotificationProviderControl
|
||||
): Promise<Tx[]> {
|
||||
if (receiver.account === undefined) {
|
||||
return []
|
||||
@ -713,14 +722,16 @@ export async function getNotificationTxes (
|
||||
tx,
|
||||
originTx,
|
||||
object,
|
||||
receiver.account,
|
||||
[receiver.account],
|
||||
params.isOwn,
|
||||
params.isSpace,
|
||||
settings,
|
||||
docMessage
|
||||
)
|
||||
|
||||
if (notifyResult.has(notification.providers.InboxNotificationProvider)) {
|
||||
const notificationTx = await pushActivityInboxNotifications(
|
||||
ctx,
|
||||
originTx,
|
||||
control,
|
||||
res,
|
||||
@ -753,6 +764,7 @@ export async function getNotificationTxes (
|
||||
|
||||
if (context === undefined) {
|
||||
await createNotifyContext(
|
||||
ctx,
|
||||
control,
|
||||
message.attachedTo,
|
||||
message.attachedToClass,
|
||||
@ -767,20 +779,17 @@ export async function getNotificationTxes (
|
||||
}
|
||||
|
||||
async function updateContextsTimestamp (
|
||||
ctx: MeasureContext,
|
||||
contexts: DocNotifyContext[],
|
||||
timestamp: Timestamp,
|
||||
control: TriggerControl,
|
||||
modifiedBy: Ref<Account>
|
||||
): Promise<void> {
|
||||
if (contexts.length === 0) return
|
||||
|
||||
const accounts = await control.modelDb.findAll(contact.class.PersonAccount, {
|
||||
_id: { $in: contexts.map((it) => it.user as Ref<PersonAccount>) }
|
||||
})
|
||||
const res: Tx[] = []
|
||||
|
||||
for (const context of contexts) {
|
||||
const account = accounts.find(({ _id }) => _id === context.user)
|
||||
const account = getPersonAccountById(context.user, control) // accounts.find(({ _id }) => _id === context.user)
|
||||
const isViewed =
|
||||
context.lastViewedTimestamp !== undefined && (context.lastUpdateTimestamp ?? 0) <= context.lastViewedTimestamp
|
||||
const updateTx = control.txFactory.createTxUpdateDoc(context._class, context.space, context._id, {
|
||||
@ -803,10 +812,13 @@ async function updateContextsTimestamp (
|
||||
}
|
||||
}
|
||||
|
||||
await control.apply(res)
|
||||
if (res.length > 0) {
|
||||
await ctx.with('apply', {}, () => control.apply(res))
|
||||
}
|
||||
}
|
||||
|
||||
async function removeContexts (
|
||||
ctx: MeasureContext,
|
||||
contexts: DocNotifyContext[],
|
||||
unsubscribe: Ref<PersonAccount>[],
|
||||
control: TriggerControl
|
||||
@ -814,13 +826,13 @@ async function removeContexts (
|
||||
if (contexts.length === 0) return
|
||||
if (unsubscribe.length === 0) return
|
||||
|
||||
const unsubscribeAccounts = await control.modelDb.findAll(contact.class.PersonAccount, {
|
||||
_id: { $in: unsubscribe }
|
||||
})
|
||||
const res: Tx[] = []
|
||||
|
||||
for (const context of contexts) {
|
||||
const account = unsubscribeAccounts.find(({ _id }) => _id === context.user)
|
||||
if (!unsubscribe.includes(context.user as Ref<PersonAccount>)) {
|
||||
continue
|
||||
}
|
||||
const account = control.modelDb.getObject(context.user)
|
||||
if (account === undefined) continue
|
||||
|
||||
const removeTx = control.txFactory.createTxRemoveDoc(context._class, context.space, context._id)
|
||||
@ -864,23 +876,31 @@ export async function createCollabDocInfo (
|
||||
objectId: object._id,
|
||||
user: { $in: unsubscribe }
|
||||
})
|
||||
await removeContexts(notifyContexts, unsubscribe, control)
|
||||
await removeContexts(ctx, notifyContexts, unsubscribe, control)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
const notifyContexts = await control.findAllCtx(ctx, notification.class.DocNotifyContext, { objectId: object._id })
|
||||
await removeContexts(notifyContexts, unsubscribe, control)
|
||||
await updateContextsTimestamp(notifyContexts, originTx.modifiedOn, control, originTx.modifiedBy)
|
||||
let notifyContexts: DocNotifyContext[] = []
|
||||
if (!(object._id === tx.objectId && tx._class === core.class.TxCreateDoc)) {
|
||||
notifyContexts = await control.findAllCtx(ctx, notification.class.DocNotifyContext, { objectId: object._id })
|
||||
}
|
||||
|
||||
if (notifyContexts.length > 0) {
|
||||
await updateContextsTimestamp(ctx, notifyContexts, originTx.modifiedOn, control, originTx.modifiedBy)
|
||||
}
|
||||
if (notifyContexts.length > 0 && unsubscribe.length > 0) {
|
||||
await removeContexts(ctx, notifyContexts, unsubscribe, control)
|
||||
}
|
||||
|
||||
const targets = new Set(collaborators)
|
||||
|
||||
// user is not collaborator of himself, but we should notify user of changes related to users account (mentions, comments etc)
|
||||
if (control.hierarchy.isDerived(object._class, contact.class.Person)) {
|
||||
const acc = await getPersonAccount(object._id as Ref<Person>, control)
|
||||
if (acc !== undefined) {
|
||||
targets.add(acc._id)
|
||||
const acc = control.modelDb.getAccountByPersonId(object._id as Ref<Person>) as PersonAccount[]
|
||||
for (const a of acc.map((it) => it._id)) {
|
||||
targets.add(a)
|
||||
}
|
||||
}
|
||||
|
||||
@ -893,19 +913,19 @@ export async function createCollabDocInfo (
|
||||
{},
|
||||
async (ctx) => await getUsersInfo(ctx, [...Array.from(targets), originTx.modifiedBy as Ref<PersonAccount>], control)
|
||||
)
|
||||
const sender: SenderInfo = usersInfo.find(({ _id }) => _id === originTx.modifiedBy) ?? {
|
||||
const sender: SenderInfo = usersInfo.get(originTx.modifiedBy) ?? {
|
||||
_id: originTx.modifiedBy
|
||||
}
|
||||
|
||||
const settings = await getNotificationProviderControl(ctx, control)
|
||||
|
||||
for (const target of targets) {
|
||||
const info: ReceiverInfo | undefined = toReceiverInfo(
|
||||
control.hierarchy,
|
||||
usersInfo.find(({ _id }) => _id === target)
|
||||
)
|
||||
const info: ReceiverInfo | undefined = toReceiverInfo(control.hierarchy, usersInfo.get(target))
|
||||
|
||||
if (info === undefined) continue
|
||||
|
||||
const targetRes = await getNotificationTxes(
|
||||
ctx,
|
||||
control,
|
||||
object,
|
||||
tx,
|
||||
@ -914,7 +934,8 @@ export async function createCollabDocInfo (
|
||||
sender,
|
||||
params,
|
||||
notifyContexts,
|
||||
docMessages
|
||||
docMessages,
|
||||
settings
|
||||
)
|
||||
const ids = new Set(targetRes.map((it) => it._id))
|
||||
if (info.account?.email !== undefined) {
|
||||
@ -994,6 +1015,7 @@ async function getTxCollabs (
|
||||
}
|
||||
|
||||
async function getSpaceCollabTxes (
|
||||
ctx: MeasureContext,
|
||||
control: TriggerControl,
|
||||
doc: Doc,
|
||||
tx: TxCUD<Doc>,
|
||||
@ -1005,7 +1027,8 @@ async function getSpaceCollabTxes (
|
||||
return []
|
||||
}
|
||||
|
||||
const space = cache.get(doc.space) ?? (await control.findAll(core.class.Space, { _id: doc.space }, { limit: 1 }))[0]
|
||||
const space =
|
||||
cache.get(doc.space) ?? (await control.findAllCtx(ctx, core.class.Space, { _id: doc.space }, { limit: 1 }))[0]
|
||||
if (space === undefined) return []
|
||||
|
||||
cache.set(space._id, space)
|
||||
@ -1018,7 +1041,7 @@ async function getSpaceCollabTxes (
|
||||
const collabs = control.hierarchy.as<Doc, Collaborators>(space, notification.mixin.Collaborators)
|
||||
if (collabs.collaborators !== undefined) {
|
||||
return await createCollabDocInfo(
|
||||
control.ctx,
|
||||
ctx,
|
||||
collabs.collaborators as Ref<PersonAccount>[],
|
||||
control,
|
||||
tx,
|
||||
@ -1059,7 +1082,7 @@ async function createCollaboratorDoc (
|
||||
const notificationTxes = await ctx.with(
|
||||
'create-collabdocinfo',
|
||||
{},
|
||||
async () =>
|
||||
async (ctx) =>
|
||||
await createCollabDocInfo(
|
||||
ctx,
|
||||
collaborators as Ref<PersonAccount>[],
|
||||
@ -1078,7 +1101,7 @@ async function createCollaboratorDoc (
|
||||
...(await ctx.with(
|
||||
'get-space-collabtxes',
|
||||
{},
|
||||
async () => await getSpaceCollabTxes(control, doc, tx, originTx, activityMessage, cache)
|
||||
async (ctx) => await getSpaceCollabTxes(ctx, control, doc, tx, originTx, activityMessage, cache)
|
||||
))
|
||||
)
|
||||
|
||||
@ -1100,6 +1123,8 @@ async function updateCollaboratorsMixin (
|
||||
|
||||
const res: Tx[] = []
|
||||
|
||||
const notificationControl = await getNotificationProviderControl(ctx, control)
|
||||
|
||||
if (tx.attributes.collaborators !== undefined) {
|
||||
const createTx = hierarchy.isDerived(tx.objectClass, core.class.AttachedDoc)
|
||||
? (
|
||||
@ -1142,7 +1167,7 @@ async function updateCollaboratorsMixin (
|
||||
for (const collab of tx.attributes.collaborators) {
|
||||
if (!prevCollabs.has(collab) && tx.modifiedBy !== collab) {
|
||||
for (const provider of providers) {
|
||||
if (await isAllowed(control, collab as Ref<PersonAccount>, type, provider)) {
|
||||
if (isAllowed(control, collab as Ref<PersonAccount>, type, provider, notificationControl)) {
|
||||
newCollabs.push(collab)
|
||||
break
|
||||
}
|
||||
@ -1161,17 +1186,15 @@ async function updateCollaboratorsMixin (
|
||||
{},
|
||||
async (ctx) => await getUsersInfo(ctx, [...newCollabs, originTx.modifiedBy] as Ref<PersonAccount>[], control)
|
||||
)
|
||||
const sender: SenderInfo = infos.find(({ _id }) => _id === originTx.modifiedBy) ?? { _id: originTx.modifiedBy }
|
||||
const sender: SenderInfo = infos.get(originTx.modifiedBy) ?? { _id: originTx.modifiedBy }
|
||||
|
||||
for (const collab of newCollabs) {
|
||||
const target = toReceiverInfo(
|
||||
hierarchy,
|
||||
infos.find(({ _id }) => _id === collab)
|
||||
)
|
||||
const target = toReceiverInfo(hierarchy, infos.get(collab))
|
||||
if (target === undefined) continue
|
||||
|
||||
for (const message of activityMessages) {
|
||||
await pushActivityInboxNotifications(
|
||||
ctx,
|
||||
originTx,
|
||||
control,
|
||||
res,
|
||||
@ -1225,7 +1248,7 @@ async function collectionCollabDoc (
|
||||
const collaborators = await ctx.with(
|
||||
'get-collaborators',
|
||||
{},
|
||||
async () => await getCollaborators(doc, control, tx, res)
|
||||
async (ctx) => await getCollaborators(ctx, doc, control, tx, res)
|
||||
)
|
||||
|
||||
res = res.concat(
|
||||
@ -1377,7 +1400,7 @@ async function updateCollaboratorDoc (
|
||||
const doc = await ctx.with(
|
||||
'find-doc',
|
||||
{ _class: tx.objectClass },
|
||||
async () =>
|
||||
async (ctx) =>
|
||||
cache.get(tx.objectId) ?? (await control.findAllCtx(ctx, tx.objectClass, { _id: tx.objectId }, { limit: 1 }))[0]
|
||||
)
|
||||
if (doc === undefined) return []
|
||||
@ -1388,7 +1411,7 @@ async function updateCollaboratorDoc (
|
||||
const collabsInfo = await ctx.with(
|
||||
'get-tx-collaborators',
|
||||
{},
|
||||
async () => await getTxCollabs(ctx, tx, control, doc)
|
||||
async (ctx) => await getTxCollabs(ctx, tx, control, doc)
|
||||
)
|
||||
|
||||
if (collabsInfo.added.length > 0) {
|
||||
@ -1403,7 +1426,7 @@ async function updateCollaboratorDoc (
|
||||
await ctx.with(
|
||||
'create-collab-docinfo',
|
||||
{},
|
||||
async () =>
|
||||
async (ctx) =>
|
||||
await createCollabDocInfo(
|
||||
ctx,
|
||||
collabsInfo.result as Ref<PersonAccount>[],
|
||||
@ -1421,7 +1444,7 @@ async function updateCollaboratorDoc (
|
||||
const collaborators = await ctx.with(
|
||||
'get-doc-collaborators',
|
||||
{},
|
||||
async () => await getDocCollaborators(ctx, doc, mixin, control)
|
||||
async (ctx) => await getDocCollaborators(ctx, doc, mixin, control)
|
||||
)
|
||||
res.push(getMixinTx(tx, control, collaborators))
|
||||
res = res.concat(
|
||||
@ -1442,11 +1465,11 @@ async function updateCollaboratorDoc (
|
||||
await ctx.with(
|
||||
'get-space-collabtxes',
|
||||
{},
|
||||
async () => await getSpaceCollabTxes(control, doc, tx, originTx, activityMessages, cache)
|
||||
async (ctx) => await getSpaceCollabTxes(ctx, control, doc, tx, originTx, activityMessages, cache)
|
||||
)
|
||||
)
|
||||
res = res.concat(
|
||||
await ctx.with('update-notify-context-space', {}, async () => await updateNotifyContextsSpace(control, tx))
|
||||
await ctx.with('update-notify-context-space', {}, async (ctx) => await updateNotifyContextsSpace(ctx, control, tx))
|
||||
)
|
||||
|
||||
return res
|
||||
@ -1546,7 +1569,7 @@ async function applyUserTxes (
|
||||
for (const [user, txs] of map.entries()) {
|
||||
const account =
|
||||
(cache.get(user) as PersonAccount) ??
|
||||
(await ctx.with('get-person-account', {}, async () => await getPersonAccountById(user, control)))
|
||||
ctx.withSync('get-person-account', {}, () => getPersonAccountById(user, control))
|
||||
|
||||
if (account !== undefined) {
|
||||
cache.set(account._id, account)
|
||||
@ -1604,15 +1627,15 @@ async function updateCollaborators (ctx: MeasureContext, control: TriggerControl
|
||||
const contexts = await control.findAll(notification.class.DocNotifyContext, { attachedTo: objectId })
|
||||
const addedInfo = await getUsersInfo(ctx, toAdd as Ref<PersonAccount>[], control)
|
||||
|
||||
for (const addedUser of addedInfo) {
|
||||
for (const addedUser of addedInfo.values()) {
|
||||
const info = toReceiverInfo(hierarchy, addedUser)
|
||||
if (info === undefined) continue
|
||||
const context = getDocNotifyContext(control, contexts, objectId, info._id)
|
||||
if (context !== undefined) continue
|
||||
await createNotifyContext(control, objectId, objectClass, objectSpace, info)
|
||||
await createNotifyContext(ctx, control, objectId, objectClass, objectSpace, info)
|
||||
}
|
||||
|
||||
await removeContexts(contexts, removedCollaborators as Ref<PersonAccount>[], control)
|
||||
await removeContexts(ctx, contexts, removedCollaborators as Ref<PersonAccount>[], control)
|
||||
|
||||
return res
|
||||
}
|
||||
@ -1630,7 +1653,7 @@ export async function createCollaboratorNotifications (
|
||||
return await ctx.with(
|
||||
'updateDerivedCollaborators',
|
||||
{},
|
||||
async () => await updateCollaborators(ctx, control, TxProcessor.extractTx(tx) as TxCUD<Doc>)
|
||||
async (ctx) => await updateCollaborators(ctx, control, TxProcessor.extractTx(tx) as TxCUD<Doc>)
|
||||
)
|
||||
}
|
||||
|
||||
@ -1643,7 +1666,7 @@ export async function createCollaboratorNotifications (
|
||||
const res = await ctx.with(
|
||||
'createCollaboratorDoc',
|
||||
{},
|
||||
async () =>
|
||||
async (ctx) =>
|
||||
await createCollaboratorDoc(ctx, tx as TxCreateDoc<Doc>, control, activityMessages, originTx ?? tx, cache)
|
||||
)
|
||||
|
||||
@ -1654,14 +1677,14 @@ export async function createCollaboratorNotifications (
|
||||
let res = await ctx.with(
|
||||
'updateCollaboratorDoc',
|
||||
{},
|
||||
async () =>
|
||||
async (ctx) =>
|
||||
await updateCollaboratorDoc(ctx, tx as TxUpdateDoc<Doc>, control, originTx ?? tx, activityMessages, cache)
|
||||
)
|
||||
res = res.concat(
|
||||
await ctx.with(
|
||||
'updateCollaboratorMixin',
|
||||
{},
|
||||
async () =>
|
||||
async (ctx) =>
|
||||
await updateCollaboratorsMixin(
|
||||
ctx,
|
||||
tx as TxMixin<Doc, Collaborators>,
|
||||
@ -1703,6 +1726,7 @@ export async function removeDocInboxNotifications (_id: Ref<ActivityMessage>, co
|
||||
}
|
||||
|
||||
export async function getCollaborators (
|
||||
ctx: MeasureContext,
|
||||
doc: Doc,
|
||||
control: TriggerControl,
|
||||
tx: TxCUD<Doc>,
|
||||
@ -1717,7 +1741,7 @@ export async function getCollaborators (
|
||||
if (control.hierarchy.hasMixin(doc, notification.mixin.Collaborators)) {
|
||||
return control.hierarchy.as(doc, notification.mixin.Collaborators).collaborators
|
||||
} else {
|
||||
const collaborators = await getDocCollaborators(control.ctx, doc, mixin, control)
|
||||
const collaborators = await getDocCollaborators(ctx, doc, mixin, control)
|
||||
|
||||
res.push(getMixinTx(tx, control, collaborators))
|
||||
return collaborators
|
||||
@ -1821,7 +1845,7 @@ export default async () => ({
|
||||
OnDocRemove
|
||||
},
|
||||
function: {
|
||||
IsUserInFieldValue: isUserInFieldValue,
|
||||
IsUserEmployeeInFieldValue: isUserEmployeeInFieldValue
|
||||
IsUserInFieldValueTypeMatch: isUserInFieldValueTypeMatch,
|
||||
IsUserEmployeeInFieldValueTypeMatch: isUserEmployeeInFieldValueTypeMatch
|
||||
}
|
||||
})
|
||||
|
@ -12,24 +12,19 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
import notification, {
|
||||
BaseNotificationType,
|
||||
Collaborators,
|
||||
CommonNotificationType,
|
||||
NotificationContent,
|
||||
notificationId,
|
||||
NotificationProvider,
|
||||
NotificationType
|
||||
} from '@hcengineering/notification'
|
||||
import serverCore, { TriggerControl } from '@hcengineering/server-core'
|
||||
import activity, { ActivityMessage, DocUpdateMessage } from '@hcengineering/activity'
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import contact, { formatName, PersonAccount } from '@hcengineering/contact'
|
||||
import chunter, { ChatMessage } from '@hcengineering/chunter'
|
||||
import contact, { formatName, PersonAccount, type Person, type PersonSpace } from '@hcengineering/contact'
|
||||
import core, {
|
||||
Account,
|
||||
Class,
|
||||
concatLink,
|
||||
Doc,
|
||||
DocumentUpdate,
|
||||
groupByArray,
|
||||
Hierarchy,
|
||||
Markup,
|
||||
matchQuery,
|
||||
MixinUpdate,
|
||||
Ref,
|
||||
@ -42,11 +37,23 @@ import core, {
|
||||
TxProcessor,
|
||||
TxRemoveDoc,
|
||||
TxUpdateDoc,
|
||||
type IdMap,
|
||||
type MeasureContext,
|
||||
Markup,
|
||||
concatLink
|
||||
type WithLookup
|
||||
} from '@hcengineering/core'
|
||||
import notification, {
|
||||
BaseNotificationType,
|
||||
Collaborators,
|
||||
CommonNotificationType,
|
||||
NotificationContent,
|
||||
notificationId,
|
||||
NotificationProvider,
|
||||
NotificationType,
|
||||
type NotificationProviderSetting,
|
||||
type NotificationTypeSetting
|
||||
} from '@hcengineering/notification'
|
||||
import { getMetadata, getResource, IntlString, translate } from '@hcengineering/platform'
|
||||
import serverCore, { TriggerControl } from '@hcengineering/server-core'
|
||||
import serverNotification, {
|
||||
getPersonAccountById,
|
||||
HTMLPresenter,
|
||||
@ -55,28 +62,26 @@ import serverNotification, {
|
||||
SenderInfo,
|
||||
TextPresenter
|
||||
} from '@hcengineering/server-notification'
|
||||
import activity, { ActivityMessage, DocUpdateMessage } from '@hcengineering/activity'
|
||||
import serverView from '@hcengineering/server-view'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
import { encodeObjectURI } from '@hcengineering/view'
|
||||
import chunter, { ChatMessage } from '@hcengineering/chunter'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
|
||||
import { NotifyResult } from './types'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function isUserEmployeeInFieldValue (
|
||||
export function isUserEmployeeInFieldValueTypeMatch (
|
||||
_: Tx,
|
||||
doc: Doc,
|
||||
user: Ref<Account>,
|
||||
user: Ref<Account>[],
|
||||
type: NotificationType,
|
||||
control: TriggerControl
|
||||
): Promise<boolean> {
|
||||
): boolean {
|
||||
if (type.field === undefined) return false
|
||||
const value = (doc as any)[type.field]
|
||||
if (value == null) return false
|
||||
const employee = (await control.modelDb.findAll(contact.class.PersonAccount, { _id: user as Ref<PersonAccount> }))[0]
|
||||
const employee = control.modelDb.findAllSync(contact.class.PersonAccount, { _id: user[0] as Ref<PersonAccount> })[0]
|
||||
if (employee === undefined) return false
|
||||
if (Array.isArray(value)) {
|
||||
return value.includes(employee.person)
|
||||
@ -88,12 +93,7 @@ export async function isUserEmployeeInFieldValue (
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function isUserInFieldValue (
|
||||
_: Tx,
|
||||
doc: Doc,
|
||||
user: Ref<Account>,
|
||||
type: NotificationType
|
||||
): Promise<boolean> {
|
||||
export function isUserInFieldValueTypeMatch (_: Tx, doc: Doc, user: Ref<Account>[], type: NotificationType): boolean {
|
||||
if (type.field === undefined) {
|
||||
return false
|
||||
}
|
||||
@ -104,7 +104,7 @@ export async function isUserInFieldValue (
|
||||
return false
|
||||
}
|
||||
|
||||
return Array.isArray(value) ? value.includes(user) : value === user
|
||||
return Array.isArray(value) ? user.some((it) => value.includes(it)) : user.includes(value)
|
||||
}
|
||||
|
||||
export function replaceAll (str: string, find: string, replace: string): string {
|
||||
@ -117,8 +117,9 @@ function escapeRegExp (str: string): string {
|
||||
|
||||
export async function shouldNotifyCommon (
|
||||
control: TriggerControl,
|
||||
user: Ref<Account>,
|
||||
typeId: Ref<CommonNotificationType>
|
||||
user: Ref<Account>[],
|
||||
typeId: Ref<CommonNotificationType>,
|
||||
notificationControl: NotificationProviderControl
|
||||
): Promise<NotifyResult> {
|
||||
const type = (await control.modelDb.findAll(notification.class.CommonNotificationType, { _id: typeId }))[0]
|
||||
|
||||
@ -130,7 +131,9 @@ export async function shouldNotifyCommon (
|
||||
const providers = await control.modelDb.findAll(notification.class.NotificationProvider, {})
|
||||
|
||||
for (const provider of providers) {
|
||||
const allowed = await isAllowed(control, user as Ref<PersonAccount>, type, provider)
|
||||
const allowed = user.some((user) =>
|
||||
isAllowed(control, user as Ref<PersonAccount>, type, provider, notificationControl)
|
||||
)
|
||||
if (allowed) {
|
||||
const cur = result.get(provider._id) ?? []
|
||||
result.set(provider._id, [...cur, type])
|
||||
@ -140,17 +143,15 @@ export async function shouldNotifyCommon (
|
||||
return result
|
||||
}
|
||||
|
||||
export async function isAllowed (
|
||||
export function isAllowed (
|
||||
control: TriggerControl,
|
||||
receiver: Ref<PersonAccount>,
|
||||
type: BaseNotificationType,
|
||||
provider: NotificationProvider
|
||||
): Promise<boolean> {
|
||||
const providersSettings = await control.queryFind(control.ctx, notification.class.NotificationProviderSetting, {
|
||||
space: core.space.Workspace
|
||||
})
|
||||
const providerSetting = providersSettings.find(
|
||||
({ attachedTo, modifiedBy }) => attachedTo === provider._id && modifiedBy === receiver
|
||||
provider: NotificationProvider,
|
||||
notificationControl: NotificationProviderControl
|
||||
): boolean {
|
||||
const providerSetting = (notificationControl.byProvider.get(provider._id) ?? []).find(
|
||||
({ attachedTo, modifiedBy }) => modifiedBy === receiver
|
||||
)
|
||||
|
||||
if (providerSetting !== undefined && !providerSetting.enabled) {
|
||||
@ -161,17 +162,13 @@ export async function isAllowed (
|
||||
return false
|
||||
}
|
||||
|
||||
const providerDefaults = await control.modelDb.findAll(notification.class.NotificationProviderDefaults, {})
|
||||
const providerDefaults = control.modelDb.findAllSync(notification.class.NotificationProviderDefaults, {})
|
||||
|
||||
if (providerDefaults.some((it) => it.provider === provider._id && it.ignoredTypes.includes(type._id))) {
|
||||
return false
|
||||
}
|
||||
|
||||
const typesSettings = await control.queryFind(control.ctx, notification.class.NotificationTypeSetting, {
|
||||
space: core.space.Workspace
|
||||
})
|
||||
const setting = typesSettings.find(
|
||||
(it) => it.attachedTo === provider._id && it.type === type._id && it.modifiedBy === receiver
|
||||
const setting = (notificationControl.settingsByProvider.get(provider._id) ?? []).find(
|
||||
(it) => it.type === type._id && it.modifiedBy === receiver
|
||||
)
|
||||
|
||||
if (setting !== undefined) {
|
||||
@ -192,29 +189,23 @@ export async function isShouldNotifyTx (
|
||||
tx: TxCUD<Doc>,
|
||||
originTx: TxCUD<Doc>,
|
||||
object: Doc,
|
||||
user: PersonAccount,
|
||||
user: PersonAccount[],
|
||||
isOwn: boolean,
|
||||
isSpace: boolean,
|
||||
notificationControl: NotificationProviderControl,
|
||||
docUpdateMessage?: DocUpdateMessage
|
||||
): Promise<NotifyResult> {
|
||||
const types = await getMatchedTypes(
|
||||
control,
|
||||
tx,
|
||||
originTx,
|
||||
isOwn,
|
||||
isSpace,
|
||||
docUpdateMessage?.attributeUpdates?.attrKey
|
||||
)
|
||||
const modifiedAccount = await getPersonAccountById(tx.modifiedBy, control)
|
||||
const types = getMatchedTypes(control, tx, originTx, isOwn, isSpace, docUpdateMessage?.attributeUpdates?.attrKey)
|
||||
const modifiedAccount = getPersonAccountById(tx.modifiedBy, control)
|
||||
const result = new Map<Ref<NotificationProvider>, BaseNotificationType[]>()
|
||||
const providers = await control.modelDb.findAll(notification.class.NotificationProvider, {})
|
||||
const providers = control.modelDb.findAllSync(notification.class.NotificationProvider, {})
|
||||
|
||||
for (const type of types) {
|
||||
if (
|
||||
type.allowedForAuthor !== true &&
|
||||
(tx.modifiedBy === user._id ||
|
||||
(user.some((it) => tx.modifiedBy === it._id) ||
|
||||
// Also check if we have different account for same user.
|
||||
(user?.person !== undefined && user?.person === modifiedAccount?.person))
|
||||
(user?.[0].person !== undefined && user?.[0]?.person === modifiedAccount?.person))
|
||||
) {
|
||||
continue
|
||||
}
|
||||
@ -222,12 +213,18 @@ export async function isShouldNotifyTx (
|
||||
const mixin = control.hierarchy.as(type, serverNotification.mixin.TypeMatch)
|
||||
if (mixin.func !== undefined) {
|
||||
const f = await getResource(mixin.func)
|
||||
const res = await f(tx, object, user._id, type, control)
|
||||
const res = f(
|
||||
tx,
|
||||
object,
|
||||
user.map((it) => it._id),
|
||||
type,
|
||||
control
|
||||
)
|
||||
if (!res) continue
|
||||
}
|
||||
}
|
||||
for (const provider of providers) {
|
||||
const allowed = await isAllowed(control, user._id, type, provider)
|
||||
const allowed = user.some((it) => isAllowed(control, it._id, type, provider, notificationControl))
|
||||
|
||||
if (allowed) {
|
||||
const cur = result.get(provider._id) ?? []
|
||||
@ -239,17 +236,17 @@ export async function isShouldNotifyTx (
|
||||
return result
|
||||
}
|
||||
|
||||
async function getMatchedTypes (
|
||||
function getMatchedTypes (
|
||||
control: TriggerControl,
|
||||
tx: TxCUD<Doc>,
|
||||
originTx: TxCUD<Doc>,
|
||||
isOwn: boolean,
|
||||
isSpace: boolean,
|
||||
field?: string
|
||||
): Promise<NotificationType[]> {
|
||||
const allTypes = (
|
||||
await control.modelDb.findAll(notification.class.NotificationType, { ...(field !== undefined ? { field } : {}) })
|
||||
).filter((p) => (isSpace ? p.spaceSubscribe === true : p.spaceSubscribe !== true))
|
||||
): NotificationType[] {
|
||||
const allTypes = control.modelDb
|
||||
.findAllSync(notification.class.NotificationType, { ...(field !== undefined ? { field } : {}) })
|
||||
.filter((p) => (isSpace ? p.spaceSubscribe === true : p.spaceSubscribe !== true))
|
||||
const filtered: NotificationType[] = []
|
||||
for (const type of allTypes) {
|
||||
if (isTypeMatched(control, type, tx, originTx, isOwn)) {
|
||||
@ -300,6 +297,7 @@ function fieldUpdated (field: string, ops: DocumentUpdate<Doc> | MixinUpdate<Doc
|
||||
}
|
||||
|
||||
export async function updateNotifyContextsSpace (
|
||||
ctx: MeasureContext,
|
||||
control: TriggerControl,
|
||||
tx: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>
|
||||
): Promise<Tx[]> {
|
||||
@ -312,7 +310,7 @@ export async function updateNotifyContextsSpace (
|
||||
return []
|
||||
}
|
||||
|
||||
const notifyContexts = await control.findAll(notification.class.DocNotifyContext, { objectId: tx.objectId })
|
||||
const notifyContexts = await control.findAllCtx(ctx, notification.class.DocNotifyContext, { objectId: tx.objectId })
|
||||
|
||||
return notifyContexts.map((value) =>
|
||||
control.txFactory.createTxUpdateDoc(value._class, value.space, value._id, {
|
||||
@ -421,7 +419,7 @@ function getNotificationPresenter (_class: Ref<Class<Doc>>, hierarchy: Hierarchy
|
||||
|
||||
export async function getNotificationContent (
|
||||
originTx: TxCUD<Doc>,
|
||||
targetUser: PersonAccount,
|
||||
targetUser: PersonAccount[],
|
||||
sender: SenderInfo,
|
||||
object: Doc,
|
||||
control: TriggerControl
|
||||
@ -440,7 +438,7 @@ export async function getNotificationContent (
|
||||
|
||||
if (notificationPresenter !== undefined) {
|
||||
const getFuillfillmentParams = await getResource(notificationPresenter.presenter)
|
||||
const updateParams = await getFuillfillmentParams(object, originTx, targetUser._id, control)
|
||||
const updateParams = await getFuillfillmentParams(object, originTx, targetUser[0]._id, control)
|
||||
title = updateParams.title
|
||||
body = updateParams.body
|
||||
data = updateParams.data
|
||||
@ -474,33 +472,57 @@ export async function getUsersInfo (
|
||||
ctx: MeasureContext,
|
||||
ids: Ref<PersonAccount>[],
|
||||
control: TriggerControl
|
||||
): Promise<(ReceiverInfo | SenderInfo)[]> {
|
||||
if (ids.length === 0) return []
|
||||
const accounts = await control.modelDb.findAll(contact.class.PersonAccount, { _id: { $in: ids } })
|
||||
): Promise<Map<Ref<Account>, ReceiverInfo | SenderInfo>> {
|
||||
if (ids.length === 0) return new Map()
|
||||
const accounts = control.modelDb.findAllSync(contact.class.PersonAccount, { _id: { $in: ids } })
|
||||
|
||||
const personIds = accounts.map((it) => it.person)
|
||||
const personIdsMap = new Set(personIds)
|
||||
const accountById = toIdMap(accounts)
|
||||
const persons = toIdMap(
|
||||
await ctx.with(
|
||||
'find-persons',
|
||||
{},
|
||||
async () => await control.findAll(contact.class.Person, { _id: { $in: personIds } })
|
||||
const persons: IdMap<WithLookup<Person>> = new Map()
|
||||
const spaces = new Map<Ref<Person>, PersonSpace>()
|
||||
|
||||
const p = await ctx.with('find-persons', {}, async (ctx) =>
|
||||
(await control.queryFind(ctx, contact.mixin.Employee, { active: { $in: [true, false] } }, {})).filter((it) =>
|
||||
personIdsMap.has(it._id)
|
||||
)
|
||||
)
|
||||
const spaces = await ctx.with('find-person-spaces', {}, async () => {
|
||||
const res = await control.findAll(contact.class.PersonSpace, { person: { $in: personIds } })
|
||||
for (const pp of p) {
|
||||
persons.set(pp._id, pp)
|
||||
}
|
||||
|
||||
return new Map(res.map((s) => [s.person, s]))
|
||||
})
|
||||
const nonEmployee = personIds.filter((it) => !persons.has(it))
|
||||
|
||||
return ids.map((_id) => {
|
||||
const account = accountById.get(_id)
|
||||
return {
|
||||
_id,
|
||||
account,
|
||||
person: account !== undefined ? persons.get(account.person) : undefined,
|
||||
space: account !== undefined ? spaces.get(account.person)?._id : undefined
|
||||
}
|
||||
})
|
||||
const p2 = await ctx.with('find-persons', {}, async (ctx) =>
|
||||
(await control.queryFind(ctx, contact.class.Person, { _id: { $in: Array.from(nonEmployee) } }, {})).filter((it) =>
|
||||
personIdsMap.has(it._id)
|
||||
)
|
||||
)
|
||||
for (const pp of p2) {
|
||||
persons.set(pp._id, pp)
|
||||
}
|
||||
|
||||
const res = await ctx.with('find-person-spaces', {}, async (ctx) =>
|
||||
(await control.queryFind(ctx, contact.class.PersonSpace, {}, {})).filter((it) => personIdsMap.has(it.person))
|
||||
)
|
||||
for (const r of res) {
|
||||
spaces.set(r.person, r)
|
||||
}
|
||||
|
||||
return new Map(
|
||||
ids.map((_id) => {
|
||||
const account = accountById.get(_id)
|
||||
return [
|
||||
_id,
|
||||
{
|
||||
_id,
|
||||
account,
|
||||
person: account !== undefined ? persons.get(account.person) : undefined,
|
||||
space: account !== undefined ? spaces.get(account.person)?._id : undefined
|
||||
}
|
||||
]
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export function toReceiverInfo (hierarchy: Hierarchy, info?: SenderInfo | ReceiverInfo): ReceiverInfo | undefined {
|
||||
@ -611,3 +633,36 @@ export async function messageToMarkup (control: TriggerControl, message: Activit
|
||||
|
||||
return undefined
|
||||
}
|
||||
export class NotificationProviderControl {
|
||||
public byProvider: Map<Ref<NotificationProvider>, NotificationProviderSetting[]>
|
||||
public settingsByProvider: Map<Ref<NotificationProvider>, NotificationTypeSetting[]>
|
||||
constructor (
|
||||
readonly providersSettings: NotificationProviderSetting[],
|
||||
readonly typesSettings: NotificationTypeSetting[]
|
||||
) {
|
||||
this.byProvider = groupByArray(providersSettings, (it) => it.attachedTo)
|
||||
this.settingsByProvider = groupByArray(typesSettings, (it) => it.attachedTo)
|
||||
}
|
||||
}
|
||||
const notificationProvidersKey = 'notification_provider_settings'
|
||||
const typesSettingsKey = 'notification_type_settings'
|
||||
export async function getNotificationProviderControl (
|
||||
ctx: MeasureContext,
|
||||
control: TriggerControl
|
||||
): Promise<NotificationProviderControl> {
|
||||
let providersSettings: NotificationProviderSetting[] = control.contextCache.get(notificationProvidersKey)
|
||||
if (providersSettings === undefined) {
|
||||
providersSettings = await control.queryFind(ctx, notification.class.NotificationProviderSetting, {
|
||||
space: core.space.Workspace
|
||||
})
|
||||
control.contextCache.set(notificationProvidersKey, providersSettings)
|
||||
}
|
||||
let typesSettings: NotificationTypeSetting[] = control.contextCache.get(typesSettingsKey)
|
||||
if (typesSettings === undefined) {
|
||||
typesSettings = await control.queryFind(ctx, notification.class.NotificationTypeSetting, {
|
||||
space: core.space.Workspace
|
||||
})
|
||||
control.contextCache.set(typesSettingsKey, typesSettings)
|
||||
}
|
||||
return new NotificationProviderControl(providersSettings, typesSettings)
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { ActivityMessage } from '@hcengineering/activity'
|
||||
import contact, { Employee, Person, PersonAccount, PersonSpace } from '@hcengineering/contact'
|
||||
import { Account, Class, Doc, Mixin, Ref, Tx, TxCUD } from '@hcengineering/core'
|
||||
import {
|
||||
@ -25,7 +26,6 @@ import {
|
||||
} from '@hcengineering/notification'
|
||||
import { Metadata, Plugin, Resource, plugin } from '@hcengineering/platform'
|
||||
import type { TriggerControl, TriggerFunc } from '@hcengineering/server-core'
|
||||
import { ActivityMessage } from '@hcengineering/activity'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -35,37 +35,13 @@ export const serverNotificationId = 'server-notification' as Plugin
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function getPersonAccount (
|
||||
person: Ref<Person>,
|
||||
control: TriggerControl
|
||||
): Promise<PersonAccount | undefined> {
|
||||
const account = (
|
||||
await control.modelDb.findAll(
|
||||
contact.class.PersonAccount,
|
||||
{
|
||||
person
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
return account
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function getPersonAccountById (
|
||||
_id: Ref<Account>,
|
||||
control: TriggerControl
|
||||
): Promise<PersonAccount | undefined> {
|
||||
const account = (
|
||||
await control.modelDb.findAll(
|
||||
contact.class.PersonAccount,
|
||||
{
|
||||
_id: _id as Ref<PersonAccount>
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
export function getPersonAccountById (_id: Ref<Account>, control: TriggerControl): PersonAccount | undefined {
|
||||
const account = control.modelDb.findAllSync(
|
||||
contact.class.PersonAccount,
|
||||
{
|
||||
_id: _id as Ref<PersonAccount>
|
||||
},
|
||||
{ limit: 1 }
|
||||
)[0]
|
||||
return account
|
||||
}
|
||||
@ -109,7 +85,7 @@ export interface TextPresenter<T extends Doc = any> extends Class<T> {
|
||||
* @public
|
||||
*/
|
||||
export type TypeMatchFunc = Resource<
|
||||
(tx: Tx, doc: Doc, user: Ref<Account>, type: NotificationType, control: TriggerControl) => Promise<boolean>
|
||||
(tx: Tx, doc: Doc, user: Ref<Account>[], type: NotificationType, control: TriggerControl) => boolean
|
||||
>
|
||||
|
||||
/**
|
||||
@ -192,7 +168,7 @@ export default plugin(serverNotificationId, {
|
||||
OnDocRemove: '' as Resource<TriggerFunc>
|
||||
},
|
||||
function: {
|
||||
IsUserInFieldValue: '' as TypeMatchFunc,
|
||||
IsUserEmployeeInFieldValue: '' as TypeMatchFunc
|
||||
IsUserInFieldValueTypeMatch: '' as TypeMatchFunc,
|
||||
IsUserEmployeeInFieldValueTypeMatch: '' as TypeMatchFunc
|
||||
}
|
||||
})
|
||||
|
@ -13,7 +13,17 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import core, { Doc, Tx, TxCUD, TxCollectionCUD, TxCreateDoc, TxUpdateDoc, TxProcessor, Ref } from '@hcengineering/core'
|
||||
import core, {
|
||||
Doc,
|
||||
Tx,
|
||||
TxCUD,
|
||||
TxCollectionCUD,
|
||||
TxCreateDoc,
|
||||
TxUpdateDoc,
|
||||
TxProcessor,
|
||||
Ref,
|
||||
type MeasureContext
|
||||
} from '@hcengineering/core'
|
||||
import request, { Request, RequestStatus } from '@hcengineering/request'
|
||||
import { getResource, translate } from '@hcengineering/platform'
|
||||
import type { TriggerControl } from '@hcengineering/server-core'
|
||||
@ -25,7 +35,8 @@ import {
|
||||
getCollaborators,
|
||||
getTextPresenter,
|
||||
getUsersInfo,
|
||||
toReceiverInfo
|
||||
toReceiverInfo,
|
||||
getNotificationProviderControl
|
||||
} from '@hcengineering/server-notification-resources'
|
||||
import { PersonAccount } from '@hcengineering/contact'
|
||||
|
||||
@ -46,7 +57,7 @@ export async function OnRequest (tx: Tx, control: TriggerControl): Promise<Tx[]>
|
||||
|
||||
let res: Tx[] = []
|
||||
|
||||
res = res.concat(await getRequestNotificationTx(ptx, control))
|
||||
res = res.concat(await getRequestNotificationTx(control.ctx, ptx, control))
|
||||
|
||||
if (ptx.tx._class === core.class.TxUpdateDoc) {
|
||||
res = res.concat(await OnRequestUpdate(ptx, control))
|
||||
@ -121,7 +132,11 @@ async function getRequest (tx: TxCUD<Request>, control: TriggerControl): Promise
|
||||
}
|
||||
|
||||
// We need request-specific logic to attach a activity message on request create/update to parent, but use request collaborators for notifications
|
||||
async function getRequestNotificationTx (tx: TxCollectionCUD<Doc, Request>, control: TriggerControl): Promise<Tx[]> {
|
||||
async function getRequestNotificationTx (
|
||||
ctx: MeasureContext,
|
||||
tx: TxCollectionCUD<Doc, Request>,
|
||||
control: TriggerControl
|
||||
): Promise<Tx[]> {
|
||||
const request = await getRequest(tx.tx, control)
|
||||
|
||||
if (request === undefined) return []
|
||||
@ -140,7 +155,7 @@ async function getRequestNotificationTx (tx: TxCollectionCUD<Doc, Request>, cont
|
||||
const messages = messagesTxes.map((messageTx) =>
|
||||
TxProcessor.createDoc2Doc(messageTx.tx as TxCreateDoc<DocUpdateMessage>)
|
||||
)
|
||||
const collaborators = await getCollaborators(request, control, tx.tx, res)
|
||||
const collaborators = await getCollaborators(control.ctx, request, control, tx.tx, res)
|
||||
|
||||
if (collaborators.length === 0) return res
|
||||
|
||||
@ -148,18 +163,18 @@ async function getRequestNotificationTx (tx: TxCollectionCUD<Doc, Request>, cont
|
||||
objectId: doc._id
|
||||
})
|
||||
const usersInfo = await getUsersInfo(control.ctx, [...collaborators, tx.modifiedBy] as Ref<PersonAccount>[], control)
|
||||
const senderInfo = usersInfo.find(({ _id }) => _id === tx.modifiedBy) ?? {
|
||||
const senderInfo = usersInfo.get(tx.modifiedBy) ?? {
|
||||
_id: tx.modifiedBy
|
||||
}
|
||||
|
||||
const notificationControl = await getNotificationProviderControl(ctx, control)
|
||||
|
||||
for (const target of collaborators) {
|
||||
const targetInfo = toReceiverInfo(
|
||||
control.hierarchy,
|
||||
usersInfo.find(({ _id }) => _id === target)
|
||||
)
|
||||
const targetInfo = toReceiverInfo(control.hierarchy, usersInfo.get(target))
|
||||
if (targetInfo === undefined) continue
|
||||
|
||||
const txes = await getNotificationTxes(
|
||||
ctx,
|
||||
control,
|
||||
request,
|
||||
tx.tx,
|
||||
@ -168,7 +183,8 @@ async function getRequestNotificationTx (tx: TxCollectionCUD<Doc, Request>, cont
|
||||
senderInfo,
|
||||
{ isOwn: true, isSpace: false, shouldUpdateTimestamp: true },
|
||||
notifyContexts,
|
||||
messages
|
||||
messages,
|
||||
notificationControl
|
||||
)
|
||||
res.push(...txes)
|
||||
}
|
||||
|
@ -90,13 +90,13 @@ export async function OnMessageCreate (tx: Tx, control: TriggerControl): Promise
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function IsIncomingMessage (
|
||||
export function IsIncomingMessageTypeMatch (
|
||||
tx: Tx,
|
||||
doc: Doc,
|
||||
user: Ref<Account>,
|
||||
user: Ref<Account>[],
|
||||
type: NotificationType,
|
||||
control: TriggerControl
|
||||
): Promise<boolean> {
|
||||
): boolean {
|
||||
const message = TxProcessor.createDoc2Doc(TxProcessor.extractTx(tx) as TxCreateDoc<TelegramMessage>)
|
||||
return message.incoming && message.sendOn > (doc.createdOn ?? doc.modifiedOn)
|
||||
}
|
||||
@ -280,7 +280,7 @@ export default async () => ({
|
||||
OnMessageCreate
|
||||
},
|
||||
function: {
|
||||
IsIncomingMessage,
|
||||
IsIncomingMessageTypeMatch,
|
||||
FindMessages,
|
||||
GetCurrentEmployeeTG,
|
||||
GetIntegrationOwnerTG,
|
||||
|
@ -35,7 +35,7 @@ export default plugin(serverTelegramId, {
|
||||
OnMessageCreate: '' as Resource<TriggerFunc>
|
||||
},
|
||||
function: {
|
||||
IsIncomingMessage: '' as TypeMatchFunc,
|
||||
IsIncomingMessageTypeMatch: '' as TypeMatchFunc,
|
||||
FindMessages: '' as Resource<ObjectDDParticipantFunc>,
|
||||
GetCurrentEmployeeTG: '' as Resource<TemplateFieldServerFunc>,
|
||||
GetIntegrationOwnerTG: '' as Resource<TemplateFieldServerFunc>,
|
||||
|
@ -34,17 +34,18 @@ import core, {
|
||||
import notification, { CommonInboxNotification } from '@hcengineering/notification'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import type { TriggerControl } from '@hcengineering/server-core'
|
||||
import { ReceiverInfo, SenderInfo } from '@hcengineering/server-notification'
|
||||
import {
|
||||
getCommonNotificationTxes,
|
||||
getNotificationContent,
|
||||
getNotificationProviderControl,
|
||||
isShouldNotifyTx
|
||||
} from '@hcengineering/server-notification-resources'
|
||||
import serverTime, { OnToDo, ToDoFactory } from '@hcengineering/server-time'
|
||||
import task, { makeRank } from '@hcengineering/task'
|
||||
import { jsonToMarkup, nodeDoc, nodeParagraph, nodeText } from '@hcengineering/text'
|
||||
import tracker, { Issue, IssueStatus, Project, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import serverTime, { OnToDo, ToDoFactory } from '@hcengineering/server-time'
|
||||
import time, { ProjectToDo, ToDo, ToDoPriority, TodoAutomationHelper, WorkSlot } from '@hcengineering/time'
|
||||
import { ReceiverInfo, SenderInfo } from '@hcengineering/server-notification'
|
||||
import tracker, { Issue, IssueStatus, Project, TimeSpendReport } from '@hcengineering/tracker'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -179,9 +180,9 @@ export async function OnToDoCreate (tx: TxCUD<Doc>, control: TriggerControl): Pr
|
||||
}
|
||||
|
||||
const todo = TxProcessor.createDoc2Doc(createTx)
|
||||
const account = await getPersonAccount(todo.user, control)
|
||||
const account = control.modelDb.getAccountByPersonId(todo.user) as PersonAccount[]
|
||||
|
||||
if (account === undefined) {
|
||||
if (account.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
@ -189,23 +190,24 @@ export async function OnToDoCreate (tx: TxCUD<Doc>, control: TriggerControl): Pr
|
||||
if (object === undefined) return []
|
||||
|
||||
const person = (
|
||||
await control.findAll(contact.mixin.Employee, { _id: account.person as Ref<Employee>, active: true }, { limit: 1 })
|
||||
await control.findAll(contact.mixin.Employee, { _id: todo.user as Ref<Employee>, active: true }, { limit: 1 })
|
||||
)[0]
|
||||
if (person === undefined) return []
|
||||
|
||||
const personSpace = (await control.findAll(contact.class.PersonSpace, { person: account.person }, { limit: 1 }))[0]
|
||||
const personSpace = (await control.findAll(contact.class.PersonSpace, { person: todo.user }, { limit: 1 }))[0]
|
||||
if (personSpace === undefined) return []
|
||||
|
||||
// TODO: Select a proper account
|
||||
const receiverInfo: ReceiverInfo = {
|
||||
_id: account._id,
|
||||
account,
|
||||
_id: account[0]._id,
|
||||
account: account[0],
|
||||
person,
|
||||
space: personSpace._id
|
||||
}
|
||||
|
||||
const senderAccount = await control.modelDb.findOne(contact.class.PersonAccount, {
|
||||
const senderAccount = control.modelDb.findAllSync(contact.class.PersonAccount, {
|
||||
_id: tx.modifiedBy as Ref<PersonAccount>
|
||||
})
|
||||
})[0]
|
||||
const senderPerson =
|
||||
senderAccount !== undefined
|
||||
? (await control.findAll(contact.class.Person, { _id: senderAccount.person }))[0]
|
||||
@ -216,8 +218,8 @@ export async function OnToDoCreate (tx: TxCUD<Doc>, control: TriggerControl): Pr
|
||||
account: senderAccount,
|
||||
person: senderPerson
|
||||
}
|
||||
|
||||
const notifyResult = await isShouldNotifyTx(control, createTx, tx, todo, account, true, false)
|
||||
const notificationControl = await getNotificationProviderControl(control.ctx, control)
|
||||
const notifyResult = await isShouldNotifyTx(control, createTx, tx, todo, account, true, false, notificationControl)
|
||||
const content = await getNotificationContent(tx, account, senderInfo, todo, control)
|
||||
const data: Partial<Data<CommonInboxNotification>> = {
|
||||
...content,
|
||||
@ -229,6 +231,7 @@ export async function OnToDoCreate (tx: TxCUD<Doc>, control: TriggerControl): Pr
|
||||
}
|
||||
|
||||
const txes = await getCommonNotificationTxes(
|
||||
control.ctx,
|
||||
control,
|
||||
object,
|
||||
data,
|
||||
@ -465,31 +468,18 @@ async function createIssueHandler (issue: Issue, control: TriggerControl): Promi
|
||||
return []
|
||||
}
|
||||
|
||||
async function getPersonAccount (person: Ref<Person>, control: TriggerControl): Promise<PersonAccount | undefined> {
|
||||
const account = (
|
||||
await control.modelDb.findAll(
|
||||
contact.class.PersonAccount,
|
||||
{
|
||||
person
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
return account
|
||||
}
|
||||
|
||||
async function getIssueToDoData (
|
||||
issue: Issue,
|
||||
user: Ref<Person>,
|
||||
control: TriggerControl
|
||||
): Promise<AttachedData<ProjectToDo> | undefined> {
|
||||
const acc = await getPersonAccount(user, control)
|
||||
if (acc === undefined) return
|
||||
const acc = control.modelDb.getAccountByPersonId(user) as PersonAccount[]
|
||||
if (acc.length === 0) return
|
||||
const firstTodoItem = (
|
||||
await control.findAll(
|
||||
time.class.ToDo,
|
||||
{
|
||||
user: acc.person,
|
||||
user: { $in: acc.map((it) => it.person) },
|
||||
doneOn: null
|
||||
},
|
||||
{
|
||||
@ -506,7 +496,7 @@ async function getIssueToDoData (
|
||||
priority: ToDoPriority.NoPriority,
|
||||
visibility: 'public',
|
||||
title: issue.title,
|
||||
user: acc.person,
|
||||
user,
|
||||
rank
|
||||
}
|
||||
return data
|
||||
|
@ -13,25 +13,25 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import contact, { type Employee, type PersonAccount } from '@hcengineering/contact'
|
||||
import { type Account, type Ref, type TxCUD } from '@hcengineering/core'
|
||||
import type { NotificationType } from '@hcengineering/notification'
|
||||
import type { TriggerControl } from '@hcengineering/server-core'
|
||||
import type { TrainingRequest } from '@hcengineering/training'
|
||||
import contact, { type Employee, type PersonAccount } from '@hcengineering/contact'
|
||||
import { isTxCreateDoc } from '../utils/isTxCreateDoc'
|
||||
import { isTxUpdateDoc } from '../utils/isTxUpdateDoc'
|
||||
|
||||
export async function TrainingRequestNotificationTypeMatch (
|
||||
export function TrainingRequestNotificationTypeMatch (
|
||||
tx: TxCUD<TrainingRequest>,
|
||||
doc: TrainingRequest,
|
||||
user: Ref<Account>,
|
||||
user: Ref<Account>[],
|
||||
type: NotificationType,
|
||||
control: TriggerControl
|
||||
): Promise<boolean> {
|
||||
): boolean {
|
||||
if (isTxCreateDoc(tx)) {
|
||||
const employeeRef: Ref<Employee> = (
|
||||
await control.modelDb.findAll(contact.class.PersonAccount, { _id: user as Ref<PersonAccount> })
|
||||
)[0]?.person as Ref<Employee>
|
||||
const employeeRef: Ref<Employee> = control.modelDb.findAllSync(contact.class.PersonAccount, {
|
||||
_id: user[0] as Ref<PersonAccount>
|
||||
})[0]?.person as Ref<Employee>
|
||||
return doc.trainees.includes(employeeRef)
|
||||
}
|
||||
|
||||
@ -41,9 +41,9 @@ export async function TrainingRequestNotificationTypeMatch (
|
||||
return false
|
||||
}
|
||||
|
||||
const employeeRef: Ref<Employee> = (
|
||||
await control.modelDb.findAll(contact.class.PersonAccount, { _id: user as Ref<PersonAccount> })
|
||||
)[0]?.person as Ref<Employee>
|
||||
const employeeRef: Ref<Employee> = control.modelDb.findAllSync(contact.class.PersonAccount, {
|
||||
_id: user[0] as Ref<PersonAccount>
|
||||
})[0]?.person as Ref<Employee>
|
||||
const newTrainees = typeof pushed === 'object' ? pushed.$each : [pushed]
|
||||
return newTrainees.includes(employeeRef)
|
||||
}
|
||||
|
@ -1983,7 +1983,7 @@ async function createPersonAccount (
|
||||
client ??
|
||||
(await connect(getEndpoint(ctx, workspaceInfo, EndpointKind.Internal), getWorkspaceId(workspace, productId)))
|
||||
try {
|
||||
const ops = new TxOperations(connection, core.account.System)
|
||||
const ops = new TxOperations(connection, core.account.System).apply('create-person' + generateId())
|
||||
|
||||
const name = combineName(account.first, account.last)
|
||||
// Check if PersonAccount is not exists
|
||||
@ -2033,6 +2033,7 @@ async function createPersonAccount (
|
||||
}
|
||||
}
|
||||
}
|
||||
await ops.commit()
|
||||
} finally {
|
||||
if (client === undefined) {
|
||||
await connection.close()
|
||||
|
@ -879,12 +879,10 @@ export class TServerStorage implements ServerStorage {
|
||||
ctx: SessionOperationContext,
|
||||
findAll: ServerStorage['findAll']
|
||||
): Promise<Tx[]> {
|
||||
derived.sort((a, b) => a.modifiedOn - b.modifiedOn)
|
||||
|
||||
await ctx.with('derived-route-tx', {}, (ctx) => this.routeTx(ctx, ...derived))
|
||||
|
||||
const nestedTxes: Tx[] = []
|
||||
if (derived.length > 0) {
|
||||
derived.sort((a, b) => a.modifiedOn - b.modifiedOn)
|
||||
await ctx.with('derived-route-tx', {}, (ctx) => this.routeTx(ctx, ...derived))
|
||||
nestedTxes.push(...(await this.processDerived(ctx, derived, findAll)))
|
||||
}
|
||||
|
||||
@ -992,6 +990,7 @@ export class TServerStorage implements ServerStorage {
|
||||
const onEnds: (() => void)[] = []
|
||||
const result: TxResult[] = []
|
||||
const derived: Tx[] = [...txes].filter((it) => it._class !== core.class.TxApplyIf)
|
||||
let txPromise: Promise<TxResult[]> | undefined
|
||||
|
||||
try {
|
||||
this.fillTxes(txes, txToStore, modelTx, txToProcess, applyTxes)
|
||||
@ -1023,7 +1022,7 @@ export class TServerStorage implements ServerStorage {
|
||||
await this.modelDb.tx(tx)
|
||||
}
|
||||
if (txToStore.length > 0) {
|
||||
await ctx.with(
|
||||
txPromise = ctx.with(
|
||||
'domain-tx',
|
||||
{},
|
||||
async (ctx) => await this.getAdapter(DOMAIN_TX, true).tx(ctx.ctx, ...txToStore),
|
||||
@ -1034,9 +1033,8 @@ export class TServerStorage implements ServerStorage {
|
||||
)
|
||||
}
|
||||
if (txToProcess.length > 0) {
|
||||
result.push(...(await ctx.with('routeTx', {}, (ctx) => this.routeTx(ctx, ...txToProcess))), {
|
||||
count: txToProcess.length
|
||||
})
|
||||
const routerResult = await ctx.with('routeTx', {}, (ctx) => this.routeTx(ctx, ...txToProcess))
|
||||
result.push(...routerResult, { count: txToProcess.length })
|
||||
// invoke triggers and store derived objects
|
||||
derived.push(...(await this.processDerived(ctx, txToProcess, _findAll)))
|
||||
}
|
||||
@ -1053,6 +1051,10 @@ export class TServerStorage implements ServerStorage {
|
||||
{ count: txToProcess.length + derived.length }
|
||||
)
|
||||
}
|
||||
if (txPromise !== undefined) {
|
||||
// Wait for main Tx to be stored
|
||||
await txPromise
|
||||
}
|
||||
} catch (err: any) {
|
||||
ctx.ctx.error('error process tx', { error: err })
|
||||
Analytics.handleError(err)
|
||||
|
@ -16,7 +16,6 @@
|
||||
import core, {
|
||||
DOMAIN_MODEL,
|
||||
DOMAIN_TX,
|
||||
RateLimiter,
|
||||
SortingOrder,
|
||||
TxProcessor,
|
||||
addOperation,
|
||||
@ -76,6 +75,7 @@ import { createHash } from 'crypto'
|
||||
import {
|
||||
type AbstractCursor,
|
||||
type AnyBulkWriteOperation,
|
||||
type BulkWriteResult,
|
||||
type Collection,
|
||||
type Db,
|
||||
type Document,
|
||||
@ -141,9 +141,6 @@ export interface DbAdapterOptions {
|
||||
abstract class MongoAdapterBase implements DbAdapter {
|
||||
_db: DBCollectionHelper
|
||||
|
||||
findRateLimit = new RateLimiter(parseInt(process.env.FIND_RLIMIT ?? '1000'))
|
||||
rateLimit = new RateLimiter(parseInt(process.env.TX_RLIMIT ?? '5'))
|
||||
|
||||
handlers: DbAdapterHandler[] = []
|
||||
|
||||
on (handler: DbAdapterHandler): void {
|
||||
@ -706,130 +703,119 @@ abstract class MongoAdapterBase implements DbAdapter {
|
||||
const stTime = Date.now()
|
||||
const mongoQuery = this.translateQuery(_class, query, options)
|
||||
const fQuery = { ...mongoQuery.base, ...mongoQuery.lookup }
|
||||
return await addOperation(
|
||||
ctx,
|
||||
'find-all',
|
||||
{},
|
||||
async () =>
|
||||
await this.findRateLimit.exec(async () => {
|
||||
const st = Date.now()
|
||||
let result: FindResult<T>
|
||||
const domain = options?.domain ?? this.hierarchy.getDomain(_class)
|
||||
if (
|
||||
options != null &&
|
||||
(options?.lookup != null || this.isEnumSort(_class, options) || this.isRulesSort(options))
|
||||
) {
|
||||
return await this.findWithPipeline(ctx, domain, _class, query, options, stTime)
|
||||
}
|
||||
const coll = this.collection(domain)
|
||||
|
||||
if (options?.limit === 1 || typeof query._id === 'string') {
|
||||
// Skip sort/projection/etc.
|
||||
return await ctx.with(
|
||||
'find-one',
|
||||
{ domain },
|
||||
async (ctx) => {
|
||||
const findOptions: MongoFindOptions = {}
|
||||
|
||||
if (options?.sort !== undefined) {
|
||||
findOptions.sort = this.collectSort<T>(options, _class)
|
||||
}
|
||||
if (options?.projection !== undefined) {
|
||||
findOptions.projection = this.calcProjection<T>(options, _class)
|
||||
}
|
||||
|
||||
let doc: WithId<Document> | null
|
||||
|
||||
if (typeof fQuery._id === 'string') {
|
||||
doc = await coll.findOne({ _id: fQuery._id }, findOptions)
|
||||
if (doc != null && matchQuery([doc as unknown as Doc], query, _class, this.hierarchy).length === 0) {
|
||||
doc = null
|
||||
}
|
||||
} else {
|
||||
doc = await coll.findOne(fQuery, findOptions)
|
||||
}
|
||||
|
||||
let total = -1
|
||||
if (options?.total === true) {
|
||||
total = await coll.countDocuments({ ...mongoQuery.base, ...mongoQuery.lookup })
|
||||
}
|
||||
if (doc != null) {
|
||||
return toFindResult([this.stripHash<T>(doc as unknown as T) as T], total)
|
||||
}
|
||||
return toFindResult([], total)
|
||||
},
|
||||
{ domain, mongoQuery, _idOnly: typeof fQuery._id === 'string' }
|
||||
)
|
||||
}
|
||||
|
||||
let cursor = coll.find<T>(fQuery)
|
||||
|
||||
if (options?.projection !== undefined) {
|
||||
const projection = this.calcProjection<T>(options, _class)
|
||||
if (projection != null) {
|
||||
cursor = cursor.project(projection)
|
||||
}
|
||||
}
|
||||
let total: number = -1
|
||||
if (options != null) {
|
||||
if (options.sort !== undefined) {
|
||||
const sort = this.collectSort<T>(options, _class)
|
||||
if (sort !== undefined) {
|
||||
cursor = cursor.sort(sort)
|
||||
}
|
||||
}
|
||||
if (options.limit !== undefined || typeof query._id === 'string') {
|
||||
if (options.total === true) {
|
||||
total = await coll.countDocuments(fQuery)
|
||||
}
|
||||
cursor = cursor.limit(options.limit ?? 1)
|
||||
}
|
||||
}
|
||||
// Error in case of timeout
|
||||
try {
|
||||
let res: T[] = []
|
||||
await ctx.with(
|
||||
'find-all',
|
||||
{},
|
||||
async (ctx) => {
|
||||
res = await toArray(cursor)
|
||||
},
|
||||
() => ({
|
||||
size: res.length,
|
||||
queueTime: stTime - st,
|
||||
mongoQuery,
|
||||
options,
|
||||
domain
|
||||
})
|
||||
)
|
||||
if (options?.total === true && options?.limit === undefined) {
|
||||
total = res.length
|
||||
}
|
||||
result = toFindResult(this.stripHash(res) as T[], total)
|
||||
} catch (e) {
|
||||
console.error('error during executing cursor in findAll', _class, cutObjectArray(query), options, e)
|
||||
throw e
|
||||
}
|
||||
|
||||
const edTime = Date.now()
|
||||
if (edTime - st > 1000 || st - stTime > 1000) {
|
||||
ctx.error('FindAll', {
|
||||
time: edTime - st,
|
||||
_class,
|
||||
query: fQuery,
|
||||
options,
|
||||
queueTime: st - stTime
|
||||
})
|
||||
}
|
||||
this.handleEvent(domain, 'read', result.length)
|
||||
return result
|
||||
}),
|
||||
{
|
||||
_class,
|
||||
query: fQuery,
|
||||
options
|
||||
return await addOperation(ctx, 'find-all', {}, async () => {
|
||||
const st = Date.now()
|
||||
let result: FindResult<T>
|
||||
const domain = options?.domain ?? this.hierarchy.getDomain(_class)
|
||||
if (
|
||||
options != null &&
|
||||
(options?.lookup != null || this.isEnumSort(_class, options) || this.isRulesSort(options))
|
||||
) {
|
||||
return await this.findWithPipeline(ctx, domain, _class, query, options, stTime)
|
||||
}
|
||||
)
|
||||
const coll = this.collection(domain)
|
||||
|
||||
if (options?.limit === 1 || typeof query._id === 'string') {
|
||||
// Skip sort/projection/etc.
|
||||
return await ctx.with(
|
||||
'find-one',
|
||||
{ domain },
|
||||
async (ctx) => {
|
||||
const findOptions: MongoFindOptions = {}
|
||||
|
||||
if (options?.sort !== undefined) {
|
||||
findOptions.sort = this.collectSort<T>(options, _class)
|
||||
}
|
||||
if (options?.projection !== undefined) {
|
||||
findOptions.projection = this.calcProjection<T>(options, _class)
|
||||
}
|
||||
|
||||
let doc: WithId<Document> | null
|
||||
|
||||
if (typeof fQuery._id === 'string') {
|
||||
doc = await coll.findOne({ _id: fQuery._id }, findOptions)
|
||||
if (doc != null && matchQuery([doc as unknown as Doc], query, _class, this.hierarchy).length === 0) {
|
||||
doc = null
|
||||
}
|
||||
} else {
|
||||
doc = await coll.findOne(fQuery, findOptions)
|
||||
}
|
||||
|
||||
let total = -1
|
||||
if (options?.total === true) {
|
||||
total = await coll.countDocuments({ ...mongoQuery.base, ...mongoQuery.lookup })
|
||||
}
|
||||
if (doc != null) {
|
||||
return toFindResult([this.stripHash<T>(doc as unknown as T) as T], total)
|
||||
}
|
||||
return toFindResult([], total)
|
||||
},
|
||||
{ domain, mongoQuery, _idOnly: typeof fQuery._id === 'string' }
|
||||
)
|
||||
}
|
||||
|
||||
let cursor = coll.find<T>(fQuery)
|
||||
|
||||
if (options?.projection !== undefined) {
|
||||
const projection = this.calcProjection<T>(options, _class)
|
||||
if (projection != null) {
|
||||
cursor = cursor.project(projection)
|
||||
}
|
||||
}
|
||||
let total: number = -1
|
||||
if (options != null) {
|
||||
if (options.sort !== undefined) {
|
||||
const sort = this.collectSort<T>(options, _class)
|
||||
if (sort !== undefined) {
|
||||
cursor = cursor.sort(sort)
|
||||
}
|
||||
}
|
||||
if (options.limit !== undefined || typeof query._id === 'string') {
|
||||
if (options.total === true) {
|
||||
total = await coll.countDocuments(fQuery)
|
||||
}
|
||||
cursor = cursor.limit(options.limit ?? 1)
|
||||
}
|
||||
}
|
||||
// Error in case of timeout
|
||||
try {
|
||||
let res: T[] = []
|
||||
await ctx.with(
|
||||
'find-all',
|
||||
{},
|
||||
async (ctx) => {
|
||||
res = await toArray(cursor)
|
||||
},
|
||||
() => ({
|
||||
size: res.length,
|
||||
queueTime: stTime - st,
|
||||
mongoQuery,
|
||||
options,
|
||||
domain
|
||||
})
|
||||
)
|
||||
if (options?.total === true && options?.limit === undefined) {
|
||||
total = res.length
|
||||
}
|
||||
result = toFindResult(this.stripHash(res) as T[], total)
|
||||
} catch (e) {
|
||||
console.error('error during executing cursor in findAll', _class, cutObjectArray(query), options, e)
|
||||
throw e
|
||||
}
|
||||
|
||||
const edTime = Date.now()
|
||||
if (edTime - st > 1000 || st - stTime > 1000) {
|
||||
ctx.error('FindAll', {
|
||||
time: edTime - st,
|
||||
_class,
|
||||
query: fQuery,
|
||||
options,
|
||||
queueTime: st - stTime
|
||||
})
|
||||
}
|
||||
this.handleEvent(domain, 'read', result.length)
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
private collectSort<T extends Doc>(
|
||||
@ -1129,13 +1115,14 @@ class MongoAdapter extends MongoAdapterBase {
|
||||
if (bulk.length === 0) {
|
||||
return
|
||||
}
|
||||
const promises: Promise<BulkWriteResult>[] = []
|
||||
for (const [domain, ops] of bulk) {
|
||||
if (ops === undefined || ops.length === 0) {
|
||||
continue
|
||||
}
|
||||
const coll = this.db.collection<Doc>(domain)
|
||||
|
||||
await this.rateLimit.exec(() =>
|
||||
promises.push(
|
||||
addOperation(
|
||||
ctx,
|
||||
'bulk-write',
|
||||
@ -1146,8 +1133,7 @@ class MongoAdapter extends MongoAdapterBase {
|
||||
{ domain },
|
||||
() =>
|
||||
coll.bulkWrite(ops, {
|
||||
ordered: false,
|
||||
writeConcern: { w: 0 }
|
||||
ordered: false
|
||||
}),
|
||||
{
|
||||
domain,
|
||||
@ -1157,6 +1143,7 @@ class MongoAdapter extends MongoAdapterBase {
|
||||
)
|
||||
)
|
||||
}
|
||||
await Promise.all(promises)
|
||||
}
|
||||
|
||||
async pushBulk (ctx: MeasureContext, domain: Domain, ops: AnyBulkWriteOperation<Doc>[]): Promise<void> {
|
||||
@ -1166,6 +1153,8 @@ class MongoAdapter extends MongoAdapterBase {
|
||||
} else {
|
||||
this.bulkOps.set(domain, ops)
|
||||
}
|
||||
// We need to wait next cycle to send request
|
||||
await new Promise<void>((resolve) => setImmediate(resolve))
|
||||
await this._pushBulk(ctx)
|
||||
}
|
||||
|
||||
@ -1528,6 +1517,10 @@ class MongoTxAdapter extends MongoAdapterBase implements TxAdapter {
|
||||
const txes = this.txBulk
|
||||
this.txBulk = []
|
||||
|
||||
if (txes.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const opName = txes.length === 1 ? 'tx-one' : 'tx'
|
||||
await addOperation(
|
||||
ctx,
|
||||
@ -1541,8 +1534,7 @@ class MongoTxAdapter extends MongoAdapterBase implements TxAdapter {
|
||||
this.txCollection().insertMany(
|
||||
txes.map((it) => translateDoc(it)),
|
||||
{
|
||||
ordered: false,
|
||||
writeConcern: { w: 0 }
|
||||
ordered: false
|
||||
}
|
||||
),
|
||||
{
|
||||
@ -1561,6 +1553,9 @@ class MongoTxAdapter extends MongoAdapterBase implements TxAdapter {
|
||||
return []
|
||||
}
|
||||
this.txBulk.push(...tx)
|
||||
|
||||
// We need to wait next cycle to send request
|
||||
await new Promise<void>((resolve) => setImmediate(resolve))
|
||||
await this._bulkTx(ctx)
|
||||
return []
|
||||
}
|
||||
|
@ -27,8 +27,8 @@
|
||||
"compression-webpack-plugin": "^10.0.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"fork-ts-checker-webpack-plugin": "~7.3.0",
|
||||
"update-browserslist-db": "~1.0.11",
|
||||
"browserslist": "4.21.5",
|
||||
"update-browserslist-db": "^1.1.0",
|
||||
"browserslist": "^4.23.3",
|
||||
"esbuild": "^0.20.0",
|
||||
"esbuild-loader": "^4.0.3",
|
||||
"typescript": "^5.3.3",
|
||||
|
@ -27,6 +27,9 @@ export class ChannelPage extends CommonPage {
|
||||
readonly chooseChannel = (channel: string): Locator => this.page.getByRole('button', { name: channel })
|
||||
readonly closePopupWindow = (): Locator => this.page.locator('.notifyPopup button[data-id="btnNotifyClose"]')
|
||||
readonly openAddMemberToChannel = (userName: string): Locator => this.page.getByRole('button', { name: userName })
|
||||
readonly addMemberToChannelTableButton = (userName: string): Locator =>
|
||||
this.page.locator('.antiTable-body__row').getByText(userName)
|
||||
|
||||
readonly addMemberToChannelButton = (userName: string): Locator => this.page.getByText(userName)
|
||||
readonly joinChannelButton = (): Locator => this.page.getByRole('button', { name: 'Join' })
|
||||
readonly addEmojiButton = (): Locator =>
|
||||
@ -204,7 +207,7 @@ export class ChannelPage extends CommonPage {
|
||||
}
|
||||
|
||||
async clickOnUser (user: string): Promise<void> {
|
||||
await this.addMemberToChannelButton(user).click()
|
||||
await this.addMemberToChannelTableButton(user).click()
|
||||
}
|
||||
|
||||
async addMemberToChannel (user: string): Promise<void> {
|
||||
|
Loading…
Reference in New Issue
Block a user