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