mage-ai 0.9.67__py3-none-any.whl → 0.9.69__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.

Potentially problematic release.


This version of mage-ai might be problematic. Click here for more details.

Files changed (307) hide show
  1. mage_ai/api/monitors/BaseMonitor.py +1 -2
  2. mage_ai/api/policies/PipelinePolicy.py +2 -0
  3. mage_ai/api/resources/BlockLayoutItemResource.py +2 -2
  4. mage_ai/api/resources/BlockResource.py +4 -3
  5. mage_ai/api/resources/CacheItemResource.py +1 -1
  6. mage_ai/api/resources/GitBranchResource.py +3 -5
  7. mage_ai/api/resources/PageBlockLayoutResource.py +2 -2
  8. mage_ai/api/resources/PipelineResource.py +13 -0
  9. mage_ai/api/resources/PipelineRunResource.py +10 -1
  10. mage_ai/api/resources/SeedResource.py +2 -1
  11. mage_ai/api/resources/SessionResource.py +13 -1
  12. mage_ai/authentication/permissions/constants.py +2 -0
  13. mage_ai/authentication/permissions/seed.py +32 -21
  14. mage_ai/authentication/providers/active_directory.py +4 -3
  15. mage_ai/authentication/providers/okta.py +22 -83
  16. mage_ai/cache/tag.py +3 -0
  17. mage_ai/cluster_manager/manage.py +4 -1
  18. mage_ai/data_preparation/executors/k8s_block_executor.py +8 -1
  19. mage_ai/data_preparation/executors/k8s_pipeline_executor.py +12 -1
  20. mage_ai/data_preparation/executors/streaming_pipeline_executor.py +77 -7
  21. mage_ai/data_preparation/logging/gcs_logger_manager.py +7 -4
  22. mage_ai/data_preparation/models/block/__init__.py +28 -71
  23. mage_ai/data_preparation/models/block/block_factory.py +77 -0
  24. mage_ai/data_preparation/models/block/data_integration/mixins.py +16 -5
  25. mage_ai/data_preparation/models/block/dbt/block.py +5 -7
  26. mage_ai/data_preparation/models/block/dynamic/child.py +3 -0
  27. mage_ai/data_preparation/models/block/dynamic/variables.py +2 -2
  28. mage_ai/data_preparation/models/block/extension/utils.py +1 -0
  29. mage_ai/data_preparation/models/block/global_data_product/__init__.py +9 -3
  30. mage_ai/data_preparation/models/block/integration/__init__.py +13 -9
  31. mage_ai/data_preparation/models/block/platform/mixins.py +1 -1
  32. mage_ai/data_preparation/models/block/sql/__init__.py +1 -1
  33. mage_ai/data_preparation/models/pipeline.py +102 -19
  34. mage_ai/data_preparation/models/utils.py +6 -0
  35. mage_ai/data_preparation/models/variable.py +18 -4
  36. mage_ai/data_preparation/repo_manager.py +3 -2
  37. mage_ai/data_preparation/shared/utils.py +1 -1
  38. mage_ai/data_preparation/storage/gcs_storage.py +1 -1
  39. mage_ai/data_preparation/templates/constants.py +7 -0
  40. mage_ai/data_preparation/templates/data_exporters/mysql.py +2 -2
  41. mage_ai/data_preparation/templates/data_exporters/oracledb.py +27 -0
  42. mage_ai/data_preparation/templates/repo/metadata.yaml +1 -0
  43. mage_ai/io/bigquery.py +131 -58
  44. mage_ai/io/export_utils.py +3 -0
  45. mage_ai/io/mysql.py +38 -6
  46. mage_ai/io/oracledb.py +138 -3
  47. mage_ai/io/snowflake.py +152 -29
  48. mage_ai/io/sql.py +4 -0
  49. mage_ai/orchestration/db/__init__.py +2 -2
  50. mage_ai/orchestration/db/models/oauth.py +4 -4
  51. mage_ai/orchestration/db/models/schedules.py +10 -3
  52. mage_ai/orchestration/job_manager.py +6 -0
  53. mage_ai/orchestration/notification/sender.py +8 -0
  54. mage_ai/orchestration/pipeline_scheduler_original.py +26 -7
  55. mage_ai/orchestration/queue/celery_queue.py +8 -1
  56. mage_ai/orchestration/queue/process_queue.py +67 -4
  57. mage_ai/orchestration/queue/queue.py +8 -0
  58. mage_ai/server/constants.py +1 -1
  59. mage_ai/server/frontend_dist/404.html +2 -2
  60. mage_ai/server/{frontend_dist_base_path_template/_next/static/khKiaJtwrslgMmp4YSa1f → frontend_dist/_next/static/_krrrgup_C-dPOpX36S8I}/_buildManifest.js +1 -1
  61. mage_ai/server/frontend_dist/_next/static/chunks/1557-df144fbd8b2208c3.js +1 -0
  62. mage_ai/server/frontend_dist/_next/static/chunks/2717-d9200be634dd6766.js +1 -0
  63. mage_ai/server/frontend_dist/_next/static/chunks/3548-fa0792ddb88f4646.js +1 -0
  64. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/4241-ccd0126f5750cc35.js → frontend_dist/_next/static/chunks/4241-4499461184ba0d23.js} +1 -1
  65. mage_ai/server/frontend_dist/_next/static/chunks/5627-237a3de578538022.js +1 -0
  66. mage_ai/server/frontend_dist/_next/static/chunks/5699-6d708c6b2153ea08.js +1 -0
  67. mage_ai/server/frontend_dist/_next/static/chunks/7361-8a23dd8360593e7a.js +1 -0
  68. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/7966-5c1786fb7e7a48f5.js → frontend_dist/_next/static/chunks/7966-f07b2913f7326b50.js} +1 -1
  69. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-d9c89527266296f7.js +1 -0
  70. mage_ai/server/frontend_dist/_next/static/chunks/pages/index-4e12783b064c1cfe.js +1 -0
  71. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/[user]-8bbfa0c19b5e4cb3.js +1 -0
  72. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-852d403c7bda21b3.js +1 -0
  73. mage_ai/server/frontend_dist/_next/static/chunks/pages/overview-597b74828bf105db.js +1 -0
  74. mage_ai/server/frontend_dist/_next/static/chunks/pages/{pipeline-runs-a66b4c7641ae03eb.js → pipeline-runs-3edc6270c5b0e962.js} +1 -1
  75. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +1 -0
  76. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-e1dd1ed71d26c10d.js +1 -0
  77. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-1417ad1c821d720a.js +1 -0
  78. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-59aca25a5b1d3998.js +1 -0
  79. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-90abafc7ed61c582.js +1 -0
  80. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-1bdfda8edc9cf4a8.js +1 -0
  81. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-3591d035bb3bb2b8.js +1 -0
  82. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-503049734a8b082f.js +1 -0
  83. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/settings-c2e9ef989c8bfa73.js +1 -0
  84. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-5b26eeda8aed8a7b.js +1 -0
  85. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-79a4cf66a623e667.js → frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-8b793b3b696a2cd3.js} +1 -1
  86. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{users-86814e581acaf5db.js → users-a4db8710f703c729.js} +1 -1
  87. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-7d38b2f7c3e918a1.js → frontend_dist/_next/static/chunks/pages/sign-in-09414a8b66fb6f06.js} +1 -1
  88. mage_ai/server/frontend_dist/_next/static/chunks/pages/triggers-9cba3211434a8966.js +1 -0
  89. mage_ai/server/frontend_dist/block-layout.html +2 -2
  90. mage_ai/server/frontend_dist/compute.html +2 -2
  91. mage_ai/server/frontend_dist/files.html +2 -2
  92. mage_ai/server/frontend_dist/global-data-products/[...slug].html +2 -2
  93. mage_ai/server/frontend_dist/global-data-products.html +2 -2
  94. mage_ai/server/frontend_dist/global-hooks/[...slug].html +2 -2
  95. mage_ai/server/frontend_dist/global-hooks.html +2 -2
  96. mage_ai/server/frontend_dist/index.html +2 -2
  97. mage_ai/server/frontend_dist/manage/files.html +2 -2
  98. mage_ai/server/frontend_dist/manage/settings.html +2 -2
  99. mage_ai/server/frontend_dist/manage/users/[user].html +2 -2
  100. mage_ai/server/frontend_dist/manage/users/new.html +2 -2
  101. mage_ai/server/frontend_dist/manage/users.html +2 -2
  102. mage_ai/server/frontend_dist/manage.html +2 -2
  103. mage_ai/server/frontend_dist/oauth.html +3 -3
  104. mage_ai/server/frontend_dist/overview.html +2 -2
  105. mage_ai/server/frontend_dist/pipeline-runs.html +2 -2
  106. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +2 -2
  107. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +2 -2
  108. mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +2 -2
  109. mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +2 -2
  110. mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +2 -2
  111. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +2 -2
  112. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
  113. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +2 -2
  114. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +2 -2
  115. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +2 -2
  116. mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +2 -2
  117. mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +2 -2
  118. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +2 -2
  119. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +2 -2
  120. mage_ai/server/frontend_dist/pipelines/[pipeline].html +2 -2
  121. mage_ai/server/frontend_dist/pipelines.html +2 -2
  122. mage_ai/server/frontend_dist/platform/global-hooks/[...slug].html +2 -2
  123. mage_ai/server/frontend_dist/platform/global-hooks.html +2 -2
  124. mage_ai/server/frontend_dist/settings/account/profile.html +2 -2
  125. mage_ai/server/frontend_dist/settings/platform/preferences.html +2 -2
  126. mage_ai/server/frontend_dist/settings/platform/settings.html +2 -2
  127. mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +2 -2
  128. mage_ai/server/frontend_dist/settings/workspace/permissions.html +2 -2
  129. mage_ai/server/frontend_dist/settings/workspace/preferences.html +2 -2
  130. mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +2 -2
  131. mage_ai/server/frontend_dist/settings/workspace/roles.html +2 -2
  132. mage_ai/server/frontend_dist/settings/workspace/sync-data.html +2 -2
  133. mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +2 -2
  134. mage_ai/server/frontend_dist/settings/workspace/users.html +2 -2
  135. mage_ai/server/frontend_dist/settings.html +2 -2
  136. mage_ai/server/frontend_dist/sign-in.html +5 -5
  137. mage_ai/server/frontend_dist/templates/[...slug].html +2 -2
  138. mage_ai/server/frontend_dist/templates.html +2 -2
  139. mage_ai/server/frontend_dist/terminal.html +2 -2
  140. mage_ai/server/frontend_dist/test.html +2 -2
  141. mage_ai/server/frontend_dist/triggers.html +2 -2
  142. mage_ai/server/frontend_dist/version-control.html +2 -2
  143. mage_ai/server/frontend_dist_base_path_template/404.html +2 -2
  144. mage_ai/server/{frontend_dist/_next/static/vPsMu6Fi2zrHaf2fRXKRO → frontend_dist_base_path_template/_next/static/KLL5mirre9d7_ZeEpaw3s}/_buildManifest.js +1 -1
  145. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-df144fbd8b2208c3.js +1 -0
  146. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-d9200be634dd6766.js +1 -0
  147. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-fa0792ddb88f4646.js +1 -0
  148. mage_ai/server/{frontend_dist/_next/static/chunks/4241-ccd0126f5750cc35.js → frontend_dist_base_path_template/_next/static/chunks/4241-4499461184ba0d23.js} +1 -1
  149. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5627-237a3de578538022.js +1 -0
  150. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-6d708c6b2153ea08.js +1 -0
  151. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-8a23dd8360593e7a.js +1 -0
  152. mage_ai/server/{frontend_dist/_next/static/chunks/7966-5c1786fb7e7a48f5.js → frontend_dist_base_path_template/_next/static/chunks/7966-f07b2913f7326b50.js} +1 -1
  153. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-d9c89527266296f7.js +1 -0
  154. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/index-4e12783b064c1cfe.js +1 -0
  155. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/[user]-8bbfa0c19b5e4cb3.js +1 -0
  156. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-852d403c7bda21b3.js +1 -0
  157. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/overview-597b74828bf105db.js +1 -0
  158. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{pipeline-runs-a66b4c7641ae03eb.js → pipeline-runs-3edc6270c5b0e962.js} +1 -1
  159. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +1 -0
  160. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-e1dd1ed71d26c10d.js +1 -0
  161. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-1417ad1c821d720a.js +1 -0
  162. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-59aca25a5b1d3998.js +1 -0
  163. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-90abafc7ed61c582.js +1 -0
  164. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-1bdfda8edc9cf4a8.js +1 -0
  165. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-3591d035bb3bb2b8.js +1 -0
  166. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-503049734a8b082f.js +1 -0
  167. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-c2e9ef989c8bfa73.js +1 -0
  168. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-5b26eeda8aed8a7b.js +1 -0
  169. mage_ai/server/{frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-79a4cf66a623e667.js → frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-8b793b3b696a2cd3.js} +1 -1
  170. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{users-86814e581acaf5db.js → users-a4db8710f703c729.js} +1 -1
  171. mage_ai/server/{frontend_dist/_next/static/chunks/pages/sign-in-7d38b2f7c3e918a1.js → frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-09414a8b66fb6f06.js} +1 -1
  172. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/triggers-9cba3211434a8966.js +1 -0
  173. mage_ai/server/frontend_dist_base_path_template/block-layout.html +2 -2
  174. mage_ai/server/frontend_dist_base_path_template/compute.html +2 -2
  175. mage_ai/server/frontend_dist_base_path_template/files.html +2 -2
  176. mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +2 -2
  177. mage_ai/server/frontend_dist_base_path_template/global-data-products.html +2 -2
  178. mage_ai/server/frontend_dist_base_path_template/global-hooks/[...slug].html +2 -2
  179. mage_ai/server/frontend_dist_base_path_template/global-hooks.html +2 -2
  180. mage_ai/server/frontend_dist_base_path_template/index.html +2 -2
  181. mage_ai/server/frontend_dist_base_path_template/manage/files.html +2 -2
  182. mage_ai/server/frontend_dist_base_path_template/manage/settings.html +2 -2
  183. mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +2 -2
  184. mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +2 -2
  185. mage_ai/server/frontend_dist_base_path_template/manage/users.html +2 -2
  186. mage_ai/server/frontend_dist_base_path_template/manage.html +2 -2
  187. mage_ai/server/frontend_dist_base_path_template/oauth.html +3 -3
  188. mage_ai/server/frontend_dist_base_path_template/overview.html +2 -2
  189. mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +2 -2
  190. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +2 -2
  191. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +2 -2
  192. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/dashboard.html +2 -2
  193. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +2 -2
  194. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +2 -2
  195. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +2 -2
  196. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
  197. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +2 -2
  198. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +2 -2
  199. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +2 -2
  200. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +2 -2
  201. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +2 -2
  202. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +2 -2
  203. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +2 -2
  204. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +2 -2
  205. mage_ai/server/frontend_dist_base_path_template/pipelines.html +2 -2
  206. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks/[...slug].html +2 -2
  207. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks.html +2 -2
  208. mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +2 -2
  209. mage_ai/server/frontend_dist_base_path_template/settings/platform/preferences.html +2 -2
  210. mage_ai/server/frontend_dist_base_path_template/settings/platform/settings.html +2 -2
  211. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions/[...slug].html +2 -2
  212. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions.html +2 -2
  213. mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +2 -2
  214. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles/[...slug].html +2 -2
  215. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles.html +2 -2
  216. mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +2 -2
  217. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users/[...slug].html +2 -2
  218. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +2 -2
  219. mage_ai/server/frontend_dist_base_path_template/settings.html +2 -2
  220. mage_ai/server/frontend_dist_base_path_template/sign-in.html +5 -5
  221. mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +2 -2
  222. mage_ai/server/frontend_dist_base_path_template/templates.html +2 -2
  223. mage_ai/server/frontend_dist_base_path_template/terminal.html +2 -2
  224. mage_ai/server/frontend_dist_base_path_template/test.html +2 -2
  225. mage_ai/server/frontend_dist_base_path_template/triggers.html +2 -2
  226. mage_ai/server/frontend_dist_base_path_template/version-control.html +2 -2
  227. mage_ai/server/scheduler_manager.py +7 -0
  228. mage_ai/server/server.py +12 -5
  229. mage_ai/server/websocket_server.py +1 -0
  230. mage_ai/services/k8s/job_manager.py +8 -0
  231. mage_ai/services/slack/slack.py +10 -1
  232. mage_ai/services/spark/spark.py +9 -2
  233. mage_ai/settings/backends.py +8 -8
  234. mage_ai/settings/keys/auth.py +2 -0
  235. mage_ai/settings/models/configuration_option.py +10 -9
  236. mage_ai/settings/server.py +1 -1
  237. mage_ai/shared/files.py +19 -1
  238. mage_ai/shared/path_fixer.py +3 -0
  239. mage_ai/streaming/sources/base.py +5 -0
  240. mage_ai/streaming/sources/influxdb.py +2 -0
  241. mage_ai/streaming/sources/kafka.py +2 -1
  242. mage_ai/streaming/sources/mongodb.py +4 -0
  243. mage_ai/tests/api/endpoints/mixins.py +10 -9
  244. mage_ai/tests/api/endpoints/test_seeds.py +24 -0
  245. mage_ai/tests/api/operations/test_sessions.py +53 -2
  246. mage_ai/tests/authentication/providers/test_okta.py +43 -0
  247. mage_ai/tests/data_preparation/models/block/dbt/test_block.py +2 -2
  248. mage_ai/tests/data_preparation/models/block/dbt/test_block_sql.py +1 -1
  249. mage_ai/tests/data_preparation/models/block/dbt/test_block_yaml.py +1 -1
  250. mage_ai/tests/data_preparation/models/test_block.py +2 -1
  251. mage_ai/tests/orchestration/db/models/test_oauth.py +3 -3
  252. mage_ai/tests/orchestration/queue/test_process_queue.py +1 -0
  253. mage_ai/tests/orchestration/test_pipeline_scheduler.py +2 -0
  254. mage_ai/tests/server/test_server.py +8 -4
  255. mage_ai/tests/settings/models/test_configuration_option.py +2 -2
  256. {mage_ai-0.9.67.dist-info → mage_ai-0.9.69.dist-info}/METADATA +6 -6
  257. {mage_ai-0.9.67.dist-info → mage_ai-0.9.69.dist-info}/RECORD +263 -259
  258. mage_ai/server/frontend_dist/_next/static/chunks/1557-01f0843dc6ac4971.js +0 -1
  259. mage_ai/server/frontend_dist/_next/static/chunks/181-e61915415a976861.js +0 -1
  260. mage_ai/server/frontend_dist/_next/static/chunks/2717-b5f9575799b594d5.js +0 -1
  261. mage_ai/server/frontend_dist/_next/static/chunks/3548-13563a1ff815f922.js +0 -1
  262. mage_ai/server/frontend_dist/_next/static/chunks/5699-6efc749f2f8ddd20.js +0 -1
  263. mage_ai/server/frontend_dist/_next/static/chunks/7361-18d9d8be96e1ce97.js +0 -1
  264. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-1c1ffd928f5a00f7.js +0 -1
  265. mage_ai/server/frontend_dist/_next/static/chunks/pages/index-b7b8695a7f9efde2.js +0 -1
  266. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/[user]-d3a5fd3119fdb1e4.js +0 -1
  267. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-42789d698d28a92f.js +0 -1
  268. mage_ai/server/frontend_dist/_next/static/chunks/pages/overview-d05040edba41b2ac.js +0 -1
  269. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-aaf393c86fc1bda3.js +0 -1
  270. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-36377e679da2cd91.js +0 -1
  271. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-1b688d61f8efe07a.js +0 -1
  272. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-ed3331d22d5cff7d.js +0 -1
  273. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-d94e48bad89ba1e0.js +0 -1
  274. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-f508c2f261297724.js +0 -1
  275. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-f99e99aa8f45529c.js +0 -1
  276. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-6826000cdffc36b8.js +0 -1
  277. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/settings-74d76300942dcee8.js +0 -1
  278. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-dde29a463495cebb.js +0 -1
  279. mage_ai/server/frontend_dist/_next/static/chunks/pages/triggers-ab98a7b3a678669e.js +0 -1
  280. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-01f0843dc6ac4971.js +0 -1
  281. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/181-e61915415a976861.js +0 -1
  282. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-b5f9575799b594d5.js +0 -1
  283. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-13563a1ff815f922.js +0 -1
  284. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-6efc749f2f8ddd20.js +0 -1
  285. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-18d9d8be96e1ce97.js +0 -1
  286. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-1c1ffd928f5a00f7.js +0 -1
  287. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/index-b7b8695a7f9efde2.js +0 -1
  288. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/[user]-d3a5fd3119fdb1e4.js +0 -1
  289. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-42789d698d28a92f.js +0 -1
  290. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/overview-d05040edba41b2ac.js +0 -1
  291. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-aaf393c86fc1bda3.js +0 -1
  292. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-36377e679da2cd91.js +0 -1
  293. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-1b688d61f8efe07a.js +0 -1
  294. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-ed3331d22d5cff7d.js +0 -1
  295. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-d94e48bad89ba1e0.js +0 -1
  296. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-f508c2f261297724.js +0 -1
  297. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-f99e99aa8f45529c.js +0 -1
  298. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-6826000cdffc36b8.js +0 -1
  299. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-74d76300942dcee8.js +0 -1
  300. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-dde29a463495cebb.js +0 -1
  301. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/triggers-ab98a7b3a678669e.js +0 -1
  302. /mage_ai/server/frontend_dist/_next/static/{vPsMu6Fi2zrHaf2fRXKRO → _krrrgup_C-dPOpX36S8I}/_ssgManifest.js +0 -0
  303. /mage_ai/server/frontend_dist_base_path_template/_next/static/{khKiaJtwrslgMmp4YSa1f → KLL5mirre9d7_ZeEpaw3s}/_ssgManifest.js +0 -0
  304. {mage_ai-0.9.67.dist-info → mage_ai-0.9.69.dist-info}/LICENSE +0 -0
  305. {mage_ai-0.9.67.dist-info → mage_ai-0.9.69.dist-info}/WHEEL +0 -0
  306. {mage_ai-0.9.67.dist-info → mage_ai-0.9.69.dist-info}/entry_points.txt +0 -0
  307. {mage_ai-0.9.67.dist-info → mage_ai-0.9.69.dist-info}/top_level.txt +0 -0
