dmart 1.4.35__py3-none-any.whl → 1.4.36__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.
Files changed (298) hide show
  1. dmart/__init__.py +8 -0
  2. dmart/alembic/README +1 -0
  3. dmart/alembic/notes.txt +11 -0
  4. dmart/alembic/script.py.mako +28 -0
  5. dmart/alembic.ini +117 -0
  6. dmart/config.env.sample +27 -0
  7. dmart/conftest.py +13 -0
  8. dmart/curl.sh +196 -0
  9. dmart/hypercorn_config.toml +3 -0
  10. dmart/info.json +1 -0
  11. dmart/login_creds.sh +7 -0
  12. dmart/login_creds.sh.sample +7 -0
  13. dmart/manifest.sh +12 -0
  14. dmart/plugins/action_log/config.json +13 -0
  15. dmart/plugins/admin_notification_sender/config.json +13 -0
  16. dmart/plugins/ldap_manager/config.json +12 -0
  17. dmart/plugins/ldap_manager/dmart.schema +146 -0
  18. dmart/plugins/ldap_manager/slapd.conf +53 -0
  19. dmart/plugins/local_notification/config.json +13 -0
  20. dmart/plugins/realtime_updates_notifier/config.json +12 -0
  21. dmart/plugins/redis_db_update/config.json +13 -0
  22. dmart/plugins/resource_folders_creation/config.json +12 -0
  23. dmart/plugins/system_notification_sender/config.json +13 -0
  24. dmart/plugins/update_access_controls/config.json +12 -0
  25. dmart/publish.sh +57 -0
  26. dmart/pylint.sh +16 -0
  27. dmart/pyrightconfig.json +7 -0
  28. dmart/redis_connections.sh +13 -0
  29. dmart/reload.sh +56 -0
  30. dmart/run.sh +3 -0
  31. dmart/test_utils.py +34 -0
  32. {dmart-1.4.35.dist-info → dmart-1.4.36.dist-info}/METADATA +1 -1
  33. dmart-1.4.36.dist-info/RECORD +285 -0
  34. dmart-1.4.36.dist-info/entry_points.txt +2 -0
  35. dmart-1.4.36.dist-info/top_level.txt +1 -0
  36. dmart-1.4.35.dist-info/RECORD +0 -264
  37. dmart-1.4.35.dist-info/entry_points.txt +0 -2
  38. dmart-1.4.35.dist-info/top_level.txt +0 -24
  39. pytests/api_user_models_erros_test.py +0 -16
  40. pytests/api_user_models_requests_test.py +0 -98
  41. pytests/archive_test.py +0 -72
  42. pytests/base_test.py +0 -300
  43. pytests/get_settings_test.py +0 -14
  44. pytests/json_to_db_migration_test.py +0 -237
  45. pytests/service_test.py +0 -26
  46. pytests/test_info.py +0 -55
  47. pytests/test_status.py +0 -15
  48. utils/__init__.py +0 -0
  49. {alembic → dmart/alembic}/__init__.py +0 -0
  50. {alembic → dmart/alembic}/env.py +0 -0
  51. {alembic → dmart/alembic}/scripts/__init__.py +0 -0
  52. {alembic → dmart/alembic}/scripts/calculate_checksums.py +0 -0
  53. {alembic → dmart/alembic}/scripts/migration_f7a4949eed19.py +0 -0
  54. {alembic → dmart/alembic}/versions/0f3d2b1a7c21_add_authz_materialized_views.py +0 -0
  55. {alembic → dmart/alembic}/versions/10d2041b94d4_last_checksum_history.py +0 -0
  56. {alembic → dmart/alembic}/versions/1cf4e1ee3cb8_ext_permission_with_filter_fields_values.py +0 -0
  57. {alembic → dmart/alembic}/versions/26bfe19b49d4_rm_failedloginattempts.py +0 -0
  58. {alembic → dmart/alembic}/versions/3c8bca2219cc_add_otp_table.py +0 -0
  59. {alembic → dmart/alembic}/versions/6675fd9dfe42_remove_unique_from_sessions_table.py +0 -0
  60. {alembic → dmart/alembic}/versions/71bc1df82e6a_adding_user_last_login_at.py +0 -0
  61. {alembic → dmart/alembic}/versions/74288ccbd3b5_initial.py +0 -0
  62. {alembic → dmart/alembic}/versions/7520a89a8467_rm_activesession_table.py +0 -0
  63. {alembic → dmart/alembic}/versions/848b623755a4_make_created_nd_updated_at_required.py +0 -0
  64. {alembic → dmart/alembic}/versions/8640dcbebf85_add_notes_to_users.py +0 -0
  65. {alembic → dmart/alembic}/versions/91c94250232a_adding_fk_on_owner_shortname.py +0 -0
  66. {alembic → dmart/alembic}/versions/98ecd6f56f9a_ext_meta_with_owner_group_shortname.py +0 -0
  67. {alembic → dmart/alembic}/versions/9aae9138c4ef_indexing_created_at_updated_at.py +0 -0
  68. {alembic → dmart/alembic}/versions/__init__.py +0 -0
  69. {alembic → dmart/alembic}/versions/b53f916b3f6d_json_to_jsonb.py +0 -0
  70. {alembic → dmart/alembic}/versions/eb5f1ec65156_adding_user_locked_to_device.py +0 -0
  71. {alembic → dmart/alembic}/versions/f7a4949eed19_adding_query_policies_to_meta.py +0 -0
  72. {api → dmart/api}/__init__.py +0 -0
  73. {api → dmart/api}/info/__init__.py +0 -0
  74. {api → dmart/api}/info/router.py +0 -0
  75. {api → dmart/api}/managed/__init__.py +0 -0
  76. {api → dmart/api}/managed/router.py +0 -0
  77. {api → dmart/api}/managed/utils.py +0 -0
  78. {api → dmart/api}/public/__init__.py +0 -0
  79. {api → dmart/api}/public/router.py +0 -0
  80. {api → dmart/api}/qr/__init__.py +0 -0
  81. {api → dmart/api}/qr/router.py +0 -0
  82. {api → dmart/api}/user/__init__.py +0 -0
  83. {api → dmart/api}/user/model/__init__.py +0 -0
  84. {api → dmart/api}/user/model/errors.py +0 -0
  85. {api → dmart/api}/user/model/requests.py +0 -0
  86. {api → dmart/api}/user/model/responses.py +0 -0
  87. {api → dmart/api}/user/router.py +0 -0
  88. {api → dmart/api}/user/service.py +0 -0
  89. /bundler.py → /dmart/bundler.py +0 -0
  90. {config → dmart/config}/__init__.py +0 -0
  91. {config → dmart/config}/channels.json +0 -0
  92. {config → dmart/config}/notification.json +0 -0
  93. {cxb → dmart/cxb}/__init__.py +0 -0
  94. {cxb → dmart/cxb}/assets/@codemirror-Rn7_6DkE.js +0 -0
  95. {cxb → dmart/cxb}/assets/@edraj-CS4NwVbD.js +0 -0
  96. {cxb → dmart/cxb}/assets/@floating-ui-BwwcF-xh.js +0 -0
  97. {cxb → dmart/cxb}/assets/@formatjs-yKEsAtjs.js +0 -0
  98. {cxb → dmart/cxb}/assets/@fortawesome-DRW1UCdr.js +0 -0
  99. {cxb → dmart/cxb}/assets/@jsonquerylang-laKNoFFq.js +0 -0
  100. {cxb → dmart/cxb}/assets/@lezer-za4Q-8Ew.js +0 -0
  101. {cxb → dmart/cxb}/assets/@marijn-DXwl3gUT.js +0 -0
  102. {cxb → dmart/cxb}/assets/@popperjs-l0sNRNKZ.js +0 -0
  103. {cxb → dmart/cxb}/assets/@replit--ERk53eB.js +0 -0
  104. {cxb → dmart/cxb}/assets/@roxi-CGMFK4i8.js +0 -0
  105. {cxb → dmart/cxb}/assets/@typewriter-cCzskkIv.js +0 -0
  106. {cxb → dmart/cxb}/assets/@zerodevx-BlBZjKxu.js +0 -0
  107. {cxb → dmart/cxb}/assets/@zerodevx-CVEpe6WZ.css +0 -0
  108. {cxb → dmart/cxb}/assets/BreadCrumbLite-DAhOx38v.js +0 -0
  109. {cxb → dmart/cxb}/assets/EntryRenderer-25YDhRen.js +0 -0
  110. {cxb → dmart/cxb}/assets/EntryRenderer-DXytdFp9.css +0 -0
  111. {cxb → dmart/cxb}/assets/ListView-BpAycA2h.js +0 -0
  112. {cxb → dmart/cxb}/assets/ListView-U8of-_c-.css +0 -0
  113. {cxb → dmart/cxb}/assets/Prism--hMplq-p.js +0 -0
  114. {cxb → dmart/cxb}/assets/Prism-Uh6uStUw.css +0 -0
  115. {cxb → dmart/cxb}/assets/Table2Cols-BsbwicQm.js +0 -0
  116. {cxb → dmart/cxb}/assets/_..-BvT6vdHa.css +0 -0
  117. {cxb → dmart/cxb}/assets/_...404_-fuLH_rX9.js +0 -0
  118. {cxb → dmart/cxb}/assets/_...fallback_-Ba_NLmAE.js +0 -0
  119. {cxb → dmart/cxb}/assets/_module-Bfk8MiCs.js +0 -0
  120. {cxb → dmart/cxb}/assets/_module-CEW0D5oI.js +0 -0
  121. {cxb → dmart/cxb}/assets/_module-Dgq0ZVtz.js +0 -0
  122. {cxb → dmart/cxb}/assets/ajv-Cpj98o6Y.js +0 -0
  123. {cxb → dmart/cxb}/assets/axios-CG2WSiiR.js +0 -0
  124. {cxb → dmart/cxb}/assets/clsx-B-dksMZM.js +0 -0
  125. {cxb → dmart/cxb}/assets/codemirror-wrapped-line-indent-DPhKvljI.js +0 -0
  126. {cxb → dmart/cxb}/assets/compare-C3AjiGFR.js +0 -0
  127. {cxb → dmart/cxb}/assets/compute-scroll-into-view-Bl8rNFhg.js +0 -0
  128. {cxb → dmart/cxb}/assets/consolite-DlCuI0F9.js +0 -0
  129. {cxb → dmart/cxb}/assets/crelt-C8TCjufn.js +0 -0
  130. {cxb → dmart/cxb}/assets/date-fns-l0sNRNKZ.js +0 -0
  131. {cxb → dmart/cxb}/assets/deepmerge-rn4rBaHU.js +0 -0
  132. {cxb → dmart/cxb}/assets/dmart_services-AL6-IdDE.js +0 -0
  133. {cxb → dmart/cxb}/assets/downloadFile-D08i0YDh.js +0 -0
  134. {cxb → dmart/cxb}/assets/easy-signal-BiPFIK3O.js +0 -0
  135. {cxb → dmart/cxb}/assets/esm-env-rsSWfq8L.js +0 -0
  136. {cxb → dmart/cxb}/assets/export-OF_rTiXu.js +0 -0
  137. {cxb → dmart/cxb}/assets/fast-deep-equal-l0sNRNKZ.js +0 -0
  138. {cxb → dmart/cxb}/assets/fast-diff-C-IidNf4.js +0 -0
  139. {cxb → dmart/cxb}/assets/fast-uri-l0sNRNKZ.js +0 -0
  140. {cxb → dmart/cxb}/assets/flowbite-svelte-BLvjb-sa.js +0 -0
  141. {cxb → dmart/cxb}/assets/flowbite-svelte-CD54FDqW.css +0 -0
  142. {cxb → dmart/cxb}/assets/flowbite-svelte-icons-BI8GVhw_.js +0 -0
  143. {cxb → dmart/cxb}/assets/github-slugger-CQ4oX9Ud.js +0 -0
  144. {cxb → dmart/cxb}/assets/global-igKv-1g9.js +0 -0
  145. {cxb → dmart/cxb}/assets/hookar-BMRD9G9H.js +0 -0
  146. {cxb → dmart/cxb}/assets/immutable-json-patch-DtRO2E_S.js +0 -0
  147. {cxb → dmart/cxb}/assets/import-1vE3gBat.js +0 -0
  148. {cxb → dmart/cxb}/assets/index-B-eTh-ZX.js +0 -0
  149. {cxb → dmart/cxb}/assets/index-BVyxzKtH.js +0 -0
  150. {cxb → dmart/cxb}/assets/index-BdeNM69f.js +0 -0
  151. {cxb → dmart/cxb}/assets/index-C6cPO4op.js +0 -0
  152. {cxb → dmart/cxb}/assets/index-CC-A1ipE.js +0 -0
  153. {cxb → dmart/cxb}/assets/index-CTxJ-lDp.js +0 -0
  154. {cxb → dmart/cxb}/assets/index-Cd-F5j_k.js +0 -0
  155. {cxb → dmart/cxb}/assets/index-D742rwaM.js +0 -0
  156. {cxb → dmart/cxb}/assets/index-DTfhnhwd.js +0 -0
  157. {cxb → dmart/cxb}/assets/index-DdXRK7n9.js +0 -0
  158. {cxb → dmart/cxb}/assets/index-DtiCmB4o.js +0 -0
  159. {cxb → dmart/cxb}/assets/index-NBrXBlLA.css +0 -0
  160. {cxb → dmart/cxb}/assets/index-ac-Buu_H.js +0 -0
  161. {cxb → dmart/cxb}/assets/index-iYkH7C67.js +0 -0
  162. {cxb → dmart/cxb}/assets/info-B986lRiM.js +0 -0
  163. {cxb → dmart/cxb}/assets/intl-messageformat-Dc5UU-HB.js +0 -0
  164. {cxb → dmart/cxb}/assets/jmespath-l0sNRNKZ.js +0 -0
  165. {cxb → dmart/cxb}/assets/json-schema-traverse-l0sNRNKZ.js +0 -0
  166. {cxb → dmart/cxb}/assets/json-source-map-DRgZidqy.js +0 -0
  167. {cxb → dmart/cxb}/assets/jsonpath-plus-l0sNRNKZ.js +0 -0
  168. {cxb → dmart/cxb}/assets/jsonrepair-B30Dx381.js +0 -0
  169. {cxb → dmart/cxb}/assets/lodash-es-DZVAA2ox.js +0 -0
  170. {cxb → dmart/cxb}/assets/marked-DKjyhwJX.js +0 -0
  171. {cxb → dmart/cxb}/assets/marked-gfm-heading-id-U5zO829x.js +0 -0
  172. {cxb → dmart/cxb}/assets/marked-mangle-CDMeiHC6.js +0 -0
  173. {cxb → dmart/cxb}/assets/memoize-one-BdPwpGay.js +0 -0
  174. {cxb → dmart/cxb}/assets/natural-compare-lite-Bg2Xcf-o.js +0 -0
  175. {cxb → dmart/cxb}/assets/pagination-svelte-D5CyoiE_.js +0 -0
  176. {cxb → dmart/cxb}/assets/pagination-svelte-v10nAbbM.css +0 -0
  177. {cxb → dmart/cxb}/assets/plantuml-encoder-C47mzt9T.js +0 -0
  178. {cxb → dmart/cxb}/assets/prismjs-DTUiLGJu.js +0 -0
  179. {cxb → dmart/cxb}/assets/profile-BUf-tKMe.js +0 -0
  180. {cxb → dmart/cxb}/assets/query-CNmXTsgf.js +0 -0
  181. {cxb → dmart/cxb}/assets/queryHelpers-C9iBWwqe.js +0 -0
  182. {cxb → dmart/cxb}/assets/scroll-into-view-if-needed-KR58zyjF.js +0 -0
  183. {cxb → dmart/cxb}/assets/spaces-0oyGvpii.js +0 -0
  184. {cxb → dmart/cxb}/assets/style-mod-Bs6eFhZE.js +0 -0
  185. {cxb → dmart/cxb}/assets/svelte-B2XmcTi_.js +0 -0
  186. {cxb → dmart/cxb}/assets/svelte-awesome-COLlx0DN.css +0 -0
  187. {cxb → dmart/cxb}/assets/svelte-awesome-DhnMA6Q_.js +0 -0
  188. {cxb → dmart/cxb}/assets/svelte-datatables-net-CY7LBj6I.js +0 -0
  189. {cxb → dmart/cxb}/assets/svelte-floating-ui-BlS3sOAQ.js +0 -0
  190. {cxb → dmart/cxb}/assets/svelte-i18n-CT2KkQaN.js +0 -0
  191. {cxb → dmart/cxb}/assets/svelte-jsoneditor-BzfX6Usi.css +0 -0
  192. {cxb → dmart/cxb}/assets/svelte-jsoneditor-CUGSvWId.js +0 -0
  193. {cxb → dmart/cxb}/assets/svelte-select-CegQKzqH.css +0 -0
  194. {cxb → dmart/cxb}/assets/svelte-select-CjHAt_85.js +0 -0
  195. {cxb → dmart/cxb}/assets/tailwind-merge-CJvxXMcu.js +0 -0
  196. {cxb → dmart/cxb}/assets/tailwind-variants-Cj20BoQ3.js +0 -0
  197. {cxb → dmart/cxb}/assets/toast-B9WDyfyI.js +0 -0
  198. {cxb → dmart/cxb}/assets/tslib-pJfR_DrR.js +0 -0
  199. {cxb → dmart/cxb}/assets/typewriter-editor-DkTVIJdm.js +0 -0
  200. {cxb → dmart/cxb}/assets/user-DeK_NB5v.js +0 -0
  201. {cxb → dmart/cxb}/assets/vanilla-picker-l5rcX3cq.js +0 -0
  202. {cxb → dmart/cxb}/assets/w3c-keyname-Vcq4gwWv.js +0 -0
  203. {cxb → dmart/cxb}/config.json +0 -0
  204. {cxb → dmart/cxb}/config.sample.json +0 -0
  205. {cxb → dmart/cxb}/favicon.ico +0 -0
  206. {cxb → dmart/cxb}/favicon.png +0 -0
  207. {cxb → dmart/cxb}/index.html +0 -0
  208. {data_adapters → dmart/data_adapters}/__init__.py +0 -0
  209. {data_adapters → dmart/data_adapters}/adapter.py +0 -0
  210. {data_adapters → dmart/data_adapters}/base_data_adapter.py +0 -0
  211. {data_adapters → dmart/data_adapters}/file/__init__.py +0 -0
  212. {data_adapters → dmart/data_adapters}/file/adapter.py +0 -0
  213. {data_adapters → dmart/data_adapters}/file/adapter_helpers.py +0 -0
  214. {data_adapters → dmart/data_adapters}/file/archive.py +0 -0
  215. {data_adapters → dmart/data_adapters}/file/create_index.py +0 -0
  216. {data_adapters → dmart/data_adapters}/file/create_users_folders.py +0 -0
  217. {data_adapters → dmart/data_adapters}/file/custom_validations.py +0 -0
  218. {data_adapters → dmart/data_adapters}/file/drop_index.py +0 -0
  219. {data_adapters → dmart/data_adapters}/file/health_check.py +0 -0
  220. {data_adapters → dmart/data_adapters}/file/redis_services.py +0 -0
  221. {data_adapters → dmart/data_adapters}/helpers.py +0 -0
  222. {data_adapters → dmart/data_adapters}/sql/__init__.py +0 -0
  223. {data_adapters → dmart/data_adapters}/sql/adapter.py +0 -0
  224. {data_adapters → dmart/data_adapters}/sql/adapter_helpers.py +0 -0
  225. {data_adapters → dmart/data_adapters}/sql/create_tables.py +0 -0
  226. {data_adapters → dmart/data_adapters}/sql/create_users_folders.py +0 -0
  227. {data_adapters → dmart/data_adapters}/sql/db_to_json_migration.py +0 -0
  228. {data_adapters → dmart/data_adapters}/sql/health_check_sql.py +0 -0
  229. {data_adapters → dmart/data_adapters}/sql/json_to_db_migration.py +0 -0
  230. {data_adapters → dmart/data_adapters}/sql/update_query_policies.py +0 -0
  231. /data_generator.py → /dmart/data_generator.py +0 -0
  232. /dmart.py → /dmart/dmart.py +0 -0
  233. /get_settings.py → /dmart/get_settings.py +0 -0
  234. {languages → dmart/languages}/__init__.py +0 -0
  235. {languages → dmart/languages}/arabic.json +0 -0
  236. {languages → dmart/languages}/english.json +0 -0
  237. {languages → dmart/languages}/kurdish.json +0 -0
  238. {languages → dmart/languages}/loader.py +0 -0
  239. /main.py → /dmart/main.py +0 -0
  240. /migrate.py → /dmart/migrate.py +0 -0
  241. {models → dmart/models}/__init__.py +0 -0
  242. {models → dmart/models}/api.py +0 -0
  243. {models → dmart/models}/core.py +0 -0
  244. {models → dmart/models}/enums.py +0 -0
  245. /password_gen.py → /dmart/password_gen.py +0 -0
  246. {plugins → dmart/plugins}/__init__.py +0 -0
  247. {plugins → dmart/plugins}/action_log/__init__.py +0 -0
  248. {plugins → dmart/plugins}/action_log/plugin.py +0 -0
  249. {plugins → dmart/plugins}/admin_notification_sender/__init__.py +0 -0
  250. {plugins → dmart/plugins}/admin_notification_sender/plugin.py +0 -0
  251. {plugins → dmart/plugins}/ldap_manager/__init__.py +0 -0
  252. {plugins → dmart/plugins}/ldap_manager/plugin.py +0 -0
  253. {plugins → dmart/plugins}/local_notification/__init__.py +0 -0
  254. {plugins → dmart/plugins}/local_notification/plugin.py +0 -0
  255. {plugins → dmart/plugins}/realtime_updates_notifier/__init__.py +0 -0
  256. {plugins → dmart/plugins}/realtime_updates_notifier/plugin.py +0 -0
  257. {plugins → dmart/plugins}/redis_db_update/__init__.py +0 -0
  258. {plugins → dmart/plugins}/redis_db_update/plugin.py +0 -0
  259. {plugins → dmart/plugins}/resource_folders_creation/__init__.py +0 -0
  260. {plugins → dmart/plugins}/resource_folders_creation/plugin.py +0 -0
  261. {plugins → dmart/plugins}/system_notification_sender/__init__.py +0 -0
  262. {plugins → dmart/plugins}/system_notification_sender/plugin.py +0 -0
  263. {plugins → dmart/plugins}/update_access_controls/__init__.py +0 -0
  264. {plugins → dmart/plugins}/update_access_controls/plugin.py +0 -0
  265. /run_notification_campaign.py → /dmart/run_notification_campaign.py +0 -0
  266. /scheduled_notification_handler.py → /dmart/scheduled_notification_handler.py +0 -0
  267. /schema_migration.py → /dmart/schema_migration.py +0 -0
  268. /schema_modulate.py → /dmart/schema_modulate.py +0 -0
  269. /set_admin_passwd.py → /dmart/set_admin_passwd.py +0 -0
  270. /sync.py → /dmart/sync.py +0 -0
  271. {pytests → dmart/utils}/__init__.py +0 -0
  272. {utils → dmart/utils}/access_control.py +0 -0
  273. {utils → dmart/utils}/async_request.py +0 -0
  274. {utils → dmart/utils}/exporter.py +0 -0
  275. {utils → dmart/utils}/firebase_notifier.py +0 -0
  276. {utils → dmart/utils}/generate_email.py +0 -0
  277. {utils → dmart/utils}/helpers.py +0 -0
  278. {utils → dmart/utils}/hypercorn_config.py +0 -0
  279. {utils → dmart/utils}/internal_error_code.py +0 -0
  280. {utils → dmart/utils}/jwt.py +0 -0
  281. {utils → dmart/utils}/logger.py +0 -0
  282. {utils → dmart/utils}/middleware.py +0 -0
  283. {utils → dmart/utils}/notification.py +0 -0
  284. {utils → dmart/utils}/password_hashing.py +0 -0
  285. {utils → dmart/utils}/plugin_manager.py +0 -0
  286. {utils → dmart/utils}/query_policies_helper.py +0 -0
  287. {utils → dmart/utils}/regex.py +0 -0
  288. {utils → dmart/utils}/repository.py +0 -0
  289. {utils → dmart/utils}/router_helper.py +0 -0
  290. {utils → dmart/utils}/settings.py +0 -0
  291. {utils → dmart/utils}/sms_notifier.py +0 -0
  292. {utils → dmart/utils}/social_sso.py +0 -0
  293. {utils → dmart/utils}/templates/activation.html.j2 +0 -0
  294. {utils → dmart/utils}/templates/reminder.html.j2 +0 -0
  295. {utils → dmart/utils}/ticket_sys_utils.py +0 -0
  296. {utils → dmart/utils}/web_notifier.py +0 -0
  297. /websocket.py → /dmart/websocket.py +0 -0
  298. {dmart-1.4.35.dist-info → dmart-1.4.36.dist-info}/WHEEL +0 -0
