dmart 1.4.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- alembic.ini +117 -0
- api/__init__.py +0 -0
- api/info/__init__.py +0 -0
- api/info/router.py +109 -0
- api/managed/__init__.py +0 -0
- api/managed/router.py +1541 -0
- api/managed/utils.py +1879 -0
- api/public/__init__.py +0 -0
- api/public/router.py +758 -0
- api/qr/__init__.py +0 -0
- api/qr/router.py +108 -0
- api/user/__init__.py +0 -0
- api/user/model/__init__.py +0 -0
- api/user/model/errors.py +14 -0
- api/user/model/requests.py +165 -0
- api/user/model/responses.py +11 -0
- api/user/router.py +1413 -0
- api/user/service.py +270 -0
- bundler.py +55 -0
- config/__init__.py +0 -0
- config/channels.json +11 -0
- config/notification.json +17 -0
- cxb/__init__.py +0 -0
- cxb/client/__init__.py +0 -0
- cxb/client/assets/@codemirror-Rn7_6DkE.js +10 -0
- cxb/client/assets/@edraj-CS4NwVbD.js +1 -0
- cxb/client/assets/@floating-ui-BwwcF-xh.js +1 -0
- cxb/client/assets/@formatjs-yKEsAtjs.js +1 -0
- cxb/client/assets/@fortawesome-DRW1UCdr.js +9 -0
- cxb/client/assets/@jsonquerylang-laKNoFFq.js +12 -0
- cxb/client/assets/@lezer-za4Q-8Ew.js +1 -0
- cxb/client/assets/@marijn-DXwl3gUT.js +1 -0
- cxb/client/assets/@popperjs-l0sNRNKZ.js +1 -0
- cxb/client/assets/@replit--ERk53eB.js +1 -0
- cxb/client/assets/@roxi-CGMFK4i8.js +6 -0
- cxb/client/assets/@typewriter-cCzskkIv.js +17 -0
- cxb/client/assets/@zerodevx-BlBZjKxu.js +1 -0
- cxb/client/assets/@zerodevx-CVEpe6WZ.css +1 -0
- cxb/client/assets/BreadCrumbLite-DAhOx38v.js +1 -0
- cxb/client/assets/EntryRenderer-25YDhRen.js +32 -0
- cxb/client/assets/EntryRenderer-DXytdFp9.css +1 -0
- cxb/client/assets/ListView-BpAycA2h.js +16 -0
- cxb/client/assets/ListView-U8of-_c-.css +1 -0
- cxb/client/assets/Prism--hMplq-p.js +3 -0
- cxb/client/assets/Prism-Uh6uStUw.css +1 -0
- cxb/client/assets/Table2Cols-BsbwicQm.js +1 -0
- cxb/client/assets/_..-BvT6vdHa.css +1 -0
- cxb/client/assets/_...404_-fuLH_rX9.js +2 -0
- cxb/client/assets/_...fallback_-Ba_NLmAE.js +1 -0
- cxb/client/assets/_module-Bfk8MiCs.js +3 -0
- cxb/client/assets/_module-CEW0D5oI.js +4 -0
- cxb/client/assets/_module-Dgq0ZVtz.js +1 -0
- cxb/client/assets/ajv-Cpj98o6Y.js +1 -0
- cxb/client/assets/axios-CG2WSiiR.js +6 -0
- cxb/client/assets/clsx-B-dksMZM.js +1 -0
- cxb/client/assets/codemirror-wrapped-line-indent-DPhKvljI.js +1 -0
- cxb/client/assets/compare-C3AjiGFR.js +1 -0
- cxb/client/assets/compute-scroll-into-view-Bl8rNFhg.js +1 -0
- cxb/client/assets/consolite-DlCuI0F9.js +1 -0
- cxb/client/assets/crelt-C8TCjufn.js +1 -0
- cxb/client/assets/date-fns-l0sNRNKZ.js +1 -0
- cxb/client/assets/deepmerge-rn4rBaHU.js +1 -0
- cxb/client/assets/dmart_services-AL6-IdDE.js +1 -0
- cxb/client/assets/downloadFile-D08i0YDh.js +1 -0
- cxb/client/assets/easy-signal-BiPFIK3O.js +1 -0
- cxb/client/assets/esm-env-rsSWfq8L.js +1 -0
- cxb/client/assets/export-OF_rTiXu.js +1 -0
- cxb/client/assets/fast-deep-equal-l0sNRNKZ.js +1 -0
- cxb/client/assets/fast-diff-C-IidNf4.js +1 -0
- cxb/client/assets/fast-uri-l0sNRNKZ.js +1 -0
- cxb/client/assets/flowbite-svelte-BLvjb-sa.js +1 -0
- cxb/client/assets/flowbite-svelte-CD54FDqW.css +1 -0
- cxb/client/assets/flowbite-svelte-icons-BI8GVhw_.js +1 -0
- cxb/client/assets/github-slugger-CQ4oX9Ud.js +1 -0
- cxb/client/assets/global-igKv-1g9.js +1 -0
- cxb/client/assets/hookar-BMRD9G9H.js +1 -0
- cxb/client/assets/immutable-json-patch-DtRO2E_S.js +1 -0
- cxb/client/assets/import-1vE3gBat.js +1 -0
- cxb/client/assets/index-B-eTh-ZX.js +1 -0
- cxb/client/assets/index-BVyxzKtH.js +1 -0
- cxb/client/assets/index-BdeNM69f.js +1 -0
- cxb/client/assets/index-C6cPO4op.js +1 -0
- cxb/client/assets/index-CC-A1ipE.js +1 -0
- cxb/client/assets/index-CTxJ-lDp.js +1 -0
- cxb/client/assets/index-Cd-F5j_k.js +1 -0
- cxb/client/assets/index-D742rwaM.js +1 -0
- cxb/client/assets/index-DTfhnhwd.js +1 -0
- cxb/client/assets/index-DdXRK7n9.js +2 -0
- cxb/client/assets/index-DtiCmB4o.js +1 -0
- cxb/client/assets/index-NBrXBlLA.css +2 -0
- cxb/client/assets/index-ac-Buu_H.js +4 -0
- cxb/client/assets/index-iYkH7C67.js +1 -0
- cxb/client/assets/info-B986lRiM.js +1 -0
- cxb/client/assets/intl-messageformat-Dc5UU-HB.js +3 -0
- cxb/client/assets/jmespath-l0sNRNKZ.js +1 -0
- cxb/client/assets/json-schema-traverse-l0sNRNKZ.js +1 -0
- cxb/client/assets/json-source-map-DRgZidqy.js +5 -0
- cxb/client/assets/jsonpath-plus-l0sNRNKZ.js +1 -0
- cxb/client/assets/jsonrepair-B30Dx381.js +8 -0
- cxb/client/assets/lodash-es-DZVAA2ox.js +1 -0
- cxb/client/assets/marked-DKjyhwJX.js +56 -0
- cxb/client/assets/marked-gfm-heading-id-U5zO829x.js +2 -0
- cxb/client/assets/marked-mangle-CDMeiHC6.js +1 -0
- cxb/client/assets/memoize-one-BdPwpGay.js +1 -0
- cxb/client/assets/natural-compare-lite-Bg2Xcf-o.js +7 -0
- cxb/client/assets/pagination-svelte-D5CyoiE_.js +13 -0
- cxb/client/assets/pagination-svelte-v10nAbbM.css +1 -0
- cxb/client/assets/plantuml-encoder-C47mzt9T.js +1 -0
- cxb/client/assets/prismjs-DTUiLGJu.js +9 -0
- cxb/client/assets/profile-BUf-tKMe.js +1 -0
- cxb/client/assets/query-CNmXTsgf.js +1 -0
- cxb/client/assets/queryHelpers-C9iBWwqe.js +1 -0
- cxb/client/assets/scroll-into-view-if-needed-KR58zyjF.js +1 -0
- cxb/client/assets/spaces-0oyGvpii.js +1 -0
- cxb/client/assets/style-mod-Bs6eFhZE.js +3 -0
- cxb/client/assets/svelte-B2XmcTi_.js +4 -0
- cxb/client/assets/svelte-awesome-COLlx0DN.css +1 -0
- cxb/client/assets/svelte-awesome-DhnMA6Q_.js +1 -0
- cxb/client/assets/svelte-datatables-net-CY7LBj6I.js +1 -0
- cxb/client/assets/svelte-floating-ui-BlS3sOAQ.js +1 -0
- cxb/client/assets/svelte-i18n-CT2KkQaN.js +3 -0
- cxb/client/assets/svelte-jsoneditor-BzfX6Usi.css +1 -0
- cxb/client/assets/svelte-jsoneditor-CUGSvWId.js +25 -0
- cxb/client/assets/svelte-select-CegQKzqH.css +1 -0
- cxb/client/assets/svelte-select-CjHAt_85.js +6 -0
- cxb/client/assets/tailwind-merge-CJvxXMcu.js +1 -0
- cxb/client/assets/tailwind-variants-Cj20BoQ3.js +1 -0
- cxb/client/assets/toast-B9WDyfyI.js +1 -0
- cxb/client/assets/tslib-pJfR_DrR.js +1 -0
- cxb/client/assets/typewriter-editor-DkTVIJdm.js +25 -0
- cxb/client/assets/user-DeK_NB5v.js +1 -0
- cxb/client/assets/vanilla-picker-l5rcX3cq.js +8 -0
- cxb/client/assets/w3c-keyname-Vcq4gwWv.js +1 -0
- cxb/client/config.json +11 -0
- cxb/client/config.sample.json +11 -0
- cxb/client/favicon.ico +0 -0
- cxb/client/favicon.png +0 -0
- cxb/client/index.html +28 -0
- data_adapters/__init__.py +0 -0
- data_adapters/adapter.py +16 -0
- data_adapters/base_data_adapter.py +467 -0
- data_adapters/file/__init__.py +0 -0
- data_adapters/file/adapter.py +2043 -0
- data_adapters/file/adapter_helpers.py +1013 -0
- data_adapters/file/archive.py +150 -0
- data_adapters/file/create_index.py +331 -0
- data_adapters/file/create_users_folders.py +52 -0
- data_adapters/file/custom_validations.py +68 -0
- data_adapters/file/drop_index.py +40 -0
- data_adapters/file/health_check.py +560 -0
- data_adapters/file/redis_services.py +1110 -0
- data_adapters/helpers.py +27 -0
- data_adapters/sql/__init__.py +0 -0
- data_adapters/sql/adapter.py +3218 -0
- data_adapters/sql/adapter_helpers.py +491 -0
- data_adapters/sql/create_tables.py +451 -0
- data_adapters/sql/create_users_folders.py +53 -0
- data_adapters/sql/db_to_json_migration.py +485 -0
- data_adapters/sql/health_check_sql.py +232 -0
- data_adapters/sql/json_to_db_migration.py +454 -0
- data_adapters/sql/update_query_policies.py +101 -0
- data_generator.py +81 -0
- dmart-1.4.17.dist-info/METADATA +65 -0
- dmart-1.4.17.dist-info/RECORD +289 -0
- dmart-1.4.17.dist-info/WHEEL +5 -0
- dmart-1.4.17.dist-info/entry_points.txt +2 -0
- dmart-1.4.17.dist-info/top_level.txt +24 -0
- dmart.py +623 -0
- dmart_migrations/README +1 -0
- dmart_migrations/__init__.py +0 -0
- dmart_migrations/__pycache__/__init__.cpython-314.pyc +0 -0
- dmart_migrations/__pycache__/env.cpython-314.pyc +0 -0
- dmart_migrations/env.py +100 -0
- dmart_migrations/notes.txt +11 -0
- dmart_migrations/script.py.mako +28 -0
- dmart_migrations/scripts/__init__.py +0 -0
- dmart_migrations/scripts/calculate_checksums.py +77 -0
- dmart_migrations/scripts/migration_f7a4949eed19.py +28 -0
- dmart_migrations/versions/0f3d2b1a7c21_add_authz_materialized_views.py +87 -0
- dmart_migrations/versions/10d2041b94d4_last_checksum_history.py +62 -0
- dmart_migrations/versions/1cf4e1ee3cb8_ext_permission_with_filter_fields_values.py +33 -0
- dmart_migrations/versions/26bfe19b49d4_rm_failedloginattempts.py +42 -0
- dmart_migrations/versions/3c8bca2219cc_add_otp_table.py +38 -0
- dmart_migrations/versions/6675fd9dfe42_remove_unique_from_sessions_table.py +36 -0
- dmart_migrations/versions/71bc1df82e6a_adding_user_last_login_at.py +43 -0
- dmart_migrations/versions/74288ccbd3b5_initial.py +264 -0
- dmart_migrations/versions/7520a89a8467_rm_activesession_table.py +39 -0
- dmart_migrations/versions/848b623755a4_make_created_nd_updated_at_required.py +138 -0
- dmart_migrations/versions/8640dcbebf85_add_notes_to_users.py +32 -0
- dmart_migrations/versions/91c94250232a_adding_fk_on_owner_shortname.py +104 -0
- dmart_migrations/versions/98ecd6f56f9a_ext_meta_with_owner_group_shortname.py +66 -0
- dmart_migrations/versions/9aae9138c4ef_indexing_created_at_updated_at.py +80 -0
- dmart_migrations/versions/__init__.py +0 -0
- dmart_migrations/versions/__pycache__/0f3d2b1a7c21_add_authz_materialized_views.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/10d2041b94d4_last_checksum_history.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/1cf4e1ee3cb8_ext_permission_with_filter_fields_values.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/26bfe19b49d4_rm_failedloginattempts.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/3c8bca2219cc_add_otp_table.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/6675fd9dfe42_remove_unique_from_sessions_table.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/71bc1df82e6a_adding_user_last_login_at.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/74288ccbd3b5_initial.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/7520a89a8467_rm_activesession_table.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/848b623755a4_make_created_nd_updated_at_required.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/8640dcbebf85_add_notes_to_users.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/91c94250232a_adding_fk_on_owner_shortname.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/98ecd6f56f9a_ext_meta_with_owner_group_shortname.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/9aae9138c4ef_indexing_created_at_updated_at.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/b53f916b3f6d_json_to_jsonb.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/eb5f1ec65156_adding_user_locked_to_device.cpython-314.pyc +0 -0
- dmart_migrations/versions/__pycache__/f7a4949eed19_adding_query_policies_to_meta.cpython-314.pyc +0 -0
- dmart_migrations/versions/b53f916b3f6d_json_to_jsonb.py +492 -0
- dmart_migrations/versions/eb5f1ec65156_adding_user_locked_to_device.py +36 -0
- dmart_migrations/versions/f7a4949eed19_adding_query_policies_to_meta.py +60 -0
- get_settings.py +7 -0
- info.json +1 -0
- languages/__init__.py +0 -0
- languages/arabic.json +15 -0
- languages/english.json +16 -0
- languages/kurdish.json +14 -0
- languages/loader.py +12 -0
- main.py +560 -0
- migrate.py +24 -0
- models/__init__.py +0 -0
- models/api.py +203 -0
- models/core.py +597 -0
- models/enums.py +255 -0
- password_gen.py +8 -0
- plugins/__init__.py +0 -0
- plugins/action_log/__init__.py +0 -0
- plugins/action_log/plugin.py +121 -0
- plugins/admin_notification_sender/__init__.py +0 -0
- plugins/admin_notification_sender/plugin.py +124 -0
- plugins/ldap_manager/__init__.py +0 -0
- plugins/ldap_manager/plugin.py +100 -0
- plugins/local_notification/__init__.py +0 -0
- plugins/local_notification/plugin.py +123 -0
- plugins/realtime_updates_notifier/__init__.py +0 -0
- plugins/realtime_updates_notifier/plugin.py +58 -0
- plugins/redis_db_update/__init__.py +0 -0
- plugins/redis_db_update/plugin.py +188 -0
- plugins/resource_folders_creation/__init__.py +0 -0
- plugins/resource_folders_creation/plugin.py +81 -0
- plugins/system_notification_sender/__init__.py +0 -0
- plugins/system_notification_sender/plugin.py +188 -0
- plugins/update_access_controls/__init__.py +0 -0
- plugins/update_access_controls/plugin.py +9 -0
- pytests/__init__.py +0 -0
- pytests/api_user_models_erros_test.py +16 -0
- pytests/api_user_models_requests_test.py +98 -0
- pytests/archive_test.py +72 -0
- pytests/base_test.py +300 -0
- pytests/get_settings_test.py +14 -0
- pytests/json_to_db_migration_test.py +237 -0
- pytests/service_test.py +26 -0
- pytests/test_info.py +55 -0
- pytests/test_status.py +15 -0
- run_notification_campaign.py +85 -0
- scheduled_notification_handler.py +121 -0
- schema_migration.py +208 -0
- schema_modulate.py +192 -0
- set_admin_passwd.py +55 -0
- sync.py +202 -0
- utils/__init__.py +0 -0
- utils/access_control.py +306 -0
- utils/async_request.py +8 -0
- utils/exporter.py +309 -0
- utils/firebase_notifier.py +57 -0
- utils/generate_email.py +37 -0
- utils/helpers.py +352 -0
- utils/hypercorn_config.py +12 -0
- utils/internal_error_code.py +60 -0
- utils/jwt.py +124 -0
- utils/logger.py +167 -0
- utils/middleware.py +99 -0
- utils/notification.py +75 -0
- utils/password_hashing.py +16 -0
- utils/plugin_manager.py +202 -0
- utils/query_policies_helper.py +128 -0
- utils/regex.py +44 -0
- utils/repository.py +529 -0
- utils/router_helper.py +19 -0
- utils/settings.py +166 -0
- utils/sms_notifier.py +21 -0
- utils/social_sso.py +67 -0
- utils/templates/activation.html.j2 +26 -0
- utils/templates/reminder.html.j2 +17 -0
- utils/ticket_sys_utils.py +203 -0
- utils/web_notifier.py +29 -0
- websocket.py +231 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from models.core import Content, Payload, PluginBase, Event, Reaction, Comment, Relationship, Locator
|
|
3
|
+
from models.enums import ContentType
|
|
4
|
+
from utils.helpers import camel_case
|
|
5
|
+
from data_adapters.adapter import data_adapter as db
|
|
6
|
+
from uuid import uuid4
|
|
7
|
+
from fastapi.logger import logger
|
|
8
|
+
from utils.async_request import AsyncRequest
|
|
9
|
+
from utils.settings import settings
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Plugin(PluginBase):
|
|
13
|
+
async def hook(self, data: Event):
|
|
14
|
+
if not isinstance(data.shortname, str):
|
|
15
|
+
logger.error("data.shortname is None and str is required at local_notification")
|
|
16
|
+
return
|
|
17
|
+
|
|
18
|
+
if data.resource_type is None:
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
resource_type = data.resource_type
|
|
22
|
+
try:
|
|
23
|
+
class_type = getattr(sys.modules["models.core"], camel_case(resource_type))
|
|
24
|
+
except AttributeError:
|
|
25
|
+
logger.warning(
|
|
26
|
+
f"local_notification: unsupported resource_type={resource_type!s} (no model class found)"
|
|
27
|
+
)
|
|
28
|
+
return
|
|
29
|
+
parent_subpath, parent_shortname, parent_owner = None, None, None
|
|
30
|
+
|
|
31
|
+
entry = await db.load(
|
|
32
|
+
space_name=data.space_name,
|
|
33
|
+
subpath=data.subpath,
|
|
34
|
+
shortname=data.shortname,
|
|
35
|
+
class_type=class_type,
|
|
36
|
+
user_shortname=data.user_shortname
|
|
37
|
+
)
|
|
38
|
+
relationship = None
|
|
39
|
+
if class_type in [Reaction, Comment]:
|
|
40
|
+
parent_subpath, parent_shortname = data.subpath.rsplit("/", 1)
|
|
41
|
+
parent = await db.load(
|
|
42
|
+
space_name=data.space_name,
|
|
43
|
+
subpath=parent_subpath,
|
|
44
|
+
shortname=parent_shortname,
|
|
45
|
+
class_type=Content,
|
|
46
|
+
user_shortname=data.user_shortname
|
|
47
|
+
)
|
|
48
|
+
if parent.owner_shortname == data.user_shortname:
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
parent_owner = parent.owner_shortname
|
|
52
|
+
|
|
53
|
+
relationship = Relationship(
|
|
54
|
+
related_to=Locator(
|
|
55
|
+
type=resource_type,
|
|
56
|
+
space_name=data.space_name,
|
|
57
|
+
subpath=parent_subpath,
|
|
58
|
+
shortname=parent_shortname
|
|
59
|
+
),
|
|
60
|
+
attributes={
|
|
61
|
+
"parent_owner": parent_owner
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
else:
|
|
66
|
+
if not entry.owner_shortname or entry.owner_shortname == data.user_shortname:
|
|
67
|
+
return
|
|
68
|
+
parent_owner = entry.owner_shortname
|
|
69
|
+
|
|
70
|
+
uuid = uuid4()
|
|
71
|
+
|
|
72
|
+
meta_obj = Content(
|
|
73
|
+
uuid=uuid,
|
|
74
|
+
shortname=str(uuid)[:8],
|
|
75
|
+
owner_shortname=data.user_shortname,
|
|
76
|
+
is_active=True,
|
|
77
|
+
payload=Payload(
|
|
78
|
+
content_type=ContentType.json,
|
|
79
|
+
schema_shortname="notification",
|
|
80
|
+
body=f"{str(uuid)[:8]}.json"
|
|
81
|
+
),
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if relationship is not None:
|
|
85
|
+
meta_obj.relationships = [relationship]
|
|
86
|
+
|
|
87
|
+
await db.save(
|
|
88
|
+
"personal",
|
|
89
|
+
f"people/{parent_owner}/notifications",
|
|
90
|
+
meta_obj,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
notification_obj = {
|
|
94
|
+
"entry_space": data.space_name,
|
|
95
|
+
"entry_subpath": data.subpath,
|
|
96
|
+
"entry_shortname": data.shortname,
|
|
97
|
+
|
|
98
|
+
"action_by": data.user_shortname,
|
|
99
|
+
"action_type": data.action_type,
|
|
100
|
+
"resource_type": data.resource_type,
|
|
101
|
+
"is_read": "no"
|
|
102
|
+
}
|
|
103
|
+
await db.save_payload_from_json(
|
|
104
|
+
"personal",
|
|
105
|
+
f"people/{parent_owner}/notifications",
|
|
106
|
+
meta_obj,
|
|
107
|
+
notification_obj,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if not settings.websocket_url:
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
async with AsyncRequest() as client:
|
|
114
|
+
await client.post(
|
|
115
|
+
f"{settings.websocket_url}/send-message/{parent_owner}",
|
|
116
|
+
json={
|
|
117
|
+
"type": "notification",
|
|
118
|
+
"channels": [
|
|
119
|
+
f"{data.space_name}:__ALL__:__ALL__:__ALL__:__ALL__",
|
|
120
|
+
],
|
|
121
|
+
"message": notification_obj
|
|
122
|
+
}
|
|
123
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from models.core import PluginBase, Event
|
|
2
|
+
from utils.async_request import AsyncRequest
|
|
3
|
+
from utils.settings import settings
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Plugin(PluginBase):
|
|
7
|
+
|
|
8
|
+
async def hook(self, data: Event):
|
|
9
|
+
all_MKW = "__ALL__"
|
|
10
|
+
|
|
11
|
+
state = data.attributes.get("state", all_MKW)
|
|
12
|
+
|
|
13
|
+
# if subpath = parent/child
|
|
14
|
+
# send to channels with subpaths "parent" and "parent/child"
|
|
15
|
+
channels = []
|
|
16
|
+
subpath = ""
|
|
17
|
+
for subpath_part in data.subpath.split("/"):
|
|
18
|
+
if not subpath_part:
|
|
19
|
+
continue
|
|
20
|
+
subpath += subpath_part
|
|
21
|
+
|
|
22
|
+
# Consider channels with __ALL__ magic word
|
|
23
|
+
if not subpath.startswith("/"):
|
|
24
|
+
subpath = "/" + subpath
|
|
25
|
+
channels.extend([
|
|
26
|
+
f"{data.space_name}:{subpath}:{all_MKW}:{data.action_type}:{state}",
|
|
27
|
+
f"{data.space_name}:{subpath}:{all_MKW}:{all_MKW}:{state}",
|
|
28
|
+
f"{data.space_name}:{subpath}:{all_MKW}:{data.action_type}:{all_MKW}",
|
|
29
|
+
f"{data.space_name}:{subpath}:{all_MKW}:{all_MKW}:{all_MKW}",
|
|
30
|
+
])
|
|
31
|
+
if data.schema_shortname:
|
|
32
|
+
channels.extend([
|
|
33
|
+
f"{data.space_name}:{subpath}:{data.schema_shortname}:{data.action_type}:{state}",
|
|
34
|
+
f"{data.space_name}:{subpath}:{data.schema_shortname}:{all_MKW}:{state}",
|
|
35
|
+
f"{data.space_name}:{subpath}:{data.schema_shortname}:{data.action_type}:{all_MKW}",
|
|
36
|
+
f"{data.space_name}:{subpath}:{data.schema_shortname}:{all_MKW}:{all_MKW}",
|
|
37
|
+
])
|
|
38
|
+
subpath += "/"
|
|
39
|
+
if not settings.websocket_url:
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
async with AsyncRequest() as client:
|
|
43
|
+
await client.post(
|
|
44
|
+
f"{settings.websocket_url}/broadcast-to-channels",
|
|
45
|
+
json={
|
|
46
|
+
"type": "notification_subscription",
|
|
47
|
+
"channels": [*set(channels)],
|
|
48
|
+
"message": {
|
|
49
|
+
"title": "updated",
|
|
50
|
+
"subpath": data.subpath,
|
|
51
|
+
"space": data.space_name,
|
|
52
|
+
"shortname": data.shortname,
|
|
53
|
+
"action_type": data.action_type,
|
|
54
|
+
"owner_shortname": data.user_shortname
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
|
|
File without changes
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from models.core import ActionType, Attachment, PluginBase, Event, Space
|
|
3
|
+
from utils.helpers import camel_case
|
|
4
|
+
from data_adapters.file.adapter_helpers import generate_payload_string
|
|
5
|
+
from data_adapters.adapter import data_adapter as db
|
|
6
|
+
from models import core
|
|
7
|
+
from models.enums import ContentType, ResourceType
|
|
8
|
+
from data_adapters.file.redis_services import RedisServices
|
|
9
|
+
from fastapi.logger import logger
|
|
10
|
+
from data_adapters.file.create_index import main as reload_redis
|
|
11
|
+
from utils.settings import settings
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Plugin(PluginBase):
|
|
16
|
+
async def hook(self, data: Event):
|
|
17
|
+
if settings.active_data_db == "sql":
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
self.data = data
|
|
21
|
+
# Type narrowing for PyRight
|
|
22
|
+
if (
|
|
23
|
+
not isinstance(data.shortname, str)
|
|
24
|
+
or not isinstance(data.resource_type, ResourceType)
|
|
25
|
+
or not isinstance(data.attributes, dict)
|
|
26
|
+
):
|
|
27
|
+
logger.error("invalid data at redis_db_update")
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
spaces = await db.get_spaces()
|
|
31
|
+
if (
|
|
32
|
+
data.space_name not in spaces
|
|
33
|
+
or not Space.model_validate_json(spaces[data.space_name]).indexing_enabled
|
|
34
|
+
):
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
class_type = getattr(
|
|
38
|
+
sys.modules["models.core"],
|
|
39
|
+
camel_case(core.ResourceType(data.resource_type)),
|
|
40
|
+
)
|
|
41
|
+
if issubclass(class_type, Attachment):
|
|
42
|
+
await self.update_parent_entry_payload_string()
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
async with RedisServices() as redis_services:
|
|
46
|
+
if data.resource_type == ResourceType.folder and data.action_type in [
|
|
47
|
+
ActionType.delete,
|
|
48
|
+
ActionType.move,
|
|
49
|
+
]:
|
|
50
|
+
await reload_redis(for_space=data.space_name)
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
if data.action_type == ActionType.delete:
|
|
54
|
+
doc_id = redis_services.generate_doc_id(
|
|
55
|
+
data.space_name,
|
|
56
|
+
"meta",
|
|
57
|
+
data.shortname,
|
|
58
|
+
data.subpath,
|
|
59
|
+
)
|
|
60
|
+
meta_doc = await redis_services.get_doc_by_id(doc_id)
|
|
61
|
+
# Delete meta doc
|
|
62
|
+
await redis_services.delete_doc(
|
|
63
|
+
data.space_name,
|
|
64
|
+
"meta",
|
|
65
|
+
data.shortname,
|
|
66
|
+
data.subpath,
|
|
67
|
+
)
|
|
68
|
+
# Delete payload doc
|
|
69
|
+
await redis_services.delete_doc(
|
|
70
|
+
data.space_name,
|
|
71
|
+
(
|
|
72
|
+
meta_doc.get("payload", {}).get("schema_shortname", "meta")
|
|
73
|
+
if meta_doc
|
|
74
|
+
else "meta"
|
|
75
|
+
),
|
|
76
|
+
data.shortname,
|
|
77
|
+
data.subpath,
|
|
78
|
+
)
|
|
79
|
+
return
|
|
80
|
+
try:
|
|
81
|
+
meta = await db.load(
|
|
82
|
+
space_name=data.space_name,
|
|
83
|
+
subpath=data.subpath,
|
|
84
|
+
shortname=data.shortname,
|
|
85
|
+
class_type=class_type,
|
|
86
|
+
user_shortname=data.user_shortname,
|
|
87
|
+
)
|
|
88
|
+
except Exception as _:
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
if data.action_type in [
|
|
92
|
+
ActionType.create,
|
|
93
|
+
ActionType.update,
|
|
94
|
+
ActionType.progress_ticket,
|
|
95
|
+
]:
|
|
96
|
+
meta_doc_id, meta_json = redis_services.prepare_meta_doc(
|
|
97
|
+
data.space_name, data.subpath, meta
|
|
98
|
+
)
|
|
99
|
+
payload: dict[str, Any] = {}
|
|
100
|
+
if (
|
|
101
|
+
meta.payload
|
|
102
|
+
and meta.payload.content_type == ContentType.json
|
|
103
|
+
and meta.payload.body is not None
|
|
104
|
+
):
|
|
105
|
+
mypayload = await db.load_resource_payload(
|
|
106
|
+
space_name=data.space_name,
|
|
107
|
+
subpath=data.subpath,
|
|
108
|
+
filename=str(meta.payload.body),
|
|
109
|
+
class_type=class_type,
|
|
110
|
+
)
|
|
111
|
+
payload = mypayload if mypayload else {}
|
|
112
|
+
|
|
113
|
+
meta_json["payload_string"] = await generate_payload_string(
|
|
114
|
+
db,
|
|
115
|
+
space_name=data.space_name,
|
|
116
|
+
subpath=meta_json["subpath"],
|
|
117
|
+
shortname=meta_json["shortname"],
|
|
118
|
+
payload=payload,
|
|
119
|
+
) if settings.store_payload_string else ""
|
|
120
|
+
|
|
121
|
+
await redis_services.save_doc(meta_doc_id, meta_json)
|
|
122
|
+
if meta.payload:
|
|
123
|
+
payload.update(meta_json)
|
|
124
|
+
await redis_services.save_payload_doc(
|
|
125
|
+
data.space_name,
|
|
126
|
+
data.subpath,
|
|
127
|
+
meta,
|
|
128
|
+
payload,
|
|
129
|
+
data.resource_type,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
elif data.action_type == ActionType.move:
|
|
133
|
+
await redis_services.move_meta_doc(
|
|
134
|
+
data.space_name,
|
|
135
|
+
data.attributes["src_shortname"],
|
|
136
|
+
data.attributes["src_subpath"],
|
|
137
|
+
data.subpath,
|
|
138
|
+
meta,
|
|
139
|
+
)
|
|
140
|
+
if meta.payload and meta.payload.schema_shortname:
|
|
141
|
+
await redis_services.move_payload_doc(
|
|
142
|
+
data.space_name,
|
|
143
|
+
meta.payload.schema_shortname,
|
|
144
|
+
data.attributes["src_shortname"],
|
|
145
|
+
data.attributes["src_subpath"],
|
|
146
|
+
meta.shortname,
|
|
147
|
+
data.subpath,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
async def update_parent_entry_payload_string(self) -> None:
|
|
151
|
+
async with RedisServices() as redis_services:
|
|
152
|
+
# get the parent meta doc
|
|
153
|
+
subpath_parts = self.data.subpath.strip("/").split("/")
|
|
154
|
+
if len(subpath_parts) <= 1:
|
|
155
|
+
return
|
|
156
|
+
parent_subpath, parent_shortname = (
|
|
157
|
+
"/".join(subpath_parts[:-1]),
|
|
158
|
+
subpath_parts[-1],
|
|
159
|
+
)
|
|
160
|
+
doc_id = redis_services.generate_doc_id(
|
|
161
|
+
self.data.space_name,
|
|
162
|
+
"meta",
|
|
163
|
+
parent_shortname,
|
|
164
|
+
parent_subpath,
|
|
165
|
+
)
|
|
166
|
+
meta_doc: dict = await redis_services.get_doc_by_id(doc_id)
|
|
167
|
+
|
|
168
|
+
if meta_doc is None:
|
|
169
|
+
raise Exception("Meta doc not found")
|
|
170
|
+
|
|
171
|
+
payload = {}
|
|
172
|
+
if meta_doc.get("payload_doc_id"):
|
|
173
|
+
payload_doc = await redis_services.get_doc_by_id(
|
|
174
|
+
meta_doc["payload_doc_id"]
|
|
175
|
+
)
|
|
176
|
+
payload = {k: v for k, v in payload_doc.items() if k not in meta_doc}
|
|
177
|
+
|
|
178
|
+
# generate the payload string
|
|
179
|
+
meta_doc["payload_string"] = await generate_payload_string(
|
|
180
|
+
db,
|
|
181
|
+
space_name=self.data.space_name,
|
|
182
|
+
subpath=parent_subpath,
|
|
183
|
+
shortname=parent_shortname,
|
|
184
|
+
payload=payload,
|
|
185
|
+
) if settings.store_payload_string else ""
|
|
186
|
+
|
|
187
|
+
# update parent meta doc
|
|
188
|
+
await redis_services.save_doc(doc_id, meta_doc)
|
|
File without changes
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from models.core import Folder, PluginBase, Event
|
|
2
|
+
from models.enums import ResourceType
|
|
3
|
+
from data_adapters.file.redis_services import RedisServices
|
|
4
|
+
from fastapi.logger import logger
|
|
5
|
+
from data_adapters.adapter import data_adapter as db
|
|
6
|
+
from utils.settings import settings
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Plugin(PluginBase):
|
|
10
|
+
async def hook(self, data: Event):
|
|
11
|
+
# Type narrowing for PyRight
|
|
12
|
+
if (
|
|
13
|
+
not isinstance(data.shortname, str)
|
|
14
|
+
or not isinstance(data.resource_type, ResourceType)
|
|
15
|
+
or not isinstance(data.attributes, dict)
|
|
16
|
+
):
|
|
17
|
+
logger.error("invalid data at resource_folders_creation")
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
folders = []
|
|
21
|
+
if data.resource_type == ResourceType.user:
|
|
22
|
+
folders = [
|
|
23
|
+
("personal", "people", data.shortname),
|
|
24
|
+
("personal", f"people/{data.shortname}", "notifications"),
|
|
25
|
+
("personal", f"people/{data.shortname}", "private"),
|
|
26
|
+
("personal", f"people/{data.shortname}", "protected"),
|
|
27
|
+
("personal", f"people/{data.shortname}", "public"),
|
|
28
|
+
("personal", f"people/{data.shortname}", "inbox"),
|
|
29
|
+
]
|
|
30
|
+
elif data.resource_type == ResourceType.space:
|
|
31
|
+
# sys_schemas = ["meta_schema", "folder_rendering"]
|
|
32
|
+
# for schema_name in sys_schemas:
|
|
33
|
+
# await clone(
|
|
34
|
+
# src_space=settings.management_space,
|
|
35
|
+
# src_subpath="schema",
|
|
36
|
+
# src_shortname=schema_name,
|
|
37
|
+
# dest_space=data.shortname,
|
|
38
|
+
# dest_subpath="schema",
|
|
39
|
+
# dest_shortname=schema_name,
|
|
40
|
+
# class_type=Schema,
|
|
41
|
+
# )
|
|
42
|
+
if settings.active_data_db == "file":
|
|
43
|
+
async with RedisServices() as redis_services:
|
|
44
|
+
await redis_services.create_indices(
|
|
45
|
+
for_space=data.shortname,
|
|
46
|
+
# for_schemas=sys_schemas,
|
|
47
|
+
for_custom_indices=False,
|
|
48
|
+
del_docs=False,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# redis_update_plugin = RedisUpdatePlugin()
|
|
52
|
+
# for schema_name in sys_schemas:
|
|
53
|
+
# await redis_update_plugin.hook(Event(
|
|
54
|
+
# space_name=data.shortname,
|
|
55
|
+
# subpath="schema",
|
|
56
|
+
# shortname=schema_name,
|
|
57
|
+
# action_type=ActionType.create,
|
|
58
|
+
# resource_type=ResourceType.schema,
|
|
59
|
+
# user_shortname=data.user_shortname
|
|
60
|
+
# ))
|
|
61
|
+
|
|
62
|
+
folders = [(data.shortname, "/", "schema")]
|
|
63
|
+
|
|
64
|
+
for folder in folders:
|
|
65
|
+
existing_folder = await db.load_or_none(
|
|
66
|
+
space_name=folder[0],
|
|
67
|
+
subpath=folder[1],
|
|
68
|
+
shortname=folder[2],
|
|
69
|
+
class_type=Folder,
|
|
70
|
+
user_shortname=data.user_shortname,
|
|
71
|
+
)
|
|
72
|
+
if existing_folder is None:
|
|
73
|
+
await db.internal_save_model(
|
|
74
|
+
space_name=folder[0],
|
|
75
|
+
subpath=folder[1],
|
|
76
|
+
meta=Folder(
|
|
77
|
+
shortname=folder[2],
|
|
78
|
+
is_active=True,
|
|
79
|
+
owner_shortname=data.user_shortname,
|
|
80
|
+
),
|
|
81
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
from sys import modules as sys_modules
|
|
2
|
+
from uuid import uuid4
|
|
3
|
+
from models import api
|
|
4
|
+
from models.enums import ContentType, QueryType
|
|
5
|
+
from models.core import (
|
|
6
|
+
ActionType,
|
|
7
|
+
Content,
|
|
8
|
+
NotificationData,
|
|
9
|
+
Payload,
|
|
10
|
+
PluginBase,
|
|
11
|
+
Event,
|
|
12
|
+
Translation,
|
|
13
|
+
)
|
|
14
|
+
from utils.notification import NotificationManager
|
|
15
|
+
from utils.helpers import camel_case, replace_message_vars
|
|
16
|
+
from utils.settings import settings
|
|
17
|
+
from fastapi.logger import logger
|
|
18
|
+
from data_adapters.adapter import data_adapter as db
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Plugin(PluginBase):
|
|
22
|
+
async def hook(self, data: Event):
|
|
23
|
+
"""
|
|
24
|
+
after any action
|
|
25
|
+
1- get the matching SystemNotificationRequest for this action
|
|
26
|
+
2- generate list of users to send the notification to (based on the action entry):
|
|
27
|
+
2.1- entry.owner_shortname, entry.group.members?, and entry.collaborators?
|
|
28
|
+
3- send the notification to the list of generated users
|
|
29
|
+
"""
|
|
30
|
+
# Type narrowing for PyRight
|
|
31
|
+
if not isinstance(data.shortname, str):
|
|
32
|
+
logger.warning(
|
|
33
|
+
"data.shortname is None and str is required at system_notification_sender"
|
|
34
|
+
)
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
if data.action_type == ActionType.delete and data.attributes.get("entry"):
|
|
38
|
+
entry = data.attributes["entry"].model_dump()
|
|
39
|
+
else:
|
|
40
|
+
entry = (
|
|
41
|
+
await db.load(
|
|
42
|
+
data.space_name,
|
|
43
|
+
data.subpath,
|
|
44
|
+
data.shortname,
|
|
45
|
+
getattr(sys_modules["models.core"], camel_case(data.resource_type)),
|
|
46
|
+
data.user_shortname,
|
|
47
|
+
)
|
|
48
|
+
).model_dump()
|
|
49
|
+
if entry["payload"] is not None:
|
|
50
|
+
try:
|
|
51
|
+
entry["payload"]["body"] = await db.load_resource_payload(
|
|
52
|
+
space_name=data.space_name,
|
|
53
|
+
subpath=data.subpath,
|
|
54
|
+
filename=entry["payload"]["body"],
|
|
55
|
+
class_type=getattr(
|
|
56
|
+
sys_modules["models.core"], camel_case(data.resource_type)
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
logger.warning(
|
|
61
|
+
f"Failed to load payload for entry {data.space_name}/{data.subpath}/{data.shortname}: {e}"
|
|
62
|
+
)
|
|
63
|
+
entry["space_name"] = data.space_name
|
|
64
|
+
entry["resource_type"] = str(data.resource_type)
|
|
65
|
+
entry["subpath"] = data.subpath
|
|
66
|
+
# 1- get the matching SystemNotificationRequests
|
|
67
|
+
total, matching_notification_requests = await db.query(api.Query(
|
|
68
|
+
type=QueryType.search,
|
|
69
|
+
retrieve_json_payload=True,
|
|
70
|
+
space_name="management",
|
|
71
|
+
subpath="notifications/system",
|
|
72
|
+
search=f"@payload.body.on_space:{data.space_name} @payload.body.on_subpath:{data.subpath.lstrip('/')} @payload.body.on_action:{data.action_type}",
|
|
73
|
+
limit=30,
|
|
74
|
+
offset=0
|
|
75
|
+
), "dmart")
|
|
76
|
+
if total == 0:
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
sub_matching_notification_requests = matching_notification_requests[0].model_dump()
|
|
80
|
+
notification_dict = sub_matching_notification_requests
|
|
81
|
+
if (
|
|
82
|
+
"state" in entry
|
|
83
|
+
and notification_dict.get("on_state", "") != ""
|
|
84
|
+
and notification_dict["on_state"] != entry["state"]
|
|
85
|
+
):
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# 2- get list of subscribed users
|
|
90
|
+
notification_subscribers =[]
|
|
91
|
+
# if entry.get("collaborators", None):
|
|
92
|
+
# notification_subscribers.extend(entry["collaborators"].values())
|
|
93
|
+
|
|
94
|
+
data_owner_shortname = entry["owner_shortname"]
|
|
95
|
+
if entry.get("owner_group_shortname", None):
|
|
96
|
+
group_users = await db.get_group_users(entry["owner_group_shortname"])
|
|
97
|
+
notification_subscribers.extend(group_users)
|
|
98
|
+
if data_owner_shortname in notification_subscribers:
|
|
99
|
+
notification_subscribers.remove(data_owner_shortname)
|
|
100
|
+
users_objects: dict[str, dict] = {}
|
|
101
|
+
|
|
102
|
+
for subscriber in notification_subscribers:
|
|
103
|
+
users_objects[subscriber] = (await db.load(
|
|
104
|
+
settings.management_space,
|
|
105
|
+
settings.users_subpath,
|
|
106
|
+
subscriber,
|
|
107
|
+
getattr(sys_modules["models.core"], camel_case("user")),
|
|
108
|
+
data.user_shortname,
|
|
109
|
+
)).model_dump()
|
|
110
|
+
# 3- send the notification
|
|
111
|
+
notification_manager = NotificationManager()
|
|
112
|
+
|
|
113
|
+
formatted_req = await self.prepare_request(notification_dict, entry)
|
|
114
|
+
for receiver in set(notification_subscribers):
|
|
115
|
+
if not formatted_req["push_only"]:
|
|
116
|
+
notification_content = Content(
|
|
117
|
+
shortname=str(uuid4())[:8],
|
|
118
|
+
is_active=True,
|
|
119
|
+
displayname=notification_dict["attributes"]["displayname"],
|
|
120
|
+
description=notification_dict["attributes"]["description"],
|
|
121
|
+
owner_shortname=receiver,
|
|
122
|
+
payload=Payload(
|
|
123
|
+
content_type=ContentType.json,
|
|
124
|
+
body={
|
|
125
|
+
"type": "system",
|
|
126
|
+
"is_read": False,
|
|
127
|
+
"priority": notification_dict["attributes"]["payload"]["body"]["priority"],
|
|
128
|
+
"entry_space": entry["space_name"],
|
|
129
|
+
"entry_subpath": entry["subpath"],
|
|
130
|
+
"entry_shortname": entry["shortname"],
|
|
131
|
+
"resource_type": entry["resource_type"],
|
|
132
|
+
"created_by": data.user_shortname,
|
|
133
|
+
"action_type": str(data.action_type)
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
await db.internal_save_model(
|
|
138
|
+
space_name="personal",
|
|
139
|
+
subpath=f"people/{receiver}/notifications",
|
|
140
|
+
meta=notification_content
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
for platform in formatted_req["platforms"]:
|
|
144
|
+
await notification_manager.send(
|
|
145
|
+
platform=platform,
|
|
146
|
+
data=NotificationData(
|
|
147
|
+
receiver=users_objects[receiver],
|
|
148
|
+
title=formatted_req["title"],
|
|
149
|
+
body=formatted_req["body"],
|
|
150
|
+
image_urls=formatted_req["images_urls"],
|
|
151
|
+
deep_link=notification_dict.get("deep_link", {}),
|
|
152
|
+
entry_id=entry["shortname"],
|
|
153
|
+
),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
async def prepare_request(self, notification_dict: dict, entry: dict) -> dict:
|
|
157
|
+
for locale in ["ar", "en", "ku"]:
|
|
158
|
+
if "displayname" in notification_dict:
|
|
159
|
+
notification_dict["displayname"][locale] = replace_message_vars(
|
|
160
|
+
notification_dict["displayname"][locale], entry, locale
|
|
161
|
+
)
|
|
162
|
+
if "description" in notification_dict:
|
|
163
|
+
notification_dict["description"][locale] = replace_message_vars(
|
|
164
|
+
notification_dict["description"][locale], entry, locale
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Get Notification Request Images
|
|
168
|
+
attachments_path = (
|
|
169
|
+
settings.spaces_folder
|
|
170
|
+
/ f"{settings.management_space}"
|
|
171
|
+
f"/{notification_dict['subpath']}/.dm/{notification_dict['shortname']}"
|
|
172
|
+
)
|
|
173
|
+
notification_attachments = await db.get_entry_attachments(
|
|
174
|
+
subpath=f"{notification_dict['subpath']}/{notification_dict['shortname']}",
|
|
175
|
+
attachments_path=attachments_path,
|
|
176
|
+
)
|
|
177
|
+
notification_images = {
|
|
178
|
+
"en": notification_attachments.get("media", {}).get("en"),
|
|
179
|
+
"ar": notification_attachments.get("media", {}).get("ar"),
|
|
180
|
+
"ku": notification_attachments.get("media", {}).get("ku"),
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
"platforms": notification_dict.get("types", []),
|
|
184
|
+
"title": Translation(**notification_dict.get("displayname", {})),
|
|
185
|
+
"body": Translation(**notification_dict.get("description", {})),
|
|
186
|
+
"images_urls": Translation(**notification_images),
|
|
187
|
+
"push_only": notification_dict.get("push_only", False),
|
|
188
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from models.core import PluginBase, Event
|
|
2
|
+
from utils.access_control import access_control
|
|
3
|
+
from utils.settings import settings
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Plugin(PluginBase):
|
|
7
|
+
async def hook(self, data: Event):
|
|
8
|
+
if settings.active_data_db == "file":
|
|
9
|
+
await access_control.load_permissions_and_roles()
|
pytests/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from models.api import Error
|
|
3
|
+
from utils.internal_error_code import InternalErrorCode
|
|
4
|
+
from api.user.model.errors import INVALID_OTP, EXPIRED_OTP
|
|
5
|
+
|
|
6
|
+
def test_invalid_otp():
|
|
7
|
+
assert isinstance(INVALID_OTP, Error)
|
|
8
|
+
assert INVALID_OTP.type == "OTP"
|
|
9
|
+
assert INVALID_OTP.code == InternalErrorCode.OTP_INVALID
|
|
10
|
+
assert INVALID_OTP.message == "Invalid OTP"
|
|
11
|
+
|
|
12
|
+
def test_expired_otp():
|
|
13
|
+
assert isinstance(EXPIRED_OTP, Error)
|
|
14
|
+
assert EXPIRED_OTP.type == "OTP"
|
|
15
|
+
assert EXPIRED_OTP.code == InternalErrorCode.OTP_EXPIRED
|
|
16
|
+
assert EXPIRED_OTP.message == "Expired OTP"
|