@@ -24,8 +24,6 @@ class BaseMonitor:
24
24
  if self.error.type:
25
25
  data['type'] = self.error.type
26
26
 
27
- loop = asyncio.get_event_loop()
28
-
29
27
  kwargs = dict(
30
28
  resource=self.resource,
31
29
  **extract(
@@ -48,6 +46,7 @@ class BaseMonitor:
48
46
  ),
49
47
  )
50
48
 
49
+ loop = asyncio.get_event_loop()
51
50
  if loop is not None:
52
51
  loop.create_task(
53
52
  UsageStatisticLogger().error(
@@ -85,9 +85,11 @@ PipelinePolicy.allow_write([
85
85
  'conditionals',
86
86
  'clone_pipeline_uuid',
87
87
  'custom_template_uuid',
88
+ 'description',
88
89
  'extensions',
89
90
  'llm',
90
91
  'name',
92
+ 'tags',
91
93
  'type',
92
94
  ], scopes=[
93
95
  OauthScope.CLIENT_PRIVATE,
@@ -6,7 +6,7 @@ import urllib.parse
6
6
 
7
7
  from mage_ai.api.errors import ApiError
8
8
  from mage_ai.api.resources.GenericResource import GenericResource
9
- from mage_ai.data_preparation.models.block import Block
9
+ from mage_ai.data_preparation.models.block.block_factory import BlockFactory
10
10
  from mage_ai.data_preparation.models.constants import BlockType
11
11
  from mage_ai.data_preparation.models.widget.constants import ChartType
12
12
  from mage_ai.data_preparation.variable_manager import get_global_variables
@@ -73,7 +73,7 @@ class BlockLayoutItemResource(GenericResource):
73
73
  data_source_type = data_source_config.get('type')
74
74
  pipeline_uuid = data_source_config.get('pipeline_uuid')
75
75
 
76
- block = Block.get_block(
76
+ block = BlockFactory.get_block(
77
77
  block_config.get('name') or file_path or uuid,
78
78
  block_uuid,
79
79
  block_type,
@@ -12,6 +12,7 @@ from mage_ai.cache.block_action_object.constants import (
12
12
  OBJECT_TYPE_MAGE_TEMPLATE,
13
13
  )
14
14
  from mage_ai.data_preparation.models.block import Block
15
+ from mage_ai.data_preparation.models.block.block_factory import BlockFactory
15
16
  from mage_ai.data_preparation.models.block.utils import clean_name
16
17
  from mage_ai.data_preparation.models.constants import (
17
18
  FILE_EXTENSION_TO_BLOCK_LANGUAGE,
@@ -163,7 +164,7 @@ class BlockResource(GenericResource):
163
164
  if payload.get('type') == BlockType.DBT and language == BlockLanguage.SQL and content:
164
165
  from mage_ai.data_preparation.models.block.dbt import DBTBlock
165
166
 
166
- dbt_block = DBTBlock(
167
+ dbt_block = DBTBlock.create(
167
168
  name,
168
169
  clean_name(name),
169
170
  BlockType.DBT,
@@ -323,7 +324,7 @@ class BlockResource(GenericResource):
323
324
  if BlockType.DBT == block_type:
324
325
  from mage_ai.data_preparation.models.block.dbt import DBTBlock
325
326
 
326
- block = DBTBlock(
327
+ block = DBTBlock.create(
327
328
  block_uuid,
328
329
  block_uuid,
329
330
  block_type,
@@ -331,7 +332,7 @@ class BlockResource(GenericResource):
331
332
  language=language,
332
333
  )
333
334
  else:
334
- block = Block.get_block(block_uuid, block_uuid, block_type, language=language)
335
+ block = BlockFactory.get_block(block_uuid, block_uuid, block_type, language=language)
335
336
 
336
337
  if not block.exists():
337
338
  error.update(ApiError.RESOURCE_NOT_FOUND)
@@ -50,7 +50,7 @@ class CacheItemResource(AsyncBaseResource):
50
50
  )
51
51
 
52
52
  if CacheItemType.DBT == item_type:
53
- block = DBTBlock(
53
+ block = DBTBlock.create(
54
54
  pk,
55
55
  pk,
56
56
  BlockType.DBT,
@@ -152,14 +152,10 @@ class GitBranchResource(GenericResource):
152
152
  )
153
153
  http_access_token = git_manager.get_access_token()
154
154
 
155
+ config_overwrite = None
155
156
  token = None
156
157
  if access_token:
157
158
  token = access_token.token
158
- elif http_access_token:
159
- token = http_access_token
160
-
161
- config_overwrite = None
162
- if token:
163
159
  user_from_api = api.get_user(token, provider=provider)
164
160
  # Default to mage user email if no email is returned from API
165
161
  email = user_from_api.get(
@@ -169,6 +165,8 @@ class GitBranchResource(GenericResource):
169
165
  username=user_from_api.get('username'),
170
166
  email=email,
171
167
  )
168
+ elif http_access_token:
169
+ token = http_access_token
172
170
 
173
171
  # Recreate git manager with updated config
174
172
  git_manager = self.get_git_manager(
@@ -3,7 +3,7 @@ import urllib.parse
3
3
 
4
4
  from mage_ai.api.errors import ApiError
5
5
  from mage_ai.api.resources.GenericResource import GenericResource
6
- from mage_ai.data_preparation.models.block import Block
6
+ from mage_ai.data_preparation.models.block.block_factory import BlockFactory
7
7
  from mage_ai.presenters.block_layout.page import PageBlockLayout
8
8
  from mage_ai.shared.hash import extract, ignore_keys, merge_dict
9
9
  from mage_ai.shared.utils import clean_name
@@ -52,7 +52,7 @@ class PageBlockLayoutResource(GenericResource):
52
52
  if len(parts) >= 2:
53
53
  block_uuid_use = os.path.join(*parts[1:])
54
54
 
55
- block = Block.get_block(
55
+ block = BlockFactory.get_block(
56
56
  block_uuid_use,
57
57
  block_uuid_use,
58
58
  block_type,
@@ -362,6 +362,8 @@ class PipelineResource(BaseResource):
362
362
  clone_pipeline_uuid = payload.get('clone_pipeline_uuid')
363
363
  template_uuid = payload.get('custom_template_uuid')
364
364
  name = payload.get('name')
365
+ description = payload.get('description')
366
+ tags = payload.get('tags')
365
367
  pipeline_type = payload.get('type')
366
368
  llm_payload = payload.get('llm')
367
369
  pipeline = None
@@ -375,8 +377,10 @@ class PipelineResource(BaseResource):
375
377
  else:
376
378
  pipeline = Pipeline.create(
377
379
  name,
380
+ description=description,
378
381
  pipeline_type=pipeline_type,
379
382
  repo_path=get_repo_path(),
383
+ tags=tags,
380
384
  )
381
385
 
382
386
  if llm_payload:
@@ -451,6 +455,15 @@ class PipelineResource(BaseResource):
451
455
  cache = await PipelineCache.initialize_cache()
452
456
  cache.add_model(resource.model)
453
457
 
458
+ tags = resource.model.tags
459
+ if tags:
460
+ from mage_ai.cache.tag import TagCache
461
+
462
+ cache = await TagCache.initialize_cache()
463
+
464
+ for tag_uuid in tags:
465
+ cache.add_pipeline(tag_uuid, resource.model)
466
+
454
467
  self.on_create_callback = _on_create_callback
455
468
 
456
469
  return self(pipeline, user, **kwargs)
@@ -12,7 +12,7 @@ from mage_ai.data_preparation.models.triggers import (
12
12
  ScheduleStatus,
13
13
  ScheduleType,
14
14
  )
15
- from mage_ai.orchestration.db import safe_db_query
15
+ from mage_ai.orchestration.db import db_connection, safe_db_query
16
16
  from mage_ai.orchestration.db.models.schedules import (
17
17
  BlockRun,
18
18
  PipelineRun,
@@ -241,6 +241,15 @@ class PipelineRunResource(DatabaseResource):
241
241
  has_next = results_size > limit
242
242
  final_end_idx = results_size - 1 if has_next else results_size
243
243
 
244
+ # Expire pipeline runs that are in progress so that the latest status is returned
245
+ # the next time they are fetched.
246
+ for run in results:
247
+ if run.status in (
248
+ PipelineRun.PipelineRunStatus.RUNNING,
249
+ PipelineRun.PipelineRunStatus.INITIAL,
250
+ ):
251
+ db_connection.session.expire(run)
252
+
244
253
  result_set = self.build_result_set(
245
254
  results[0:final_end_idx],
246
255
  user,
@@ -6,6 +6,7 @@ class SeedResource(GenericResource):
6
6
  @classmethod
7
7
  async def create(self, payload, user, **kwargs):
8
8
  if payload.get('roles') and payload.get('permissions'):
9
- await bootstrap_permissions()
9
+ policy_names = payload.get('policy_names')
10
+ await bootstrap_permissions(policy_names=policy_names)
10
11
 
11
12
  return self({}, user, **kwargs)
@@ -11,9 +11,10 @@ from mage_ai.orchestration.db.models.oauth import Role, User
11
11
  from mage_ai.settings import (
12
12
  AUTHENTICATION_MODE,
13
13
  OAUTH_DEFAULT_ACCESS,
14
+ get_bool_value,
14
15
  get_settings_value,
15
16
  )
16
- from mage_ai.settings.keys import LDAP_DEFAULT_ACCESS
17
+ from mage_ai.settings.keys import LDAP_DEFAULT_ACCESS, UPDATE_ROLES_ON_LOGIN
17
18
  from mage_ai.usage_statistics.logger import UsageStatisticLogger
18
19
 
19
20
 
@@ -29,6 +30,9 @@ class SessionResource(BaseResource):
29
30
 
30
31
  oauth_client = kwargs.get('oauth_client')
31
32
 
33
+ update_roles_on_login = get_bool_value(
34
+ get_settings_value(UPDATE_ROLES_ON_LOGIN, default='False'))
35
+
32
36
  # Oauth sign in
33
37
  if token and provider:
34
38
  roles = []
@@ -57,6 +61,9 @@ class SessionResource(BaseResource):
57
61
  email=email,
58
62
  roles_new=roles,
59
63
  )
64
+ elif update_roles_on_login and roles:
65
+ user.update(roles_new=roles)
66
+
60
67
  oauth_token = generate_access_token(user, oauth_client)
61
68
  return self(oauth_token, user, **kwargs)
62
69
 
@@ -109,6 +116,11 @@ class SessionResource(BaseResource):
109
116
  roles_new=roles,
110
117
  username=email,
111
118
  )
119
+ elif update_roles_on_login:
120
+ role_names = conn.get_user_roles(user_attributes)
121
+ if role_names:
122
+ roles = Role.query.filter(Role.name.in_(role_names)).all()
123
+ user.update(roles_new=roles)
112
124
 
113
125
  oauth_token = generate_access_token(user, kwargs['oauth_client'])
114
126
  return self(oauth_token, user, **kwargs)
@@ -27,6 +27,7 @@ class EntityName(str, Enum):
27
27
  CustomTemplate = 'CustomTemplate'
28
28
  DataProvider = 'DataProvider'
29
29
  Database = 'Database'
30
+ Download = 'Download'
30
31
  EventMatcher = 'EventMatcher'
31
32
  EventRule = 'EventRule'
32
33
  ExecutionState = 'ExecutionState'
@@ -68,6 +69,7 @@ class EntityName(str, Enum):
68
69
  Scheduler = 'Scheduler'
69
70
  SearchResult = 'SearchResult'
70
71
  Secret = 'Secret'
72
+ Seed = 'Seed'
71
73
  Session = 'Session'
72
74
  SparkApplication = 'SparkApplication'
73
75
  SparkEnvironment = 'SparkEnvironment'
@@ -171,13 +171,17 @@ async def action_rules(policy_class):
171
171
  if scope not in operations_mapping:
172
172
  operations_mapping[scope] = {}
173
173
 
174
- condition = config2.get('condition')
175
- key = await get_key(policy_class, condition)
174
+ if not isinstance(config2, list):
175
+ config2 = [config2]
176
176
 
177
- if key not in operations_mapping[scope]:
178
- operations_mapping[scope][key] = []
177
+ for c in config2:
178
+ condition = c.get('condition')
179
+ key = await get_key(policy_class, condition)
180
+
181
+ if key not in operations_mapping[scope]:
182
+ operations_mapping[scope][key] = []
179
183
 
180
- operations_mapping[scope][key].append(operation)
184
+ operations_mapping[scope][key].append(operation)
181
185
 
182
186
  return operations_mapping
183
187
 
@@ -202,34 +206,40 @@ async def attribute_rules(policy_class, rules):
202
206
  if config2 is None:
203
207
  continue
204
208
 
205
- condition = config2.get('condition') if config2 else None
206
- key = await get_key(policy_class, condition)
209
+ if not isinstance(config2, list):
210
+ config2 = [config2]
211
+
212
+ for c in config2:
213
+ condition = c.get('condition')
214
+ key = await get_key(policy_class, condition)
207
215
 
208
- if key not in mapping[scope]:
209
- mapping[scope][key] = {}
216
+ if key not in mapping[scope]:
217
+ mapping[scope][key] = {}
210
218
 
211
- if operation not in mapping[scope][key]:
212
- mapping[scope][key][operation] = []
219
+ if operation not in mapping[scope][key]:
220
+ mapping[scope][key][operation] = []
213
221
 
214
- mapping[scope][key][operation].append(attribute)
222
+ mapping[scope][key][operation].append(attribute)
215
223
 
216
224
  return mapping
217
225
 
218
226
 
219
- async def bootstrap_permissions():
227
+ async def bootstrap_permissions(policy_names: str = None):
220
228
  action_rules_mapping = {}
221
229
  query_rules_mapping = {}
222
230
  read_rules_mapping = {}
223
231
  write_rules_mapping = {}
224
232
 
225
- policy_names = []
226
- for n in os.listdir(policies.__path__[0]):
227
- if n.endswith('Policy.py') and n not in [
228
- 'BasePolicy.py',
229
- 'SeedPolicy.py',
230
- 'UserPolicy.py',
231
- ]:
232
- policy_names.append(n.replace('Policy.py', ''))
233
+ if policy_names is None:
234
+ policy_names = []
235
+ for n in os.listdir(policies.__path__[0]):
236
+ if n.endswith('Policy.py') and n not in [
237
+ 'AsyncBasePolicy.py',
238
+ 'BasePolicy.py',
239
+ 'SeedPolicy.py',
240
+ 'UserPolicy.py',
241
+ ]:
242
+ policy_names.append(n.replace('Policy.py', ''))
233
243
 
234
244
  for policy_name in policy_names:
235
245
  policy_class = getattr(
@@ -238,6 +248,7 @@ async def bootstrap_permissions():
238
248
  )
239
249
 
240
250
  model_name = policy_class.model_name()
251
+ print(f'Processing {model_name}...')
241
252
  action_rules_mapping[model_name] = await action_rules(policy_class)
242
253
  query_rules_mapping[model_name] = await attribute_rules(
243
254
  policy_class,
@@ -34,10 +34,11 @@ class ADProvider(SsoProvider, OauthProvider):
34
34
  self.client_secret = get_settings_value(ACTIVE_DIRECTORY_CLIENT_SECRET)
35
35
  self.__validate()
36
36
 
37
- self.roles_mapping = get_settings_value(ACTIVE_DIRECTORY_ROLES_MAPPING)
38
- if self.roles_mapping:
37
+ self.roles_mapping = {}
38
+ roles_mapping = get_settings_value(ACTIVE_DIRECTORY_ROLES_MAPPING)
39
+ if roles_mapping:
39
40
  try:
40
- self.roles_mapping = json.loads(self.roles_mapping)
41
+ self.roles_mapping = json.loads(roles_mapping)
41
42
  except Exception:
42
43
  logger.exception('Failed to parse roles mapping.')
43
44
 
@@ -1,104 +1,43 @@
1
- import urllib.parse
2
- import uuid
3
- from typing import Awaitable, Dict
4
-
5
- import aiohttp
6
- from aiohttp import BasicAuth
1
+ from urllib.parse import unquote, urlparse
7
2
 
8
3
  from mage_ai.authentication.oauth.constants import ProviderName
9
- from mage_ai.authentication.providers.oauth import OauthProvider
10
- from mage_ai.authentication.providers.sso import SsoProvider
11
- from mage_ai.authentication.providers.utils import get_base_url
4
+ from mage_ai.authentication.providers.oidc import OidcProvider
12
5
  from mage_ai.settings import get_settings_value
13
6
  from mage_ai.settings.keys import OKTA_CLIENT_ID, OKTA_CLIENT_SECRET, OKTA_DOMAIN_URL
14
7
 
15
8
 
16
- class OktaProvider(SsoProvider, OauthProvider):
9
+ class OktaProvider(
10
+ OidcProvider
11
+ ): # Okta configuration uses OIDC so we can just subclass the OidcProvider
17
12
  provider = ProviderName.OKTA
18
13
 
19
14
  def __init__(self):
20
15
  self.hostname = get_settings_value(OKTA_DOMAIN_URL)
21
16
  self.client_id = get_settings_value(OKTA_CLIENT_ID)
22
17
  self.client_secret = get_settings_value(OKTA_CLIENT_SECRET)
18
+ self.parsed_url = urlparse(unquote(self.hostname))
19
+ if not self.parsed_url.scheme:
20
+ self.parsed_url = urlparse(unquote(f'https://{self.hostname}'))
23
21
  self.__validate()
24
22
 
25
- if not self.hostname.startswith('https'):
26
- self.hostname = f'https://{self.hostname}'
23
+ self.discovery_url = (
24
+ f'https://{self.parsed_url.netloc}/.well-known/openid-configuration'
25
+ )
26
+ self.discover()
27
27
 
28
28
  def __validate(self):
29
- if not self.hostname:
30
- raise Exception(
29
+ if not self.parsed_url.netloc:
30
+ raise ValueError(
31
31
  'Okta hostname is empty. '
32
- 'Make sure the OKTA_DOMAIN_URL environment variable is set.')
32
+ 'Make sure the OKTA_DOMAIN_URL environment variable is set.'
33
+ )
33
34
  if not self.client_id:
34
- raise Exception(
35
+ raise ValueError(
35
36
  'Okta client id is empty. '
36
- 'Make sure the OKTA_CLIENT_ID environment variable is set.')
37
+ 'Make sure the OKTA_CLIENT_ID environment variable is set.'
38
+ )
37
39
  if not self.client_secret:
38
- raise Exception(
40
+ raise ValueError(
39
41
  'Okta client secret is empty. '
40
- 'Make sure the OKTA_CLIENT_SECRET environment variable is set.')
41
-
42
- def get_auth_url_response(self, redirect_uri: str = None, **kwargs) -> Dict:
43
- base_url = get_base_url(redirect_uri)
44
- redirect_uri_query = dict(
45
- provider=self.provider,
46
- redirect_uri=redirect_uri,
47
- )
48
- query = dict(
49
- client_id=self.client_id,
50
- redirect_uri=urllib.parse.quote_plus(
51
- f'{base_url}/oauth',
52
- ),
53
- response_mode='query',
54
- response_type='code',
55
- scope='openid email profile',
56
- state=uuid.uuid4().hex,
57
- )
58
- query_strings = []
59
- for k, v in query.items():
60
- query_strings.append(f'{k}={v}')
61
-
62
- return dict(
63
- url=f"{self.hostname}/oauth2/default/v1/authorize?{'&'.join(query_strings)}",
64
- redirect_query_params=redirect_uri_query,
65
- )
66
-
67
- async def get_access_token_response(self, code: str, **kwargs) -> Awaitable[Dict]:
68
- base_url = get_base_url(kwargs.get('redirect_uri'))
69
- data = dict()
70
- async with aiohttp.ClientSession() as session:
71
- async with session.post(
72
- f'{self.hostname}/oauth2/default/v1/token',
73
- headers={
74
- 'Content-Type': 'application/x-www-form-urlencoded',
75
- },
76
- data=dict(
77
- grant_type='authorization_code',
78
- code=code,
79
- redirect_uri=f'{base_url}/oauth',
80
- ),
81
- auth=BasicAuth(self.client_id, self.client_secret),
82
- timeout=20,
83
- ) as response:
84
- data = await response.json()
85
-
86
- return data
87
-
88
- async def get_user_info(self, access_token: str = None, **kwargs) -> Awaitable[Dict]:
89
- if access_token is None:
90
- raise Exception('Access token is required to fetch user info.')
91
- async with aiohttp.ClientSession() as session:
92
- async with session.get(
93
- f'{self.hostname}/oauth2/default/v1/userinfo',
94
- headers={
95
- 'Authorization': f'Bearer {access_token}'
96
- },
97
- timeout=10,
98
- ) as response:
99
- userinfo_resp = await response.json()
100
-
101
- return dict(
102
- email=userinfo_resp.get('email'),
103
- username=userinfo_resp.get('sub'),
104
- )
42
+ 'Make sure the OKTA_CLIENT_SECRET environment variable is set.'
43
+ )
mage_ai/cache/tag.py CHANGED
@@ -144,6 +144,9 @@ class TagCache(BaseCache):
144
144
 
145
145
  mapping[key][KEY_FOR_PIPELINES] = pipelines_dict
146
146
 
147
+ if not pipelines_dict and mapping[key].keys() == set([KEY_FOR_PIPELINES]):
148
+ mapping.pop(key, None)
149
+
147
150
  self.set(self.cache_key, mapping)
148
151
 
149
152
  async def initialize_cache_for_all_objects(self) -> None:
@@ -70,11 +70,14 @@ def get_workspaces(cluster_type: ClusterType, **kwargs) -> List[Workspace]:
70
70
  repo_path = get_repo_path()
71
71
  projects_folder = os.path.join(repo_path, 'projects')
72
72
  if is_main_project:
73
+ # sort by file last modified time
73
74
  projects = [
74
- f.name.split('.')[0]
75
+ f
75
76
  for f in os.scandir(projects_folder)
76
77
  if not f.is_dir() and f.name.endswith('.yaml')
77
78
  ]
79
+ projects.sort(key=lambda f: os.path.getmtime(f.path), reverse=True)
80
+ projects = [os.path.splitext(f.name)[0] for f in projects]
78
81
  else:
79
82
  instances = get_instances(cluster_type, **kwargs)
80
83
  projects = [instance.get('name') for instance in instances]
@@ -1,6 +1,9 @@
1
1
  from typing import Dict
2
2
 
3
+ from jinja2 import Template
4
+
3
5
  from mage_ai.data_preparation.executors.block_executor import BlockExecutor
6
+ from mage_ai.data_preparation.shared.utils import get_template_vars
4
7
  from mage_ai.services.k8s.config import K8sExecutorConfig
5
8
  from mage_ai.services.k8s.constants import DEFAULT_NAMESPACE
6
9
  from mage_ai.services.k8s.job_manager import JobManager as K8sJobManager
@@ -29,9 +32,13 @@ class K8sBlockExecutor(BlockExecutor):
29
32
  job_name_prefix = self.executor_config.job_name_prefix
30
33
 
31
34
  if self.executor_config.namespace:
32
- namespace = self.executor_config.namespace
35
+ namespace = Template(self.executor_config.namespace).render(
36
+ variables=lambda x: global_vars.get(x) if global_vars else None,
37
+ **get_template_vars()
38
+ )
33
39
  else:
34
40
  namespace = DEFAULT_NAMESPACE
41
+
35
42
  job_manager = K8sJobManager(
36
43
  job_name=f'mage-{job_name_prefix}-block-{block_run_id}',
37
44
  logger=self.logger,
@@ -1,8 +1,11 @@
1
1
  import traceback
2
2
  from typing import Dict
3
3
 
4
+ from jinja2 import Template
5
+
4
6
  from mage_ai.data_preparation.executors.pipeline_executor import PipelineExecutor
5
7
  from mage_ai.data_preparation.models.pipeline import Pipeline
8
+ from mage_ai.data_preparation.shared.utils import get_template_vars
6
9
  from mage_ai.services.k8s.config import K8sExecutorConfig
7
10
  from mage_ai.services.k8s.constants import DEFAULT_NAMESPACE
8
11
  from mage_ai.services.k8s.job_manager import JobManager as K8sJobManager
@@ -39,6 +42,7 @@ class K8sPipelineExecutor(PipelineExecutor):
39
42
  ) -> None:
40
43
  job_manager = self.get_job_manager(
41
44
  pipeline_run_id=pipeline_run_id,
45
+ global_vars=global_vars,
42
46
  **kwargs,
43
47
  )
44
48
  cmd = self._run_commands(
@@ -54,15 +58,22 @@ class K8sPipelineExecutor(PipelineExecutor):
54
58
  def get_job_manager(
55
59
  self,
56
60
  pipeline_run_id: int = None,
61
+ global_vars: Dict = None,
57
62
  **kwargs,
58
63
  ) -> K8sJobManager:
64
+ if global_vars is None:
65
+ global_vars = dict()
59
66
  if not self.executor_config.job_name_prefix:
60
67
  job_name_prefix = 'data-prep'
61
68
  else:
62
69
  job_name_prefix = self.executor_config.job_name_prefix
63
70
 
64
71
  if self.executor_config.namespace:
65
- namespace = self.executor_config.namespace
72
+
73
+ namespace = Template(self.executor_config.namespace).render(
74
+ variables=lambda x: global_vars.get(x) if global_vars else None,
75
+ **get_template_vars()
76
+ )
66
77
  else:
67
78
  namespace = DEFAULT_NAMESPACE
68
79