pytests/base_test.py DELETED
@@ -1,300 +0,0 @@
1
- import json
2
- from utils.settings import settings
3
- from fastapi import status
4
- from models.api import Query
5
- from models.enums import QueryType, ResourceType, RequestType
6
-
7
- superman = {}
8
- alibaba = {}
9
-
10
- with open("./login_creds.sh", "r") as file:
11
- for line in file.readlines():
12
- if line.strip().startswith("export SUPERMAN"):
13
- superman = json.loads(str(line.strip().split("'")[1]))
14
- if line.strip().startswith("export ALIBABA"):
15
- alibaba = json.loads(str(line.strip().split("'")[1]))
16
-
17
- MANAGEMENT_SPACE: str = f"{settings.management_space}"
18
- USERS_SUBPATH: str = "users"
19
- DEMO_SPACE: str = "test"
20
- DEMO_SUBPATH: str = "content"
21
-
22
-
23
- async def get_superman_cookie(client) -> str:
24
- response = await client.post(
25
- "/user/login",
26
- json={"shortname": superman["shortname"], "password": superman["password"]},
27
- )
28
- print(f"\n {response.json() = } \n creds: {superman = } \n")
29
- assert response.status_code == status.HTTP_200_OK
30
- # client.cookies.set("auth_token", response.cookies["auth_token"])
31
- return str(response.cookies["auth_token"])
32
-
33
- async def set_superman_cookie(client):
34
- response = await client.post(
35
- "/user/login",
36
- json={"shortname": superman["shortname"], "password": superman["password"]},
37
- )
38
- print(f"\n {response.json() = } \n creds: {superman = } \n")
39
- assert response.status_code == status.HTTP_200_OK
40
- client.cookies.set("auth_token", response.cookies["auth_token"])
41
-
42
-
43
- async def set_alibaba_cookie(client):
44
- response = await client.post(
45
- "/user/login",
46
- json={"shortname": alibaba["shortname"], "password": alibaba["password"]},
47
- )
48
- print(f"\n {response.json() = } \n creds: {alibaba = } \n")
49
- assert response.status_code == status.HTTP_200_OK
50
- client.cookies.set("auth_token", response.cookies["auth_token"])
51
-
52
-
53
- async def init_test_db(client) -> None:
54
- # Create the space
55
- await client.post(
56
- "managed/request",
57
- json={
58
- "space_name": DEMO_SPACE,
59
- "request_type": RequestType.create,
60
- "records": [
61
- {
62
- "resource_type": ResourceType.space,
63
- "subpath": "/",
64
- "shortname": DEMO_SPACE,
65
- "attributes": {},
66
- }
67
- ],
68
- },
69
- )
70
-
71
- # Create the folder
72
- await client.post(
73
- "/managed/request",
74
- json={
75
- "space_name": DEMO_SPACE,
76
- "request_type": RequestType.create,
77
- "records": [
78
- {
79
- "resource_type": ResourceType.folder,
80
- "subpath": "/",
81
- "shortname": DEMO_SUBPATH,
82
- "attributes": {},
83
- }
84
- ],
85
- },
86
- )
87
-
88
-
89
- async def delete_space(client) -> None:
90
- headers = {"Content-Type": "application/json"}
91
- endpoint = "/managed/request"
92
- request_data = {
93
- "space_name": DEMO_SPACE,
94
- "request_type": RequestType.delete,
95
- "records": [
96
- {
97
- "resource_type": ResourceType.space,
98
- "subpath": "/",
99
- "shortname": DEMO_SPACE,
100
- "attributes": {},
101
- }
102
- ],
103
- }
104
-
105
- assert_code_and_status_success(
106
- await client.post(endpoint, json=request_data, headers=headers)
107
- )
108
- check_not_found(
109
- await client.get(f"/managed/entry/space/{DEMO_SPACE}/__root__/{DEMO_SPACE}")
110
- )
111
-
112
-
113
- def check_repeated_shortname(response):
114
- json_response = response.json()
115
- assert response.status_code == status.HTTP_400_BAD_REQUEST
116
- assert "failed" == json_response.get("status")
117
- assert "request" == json_response.get("error", {}).get("type")
118
-
119
-
120
- def check_not_found(response):
121
- json_response = response.json()
122
- assert response.status_code == status.HTTP_404_NOT_FOUND
123
- assert "failed" == json_response.get("status")
124
- assert "db" == json_response.get("error").get("type")
125
-
126
-
127
- def check_unauthorized(response):
128
- json_response = response.json()
129
- assert response.status_code == status.HTTP_401_UNAUTHORIZED
130
- assert "failed" == json_response.get("status")
131
- assert "auth" == json_response.get("error", {}).get("type")
132
-
133
-
134
- def assert_code_and_status_success(response):
135
- if response.status_code != status.HTTP_200_OK:
136
- print(
137
- "\n\n\n\n\n========================= ERROR RESPONSE: =========================n:",
138
- response.json(),
139
- "\n\n\n\n\n",
140
- )
141
- json_response = response.json()
142
- print(f"{json_response=}")
143
- assert response.status_code == status.HTTP_200_OK
144
- assert json_response.get("status") == "success"
145
-
146
-
147
- def assert_bad_request(response):
148
- assert response.status_code == status.HTTP_400_BAD_REQUEST
149
- assert response.json()["status"] == "failed"
150
-
151
-
152
- async def assert_resource_created(
153
- client,
154
- query: Query,
155
- res_shortname: str,
156
- res_subpath: str,
157
- res_attributes: dict | None = None,
158
- res_attachments: dict[str, int] | None = None,
159
- ):
160
- if not query.search:
161
- query.search = ""
162
- response = await client.post(
163
- "/managed/query",
164
- json=query.model_dump(exclude_none=True),
165
- )
166
- assert_code_and_status_success(response)
167
- json_response = response.json()
168
- assert json_response["status"] == "success"
169
- assert json_response["attributes"]["returned"] == query.limit
170
- assert json_response["records"][0]["shortname"] == res_shortname
171
- assert json_response["records"][0]["subpath"] in [res_subpath, f"/{res_subpath}"]
172
- if res_attributes:
173
- if "is_active" not in res_attributes:
174
- res_attributes["is_active"] = False
175
- if "tags" not in res_attributes:
176
- res_attributes["tags"] = []
177
- res_attributes["owner_shortname"] = "dmart"
178
-
179
- json_response["records"][0]["attributes"].pop("created_at", None)
180
- json_response["records"][0]["attributes"].pop("updated_at", None)
181
- assert (
182
- json_response["records"][0]["attributes"]["payload"]["body"]
183
- == res_attributes["payload"]["body"]
184
- )
185
-
186
- # Assert correct attachments number for each attachment type returned
187
- if res_attachments:
188
- for attachment_key, attachments in json_response["records"][0][
189
- "attachments"
190
- ].items():
191
- if attachment_key in res_attachments:
192
- assert len(attachments) == res_attachments[attachment_key]
193
-
194
-
195
- async def assert_resource_deleted(client, space: str, subpath: str, shortname: str):
196
- query = Query(
197
- type=QueryType.search,
198
- space_name=space,
199
- subpath=subpath,
200
- search="",
201
- filter_shortnames=[shortname],
202
- retrieve_json_payload=True,
203
- limit=1,
204
- )
205
- response = await client.post("/managed/query", json=query.model_dump(exclude_none=True))
206
- assert_code_and_status_success(response)
207
- assert response.json()["status"] == "success"
208
- assert response.json()["attributes"]["returned"] == 0
209
-
210
-
211
- async def upload_resource_with_payload(
212
- client,
213
- space_name,
214
- record_path: str,
215
- payload_path: str,
216
- payload_type,
217
- attachment=False,
218
- is_fail=False,
219
- ):
220
- with open(record_path, "rb") as request_file, open(
221
- payload_path, "rb"
222
- ) as media_file:
223
- files = {
224
- "request_record": ("record.json", request_file, "application/json"),
225
- "payload_file": (media_file.name.split("/")[-1], media_file, payload_type),
226
- }
227
- response = await client.post(
228
- "managed/resource_with_payload",
229
- headers={},
230
- data={"space_name": space_name},
231
- files=files,
232
- )
233
-
234
- if is_fail:
235
- assert response.status_code == status.HTTP_400_BAD_REQUEST
236
- else:
237
- assert_code_and_status_success(response)
238
-
239
- if attachment:
240
- with open(record_path, 'r') as record_file:
241
- record_data = json.loads(record_file.read())
242
- subpath_parts = record_data["subpath"].split('/')
243
- attach_parent_subpath, attach_parent_shortname = "/".join(subpath_parts[:-1]), subpath_parts[-1]
244
- await assert_resource_created(
245
- client,
246
- query=Query(
247
- type=QueryType.search,
248
- space_name=space_name,
249
- subpath=attach_parent_subpath,
250
- filter_shortnames=[attach_parent_shortname],
251
- retrieve_json_payload=True,
252
- retrieve_attachments=True,
253
- limit=1,
254
- ),
255
- res_shortname=attach_parent_shortname,
256
- res_subpath=attach_parent_subpath,
257
- res_attachments={"media": 1},
258
- )
259
-
260
-
261
- async def delete_resource(client, resource_type: str, del_subpath: str, del_shortname: str):
262
- headers = {"Content-Type": "application/json"}
263
- endpoint = "/managed/request"
264
- request_data = {
265
- "space_name": DEMO_SPACE,
266
- "request_type": RequestType.delete,
267
- "records": [
268
- {
269
- "resource_type": resource_type,
270
- "subpath": del_subpath,
271
- "shortname": del_shortname,
272
- "attributes": {},
273
- }
274
- ],
275
- }
276
-
277
- response = await client.post(endpoint, json=request_data, headers=headers)
278
- assert_code_and_status_success(response)
279
-
280
-
281
- async def retrieve_content_folder(client):
282
- response = await client.get(f"managed/entry/folder/{DEMO_SPACE}/{settings.root_subpath_mw}/{DEMO_SUBPATH}")
283
-
284
- assert response.status_code == status.HTTP_200_OK
285
-
286
- await assert_resource_created(
287
- client,
288
- query=Query(
289
- type=QueryType.search,
290
- space_name=DEMO_SPACE,
291
- subpath="/",
292
- filter_shortnames=[DEMO_SUBPATH],
293
- filter_types=[ResourceType.folder],
294
- retrieve_json_payload=True,
295
- limit=1,
296
- ),
297
- res_shortname=DEMO_SUBPATH,
298
- res_subpath="/",
299
- res_attributes={},
300
- )
@@ -1,14 +0,0 @@
1
- # test_script.py
2
- import subprocess
3
- import pytest
4
-
5
-
6
- @pytest.mark.run(order=4)
7
- def test_script_execution():
8
- result = subprocess.run(
9
- ['python3', 'get_settings.py'],
10
- capture_output=True,
11
- text=True,
12
- check=True
13
- )
14
- assert result.returncode == 0
@@ -1,237 +0,0 @@
1
- import time
2
- from uuid import uuid4
3
- import pytest
4
- import os
5
- import json
6
- from pathlib import Path
7
- from sqlmodel import Session, create_engine, text, SQLModel
8
- from data_adapters.sql.create_tables import Attachments, Entries, Spaces, Histories
9
- from sqlalchemy.exc import OperationalError
10
- from utils.settings import settings
11
-
12
-
13
- def subpath_checker(subpath: str):
14
- if subpath.endswith("/"):
15
- subpath = subpath[:-1]
16
- if not subpath.startswith("/"):
17
- subpath = '/' + subpath
18
- return subpath
19
-
20
-
21
- def connect_with_retry(engine, retries=5, delay=2):
22
- """
23
- Try to connect to the database with retries.
24
- """
25
- for attempt in range(retries):
26
- try:
27
- with engine.connect() as _:
28
- print(f"Connected to the database on attempt {attempt + 1}")
29
- return
30
- except OperationalError as e:
31
- print(f"Connection attempt {attempt + 1} failed: {e}")
32
- time.sleep(delay)
33
- raise Exception("Could not connect to the database after multiple attempts")
34
-
35
-
36
- @pytest.fixture(scope="module")
37
- def setup_database():
38
- if settings.active_data_db == "file":
39
- pytest.skip("Skipping test for file-based database")
40
- return
41
-
42
- # Use the settings to connect with the main `postgres` user
43
- postgresql_url = f"{settings.database_driver.replace('+asyncpg','+psycopg')}://{settings.database_username}:{settings.database_password}@{settings.database_host}:{settings.database_port}"
44
- engine = create_engine(f"{postgresql_url}/postgres", echo=False, isolation_level="AUTOCOMMIT")
45
-
46
- # Create the database
47
- with Session(engine) as session:
48
- try:
49
- session.exec(text(f"DROP DATABASE IF EXISTS {settings.database_name}"))
50
- session.commit()
51
- session.exec(text(f"CREATE DATABASE {settings.database_name}"))
52
- session.commit() # Ensure the transaction is fully committed
53
- print(f"Database {settings.database_name} created successfully")
54
- except Exception as e:
55
- print(f"Database creation failed: {e}")
56
-
57
- # Add a small delay to ensure the database is fully ready
58
- time.sleep(2)
59
-
60
- yield
61
-
62
- # Drop the database after tests
63
- with Session(engine) as session:
64
- try:
65
- session.exec(text(f"DROP DATABASE IF EXISTS {settings.database_name}"))
66
- session.commit()
67
- print(f"Database {settings.database_name} dropped successfully")
68
- except Exception as e:
69
- print(f"Database deletion failed: {e}")
70
-
71
- engine.dispose()
72
-
73
-
74
- @pytest.fixture(scope="module")
75
- def setup_environment(setup_database):
76
- if settings.active_data_db == "file":
77
- pytest.skip("Skipping test for file-based database")
78
- return
79
-
80
- # Set the database name from settings
81
- driver = settings.database_driver.replace('+asyncpg', '+psycopg')
82
- postgresql_url = f"{driver}://{settings.database_username}:{settings.database_password}@{settings.database_host}:{settings.database_port}"
83
- engine = create_engine(f"{postgresql_url}/{settings.database_name}", echo=False)
84
-
85
- # Retry connecting to the newly created database
86
- connect_with_retry(engine)
87
-
88
- # Generate tables after ensuring connection
89
- postgresql_url = f"{driver}://{settings.database_username}:{settings.database_password}@{settings.database_host}:{settings.database_port}/{settings.database_name}"
90
- engine = create_engine(postgresql_url, echo=False)
91
- SQLModel.metadata.create_all(engine)
92
-
93
- yield engine
94
-
95
- engine.dispose()
96
-
97
-
98
- def test_json_to_db_migration(setup_environment):
99
- if settings.active_data_db == "file":
100
- pytest.skip("Skipping test for file-based database")
101
- return
102
-
103
- engine = setup_environment
104
-
105
- # Create a complex mock directory structure and files for different entry types
106
- os.makedirs('/tmp/test_space/.dm', exist_ok=True)
107
- with open('/tmp/test_space/.dm/meta.space.json', 'w') as f:
108
- json.dump({"key": "value"}, f)
109
-
110
- # Create more directories and files for the migration
111
- os.makedirs('/tmp/test_space/dir1', exist_ok=True)
112
- with open('/tmp/test_space/dir1/history.jsonl', 'w') as f:
113
- f.write(json.dumps({"key": "history"}) + '\n')
114
-
115
- # Create attachments folder and files
116
- os.makedirs('/tmp/test_space/dir1/attachments', exist_ok=True)
117
- with open('/tmp/test_space/dir1/attachments/meta.attachments.json', 'w') as f:
118
- json.dump({
119
- "uuid": str(uuid4()),
120
- "space_name": "test_space",
121
- "subpath": "/dir1",
122
- "acl": [],
123
- "relationships": [],
124
- "payload": {"body": "attachment content"}
125
- }, f)
126
-
127
- # Create ticket-related file
128
- with open('/tmp/test_space/dir1/meta.ticket.json', 'w') as f:
129
- json.dump({
130
- "state": "open",
131
- "is_open": True,
132
- "reporter": "user1",
133
- "subpath": "/dir1/ticket"
134
- }, f)
135
-
136
- # Create user meta file
137
- with open('/tmp/test_space/.dm/meta.user.json', 'w') as f:
138
- json.dump({
139
- "resource_type": "user",
140
- "firebase_token": "firebase_token",
141
- "language": "en"
142
- }, f)
143
-
144
- # Create role meta file
145
- with open('/tmp/test_space/.dm/meta.role.json', 'w') as f:
146
- json.dump({
147
- "resource_type": "role",
148
- "permissions": ["read", "write"]
149
- }, f)
150
-
151
- # Create permission meta file
152
- with open('/tmp/test_space/.dm/meta.permission.json', 'w') as f:
153
- json.dump({
154
- "resource_type": "permission",
155
- "subpaths": {"read": "/read", "write": "/write"},
156
- "resource_types": ["user", "role"]
157
- }, f)
158
-
159
- # Run the migration script
160
- try:
161
- with Session(engine) as session:
162
- for root, dirs, _ in os.walk('/tmp/test_space'):
163
- tmp = root.replace('/tmp/test_space', '')
164
- if tmp == '':
165
- continue
166
- if tmp[0] == '/':
167
- tmp = tmp[1:]
168
- space_name = tmp.split('/')[0]
169
- subpath = '/'.join(tmp.split('/')[1:])
170
- if space_name == '..':
171
- continue
172
-
173
- if space_name.startswith('.git'):
174
- continue
175
-
176
- if subpath == '' or subpath == '/':
177
- subpath = '/'
178
- p = os.path.join(root, '.dm', 'meta.space.json')
179
- entry = {}
180
- if Path(p).is_file():
181
- entry = json.load(open(p))
182
- entry['space_name'] = space_name
183
- entry['subpath'] = '/'
184
- session.add(Spaces.model_validate(entry))
185
- continue
186
-
187
- subpath = subpath.replace('.dm', '')
188
- if subpath != '/' and subpath.endswith('/'):
189
- subpath = subpath[:-1]
190
-
191
- if subpath == '':
192
- subpath = '/'
193
-
194
- for dir in dirs:
195
- for file in os.listdir(os.path.join(root, dir)):
196
- if not file.startswith('meta'):
197
- if file == 'history.jsonl':
198
- lines = open(os.path.join(root, dir, file), 'r').readlines()
199
- for line in lines:
200
- history = json.loads(line)
201
- history['shortname'] = dir
202
- history['space_name'] = space_name
203
- history['subpath'] = subpath_checker(subpath)
204
- session.add(Histories.model_validate(history))
205
- continue
206
-
207
- p = os.path.join(root, dir, file)
208
- if Path(p).is_file():
209
- if 'attachments' in p:
210
- _attachment = json.load(open(os.path.join(root, dir, file)))
211
- _attachment['space_name'] = space_name
212
- _attachment['uuid'] = _attachment.get('uuid', uuid4())
213
- _attachment['subpath'] = subpath_checker(_attachment['subpath'])
214
- session.add(Attachments.model_validate(_attachment))
215
- elif file.endswith('.json'):
216
- entry = json.load(open(p))
217
- entry['space_name'] = space_name
218
- entry['subpath'] = subpath_checker(subpath)
219
- session.add(Entries.model_validate(entry))
220
- session.commit()
221
- assert True # Assert that the migration completes without error
222
- except Exception as e:
223
- print(f"Migration failed: {e}")
224
- assert False # Fail the test if there is any exception
225
-
226
- # Clean up the mock directory structure
227
- os.remove('/tmp/test_space/.dm/meta.space.json')
228
- os.remove('/tmp/test_space/dir1/history.jsonl')
229
- os.remove('/tmp/test_space/dir1/attachments/meta.attachments.json')
230
- os.remove('/tmp/test_space/dir1/meta.ticket.json')
231
- os.remove('/tmp/test_space/.dm/meta.user.json')
232
- os.remove('/tmp/test_space/.dm/meta.role.json')
233
- os.remove('/tmp/test_space/.dm/meta.permission.json')
234
- os.rmdir('/tmp/test_space/dir1/attachments')
235
- os.rmdir('/tmp/test_space/.dm')
236
- os.rmdir('/tmp/test_space/dir1')
237
- os.rmdir('/tmp/test_space')
pytests/service_test.py DELETED
@@ -1,26 +0,0 @@
1
- from api.user.service import gen_alphanumeric
2
- import pytest
3
-
4
- @pytest.mark.run(order=9)
5
- def test_gen_alphanumeric_length():
6
- # Test default length
7
- result = gen_alphanumeric()
8
- assert len(result) == 16, "Default length should be 16"
9
-
10
- # Test custom length
11
- length = 32
12
- result = gen_alphanumeric(length)
13
- assert len(result) == length, f"Length should be {length}"
14
-
15
-
16
- @pytest.mark.run(order=9)
17
- def test_gen_alphanumeric_characters():
18
- result = gen_alphanumeric()
19
- assert all(c.isalnum() for c in result), "Result should only contain alphanumeric characters"
20
-
21
-
22
- @pytest.mark.run(order=9)
23
- def test_gen_alphanumeric_unique():
24
- num_samples = 100
25
- samples = {gen_alphanumeric() for _ in range(num_samples)}
26
- assert len(samples) == num_samples, "Generated strings should be unique"
pytests/test_info.py DELETED
@@ -1,55 +0,0 @@
1
- from httpx import AsyncClient
2
- from pytests.base_test import get_superman_cookie
3
- from fastapi import status
4
- import pytest
5
- from utils.internal_error_code import InternalErrorCode
6
- from utils.jwt import sign_jwt
7
- from utils.settings import settings
8
-
9
-
10
-
11
- @pytest.mark.run(order=6)
12
- @pytest.mark.anyio
13
- async def test_info_me(client: AsyncClient) -> None:
14
-
15
- client.cookies.set("auth_token", await get_superman_cookie(client))
16
- response = await client.get("/info/me")
17
- assert response.status_code == status.HTTP_200_OK
18
- json_response = response.json()
19
- assert json_response["status"] == "success"
20
-
21
- @pytest.mark.run(order=6)
22
- @pytest.mark.anyio
23
- async def test_info_manifest(client: AsyncClient) -> None:
24
-
25
- client.cookies.set("auth_token", await get_superman_cookie(client))
26
- response = await client.get("/info/manifest")
27
- assert response.status_code == status.HTTP_200_OK
28
- json_response = response.json()
29
- assert json_response["status"] == "success"
30
-
31
-
32
- @pytest.mark.run(order=6)
33
- @pytest.mark.anyio
34
- async def test_get_settings_should_pass(client: AsyncClient) -> None:
35
- client.cookies.set("auth_token", await get_superman_cookie(client))
36
- response = await client.get("/info/settings")
37
- assert response.status_code == status.HTTP_200_OK
38
-
39
-
40
- # @pytest.mark.run(order=6)
41
- # @pytest.mark.anyio
42
- # async def test_in_loop_tasks(client: AsyncClient) -> None:
43
- # client.cookies.set("auth_token", await get_superman_cookie(client))
44
- # response = await client.get("/info/in-loop-tasks")
45
- # assert response.status_code == status.HTTP_200_OK
46
- # json_response = response.json()
47
- # assert json_response["status"] == "success"
48
- # assert "tasks_count" in json_response["attributes"]
49
- # assert isinstance(json_response["attributes"]["tasks_count"], int)
50
- # assert "tasks" in json_response["attributes"]
51
- # assert isinstance(json_response["attributes"]["tasks"], list)
52
- # for task in json_response["attributes"]["tasks"]:
53
- # assert "name" in task
54
- # assert "coroutine" in task
55
- # assert "stack" in task
pytests/test_status.py DELETED
@@ -1,15 +0,0 @@
1
-
2
- # from pytests.base_test import client
3
- from fastapi import status
4
- from httpx import AsyncClient
5
- import pytest
6
-
7
- # @pytest.mark.asyncio(scope="session")
8
- @pytest.mark.anyio
9
- async def test_sanity(client: AsyncClient) -> None:
10
- # async with my_client as client:
11
- response = await client.get("/")
12
- assert response.status_code == status.HTTP_200_OK
13
- json_response = response.json()
14
- assert json_response["status"] == "success"
15
-
utils/__init__.py DELETED
File without changes
File without changes
File without changes
File without changes