mage-ai 0.9.68__py3-none-any.whl → 0.9.70__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 (316) hide show
  1. mage_ai/api/policies/BackfillPolicy.py +1 -0
  2. mage_ai/api/policies/PipelinePolicy.py +1 -0
  3. mage_ai/api/policies/WorkspacePolicy.py +1 -0
  4. mage_ai/api/presenters/BackfillPresenter.py +1 -0
  5. mage_ai/api/resources/GitBranchResource.py +56 -23
  6. mage_ai/api/resources/GitCustomBranchResource.py +29 -1
  7. mage_ai/api/resources/OauthResource.py +1 -1
  8. mage_ai/api/resources/PipelineResource.py +11 -5
  9. mage_ai/api/resources/PipelineRunResource.py +41 -4
  10. mage_ai/api/resources/PipelineScheduleResource.py +4 -0
  11. mage_ai/api/resources/PullRequestResource.py +6 -4
  12. mage_ai/api/resources/SeedResource.py +2 -1
  13. mage_ai/api/resources/SessionResource.py +13 -1
  14. mage_ai/api/resources/WorkspaceResource.py +5 -4
  15. mage_ai/authentication/permissions/constants.py +2 -0
  16. mage_ai/authentication/permissions/seed.py +32 -21
  17. mage_ai/authentication/providers/active_directory.py +4 -3
  18. mage_ai/authentication/providers/okta.py +22 -83
  19. mage_ai/cache/block_action_object/__init__.py +1 -1
  20. mage_ai/cluster_manager/kubernetes/workload_manager.py +52 -1
  21. mage_ai/cluster_manager/workspace/base.py +6 -0
  22. mage_ai/cluster_manager/workspace/kubernetes.py +22 -1
  23. mage_ai/command_center/applications/utils.py +2 -2
  24. mage_ai/command_center/presenters/text.py +1 -1
  25. mage_ai/data_preparation/executors/k8s_block_executor.py +30 -7
  26. mage_ai/data_preparation/executors/k8s_pipeline_executor.py +30 -7
  27. mage_ai/data_preparation/executors/streaming_pipeline_executor.py +78 -8
  28. mage_ai/data_preparation/git/__init__.py +50 -22
  29. mage_ai/data_preparation/git/api.py +62 -7
  30. mage_ai/data_preparation/git/utils.py +45 -21
  31. mage_ai/data_preparation/models/block/__init__.py +31 -8
  32. mage_ai/data_preparation/models/block/data_integration/mixins.py +16 -5
  33. mage_ai/data_preparation/models/block/dynamic/child.py +3 -0
  34. mage_ai/data_preparation/models/block/dynamic/utils.py +9 -4
  35. mage_ai/data_preparation/models/block/dynamic/variables.py +2 -2
  36. mage_ai/data_preparation/models/block/extension/utils.py +1 -0
  37. mage_ai/data_preparation/models/block/global_data_product/__init__.py +25 -2
  38. mage_ai/data_preparation/models/block/integration/__init__.py +1 -1
  39. mage_ai/data_preparation/models/block/remote/__init__.py +0 -0
  40. mage_ai/data_preparation/models/block/remote/models.py +58 -0
  41. mage_ai/data_preparation/models/block/sql/__init__.py +1 -1
  42. mage_ai/data_preparation/models/block/utils.py +38 -0
  43. mage_ai/data_preparation/models/constants.py +2 -0
  44. mage_ai/data_preparation/models/global_data_product/__init__.py +12 -0
  45. mage_ai/data_preparation/models/pipeline.py +31 -11
  46. mage_ai/data_preparation/models/triggers/__init__.py +4 -2
  47. mage_ai/data_preparation/models/utils.py +6 -0
  48. mage_ai/data_preparation/models/variable.py +18 -4
  49. mage_ai/data_preparation/repo_manager.py +3 -2
  50. mage_ai/data_preparation/shared/utils.py +1 -1
  51. mage_ai/data_preparation/storage/local_storage.py +12 -6
  52. mage_ai/data_preparation/templates/data_exporters/mysql.py +2 -2
  53. mage_ai/data_preparation/templates/data_exporters/oracledb.py +27 -0
  54. mage_ai/data_preparation/templates/repo/metadata.yaml +1 -0
  55. mage_ai/io/bigquery.py +131 -58
  56. mage_ai/io/mysql.py +38 -6
  57. mage_ai/io/snowflake.py +152 -29
  58. mage_ai/orchestration/db/migrations/versions/42a14d6143f1_update_token_column_type.py +54 -0
  59. mage_ai/orchestration/db/models/oauth.py +14 -13
  60. mage_ai/orchestration/db/models/schedules.py +30 -2
  61. mage_ai/orchestration/job_manager.py +6 -0
  62. mage_ai/orchestration/notification/sender.py +37 -15
  63. mage_ai/orchestration/pipeline_scheduler_original.py +48 -31
  64. mage_ai/orchestration/queue/celery_queue.py +8 -1
  65. mage_ai/orchestration/queue/process_queue.py +67 -4
  66. mage_ai/orchestration/queue/queue.py +8 -0
  67. mage_ai/orchestration/triggers/api.py +29 -1
  68. mage_ai/orchestration/triggers/global_data_product.py +9 -4
  69. mage_ai/orchestration/triggers/utils.py +10 -1
  70. mage_ai/orchestration/utils/resources.py +3 -0
  71. mage_ai/server/api/downloads.py +4 -1
  72. mage_ai/server/api/runs.py +151 -0
  73. mage_ai/server/constants.py +1 -1
  74. mage_ai/server/frontend_dist/404.html +6 -6
  75. mage_ai/server/frontend_dist/_next/static/{i8pymuJDTVHdWjUP1QSh1 → RhDiJSkcjCsh4xxX4BFBk}/_buildManifest.js +1 -1
  76. mage_ai/server/frontend_dist/_next/static/chunks/1557-b3502f3f1aa92ac7.js +1 -0
  77. mage_ai/server/frontend_dist/_next/static/chunks/2717-d9200be634dd6766.js +1 -0
  78. mage_ai/server/frontend_dist/_next/static/chunks/3548-9d26185b3fb663b1.js +1 -0
  79. mage_ai/server/frontend_dist/_next/static/chunks/5699-6d708c6b2153ea08.js +1 -0
  80. mage_ai/server/frontend_dist/_next/static/chunks/7361-8a23dd8360593e7a.js +1 -0
  81. mage_ai/server/frontend_dist/_next/static/chunks/7966-b9b85ba10667e654.js +1 -0
  82. mage_ai/server/frontend_dist/_next/static/chunks/9624-8b8e100079ab69e1.js +1 -0
  83. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-2a69553d8c6eeb53.js +1 -0
  84. mage_ai/server/frontend_dist/_next/static/chunks/pages/index-4e12783b064c1cfe.js +1 -0
  85. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-4bfc84ff07d7656f.js +1 -0
  86. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-a66b4c7641ae03eb.js → frontend_dist/_next/static/chunks/pages/pipeline-runs-6d183f91a2ff6668.js} +1 -1
  87. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-7181b086c93784d2.js +1 -0
  88. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-38e1fbcfbfc1014e.js +1 -0
  89. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-b645a6d13ab9fe3a.js +1 -0
  90. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-59aca25a5b1d3998.js +1 -0
  91. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-eb11c5390c982b49.js +1 -0
  92. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-cb88fd075a357fcf.js → triggers-4612d15a65c35912.js} +1 -1
  93. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-3591d035bb3bb2b8.js +1 -0
  94. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-32985f3f7c7dd3ab.js +1 -0
  95. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/settings-c2e9ef989c8bfa73.js +1 -0
  96. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-349af617d05f001b.js +1 -0
  97. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-60d01d3887e31136.js +1 -0
  98. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{users-86814e581acaf5db.js → users-a4db8710f703c729.js} +1 -1
  99. mage_ai/server/frontend_dist/_next/static/chunks/pages/sign-in-09414a8b66fb6f06.js +1 -0
  100. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/triggers-9cba3211434a8966.js → frontend_dist/_next/static/chunks/pages/triggers-a599c6ac89be8c8d.js} +1 -1
  101. mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-3433c8b22e8342aa.js +1 -0
  102. mage_ai/server/frontend_dist/block-layout.html +2 -2
  103. mage_ai/server/frontend_dist/compute.html +2 -2
  104. mage_ai/server/frontend_dist/files.html +2 -2
  105. mage_ai/server/frontend_dist/global-data-products/[...slug].html +2 -2
  106. mage_ai/server/frontend_dist/global-data-products.html +2 -2
  107. mage_ai/server/frontend_dist/global-hooks/[...slug].html +2 -2
  108. mage_ai/server/frontend_dist/global-hooks.html +2 -2
  109. mage_ai/server/frontend_dist/index.html +2 -2
  110. mage_ai/server/frontend_dist/manage/files.html +2 -2
  111. mage_ai/server/frontend_dist/manage/settings.html +2 -2
  112. mage_ai/server/frontend_dist/manage/users/[user].html +2 -2
  113. mage_ai/server/frontend_dist/manage/users/new.html +2 -2
  114. mage_ai/server/frontend_dist/manage/users.html +2 -2
  115. mage_ai/server/frontend_dist/manage.html +2 -2
  116. mage_ai/server/frontend_dist/oauth.html +2 -2
  117. mage_ai/server/frontend_dist/overview.html +2 -2
  118. mage_ai/server/frontend_dist/pipeline-runs.html +2 -2
  119. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +2 -2
  120. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +2 -2
  121. mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +2 -2
  122. mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +2 -2
  123. mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +2 -2
  124. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +2 -2
  125. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
  126. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +2 -2
  127. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +2 -2
  128. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +2 -2
  129. mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +2 -2
  130. mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +2 -2
  131. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +2 -2
  132. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +2 -2
  133. mage_ai/server/frontend_dist/pipelines/[pipeline].html +2 -2
  134. mage_ai/server/frontend_dist/pipelines.html +2 -2
  135. mage_ai/server/frontend_dist/platform/global-hooks/[...slug].html +2 -2
  136. mage_ai/server/frontend_dist/platform/global-hooks.html +2 -2
  137. mage_ai/server/frontend_dist/settings/account/profile.html +2 -2
  138. mage_ai/server/frontend_dist/settings/platform/preferences.html +2 -2
  139. mage_ai/server/frontend_dist/settings/platform/settings.html +2 -2
  140. mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +2 -2
  141. mage_ai/server/frontend_dist/settings/workspace/permissions.html +2 -2
  142. mage_ai/server/frontend_dist/settings/workspace/preferences.html +2 -2
  143. mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +2 -2
  144. mage_ai/server/frontend_dist/settings/workspace/roles.html +2 -2
  145. mage_ai/server/frontend_dist/settings/workspace/sync-data.html +2 -2
  146. mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +2 -2
  147. mage_ai/server/frontend_dist/settings/workspace/users.html +2 -2
  148. mage_ai/server/frontend_dist/settings.html +2 -2
  149. mage_ai/server/frontend_dist/sign-in.html +6 -6
  150. mage_ai/server/frontend_dist/templates/[...slug].html +2 -2
  151. mage_ai/server/frontend_dist/templates.html +2 -2
  152. mage_ai/server/frontend_dist/terminal.html +2 -2
  153. mage_ai/server/frontend_dist/test.html +2 -2
  154. mage_ai/server/frontend_dist/triggers.html +2 -2
  155. mage_ai/server/frontend_dist/version-control.html +2 -2
  156. mage_ai/server/frontend_dist_base_path_template/404.html +6 -6
  157. mage_ai/server/frontend_dist_base_path_template/_next/static/{CKCvjsYCf2imD2X8zAOBf → TdpLLFome13qvM0gXvpHs}/_buildManifest.js +1 -1
  158. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-b3502f3f1aa92ac7.js +1 -0
  159. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-d9200be634dd6766.js +1 -0
  160. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-9d26185b3fb663b1.js +1 -0
  161. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-6d708c6b2153ea08.js +1 -0
  162. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-8a23dd8360593e7a.js +1 -0
  163. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7966-b9b85ba10667e654.js +1 -0
  164. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-8b8e100079ab69e1.js +1 -0
  165. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-2a69553d8c6eeb53.js +1 -0
  166. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/index-4e12783b064c1cfe.js +1 -0
  167. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-4bfc84ff07d7656f.js +1 -0
  168. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipeline-runs-a66b4c7641ae03eb.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-6d183f91a2ff6668.js} +1 -1
  169. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-7181b086c93784d2.js +1 -0
  170. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-38e1fbcfbfc1014e.js +1 -0
  171. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-b645a6d13ab9fe3a.js +1 -0
  172. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-59aca25a5b1d3998.js +1 -0
  173. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-eb11c5390c982b49.js +1 -0
  174. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-cb88fd075a357fcf.js → triggers-4612d15a65c35912.js} +1 -1
  175. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-3591d035bb3bb2b8.js +1 -0
  176. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-32985f3f7c7dd3ab.js +1 -0
  177. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-c2e9ef989c8bfa73.js +1 -0
  178. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-349af617d05f001b.js +1 -0
  179. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-60d01d3887e31136.js +1 -0
  180. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{users-86814e581acaf5db.js → users-a4db8710f703c729.js} +1 -1
  181. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-09414a8b66fb6f06.js +1 -0
  182. mage_ai/server/{frontend_dist/_next/static/chunks/pages/triggers-9cba3211434a8966.js → frontend_dist_base_path_template/_next/static/chunks/pages/triggers-a599c6ac89be8c8d.js} +1 -1
  183. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-3433c8b22e8342aa.js +1 -0
  184. mage_ai/server/frontend_dist_base_path_template/block-layout.html +2 -2
  185. mage_ai/server/frontend_dist_base_path_template/compute.html +2 -2
  186. mage_ai/server/frontend_dist_base_path_template/files.html +2 -2
  187. mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +2 -2
  188. mage_ai/server/frontend_dist_base_path_template/global-data-products.html +2 -2
  189. mage_ai/server/frontend_dist_base_path_template/global-hooks/[...slug].html +2 -2
  190. mage_ai/server/frontend_dist_base_path_template/global-hooks.html +2 -2
  191. mage_ai/server/frontend_dist_base_path_template/index.html +2 -2
  192. mage_ai/server/frontend_dist_base_path_template/manage/files.html +2 -2
  193. mage_ai/server/frontend_dist_base_path_template/manage/settings.html +2 -2
  194. mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +2 -2
  195. mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +2 -2
  196. mage_ai/server/frontend_dist_base_path_template/manage/users.html +2 -2
  197. mage_ai/server/frontend_dist_base_path_template/manage.html +2 -2
  198. mage_ai/server/frontend_dist_base_path_template/oauth.html +2 -2
  199. mage_ai/server/frontend_dist_base_path_template/overview.html +2 -2
  200. mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +2 -2
  201. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +2 -2
  202. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +2 -2
  203. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/dashboard.html +2 -2
  204. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +2 -2
  205. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +2 -2
  206. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +2 -2
  207. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
  208. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +2 -2
  209. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +2 -2
  210. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +2 -2
  211. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +2 -2
  212. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +2 -2
  213. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +2 -2
  214. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +2 -2
  215. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +2 -2
  216. mage_ai/server/frontend_dist_base_path_template/pipelines.html +2 -2
  217. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks/[...slug].html +2 -2
  218. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks.html +2 -2
  219. mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +2 -2
  220. mage_ai/server/frontend_dist_base_path_template/settings/platform/preferences.html +2 -2
  221. mage_ai/server/frontend_dist_base_path_template/settings/platform/settings.html +2 -2
  222. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions/[...slug].html +2 -2
  223. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions.html +2 -2
  224. mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +2 -2
  225. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles/[...slug].html +2 -2
  226. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles.html +2 -2
  227. mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +2 -2
  228. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users/[...slug].html +2 -2
  229. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +2 -2
  230. mage_ai/server/frontend_dist_base_path_template/settings.html +2 -2
  231. mage_ai/server/frontend_dist_base_path_template/sign-in.html +6 -6
  232. mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +2 -2
  233. mage_ai/server/frontend_dist_base_path_template/templates.html +2 -2
  234. mage_ai/server/frontend_dist_base_path_template/terminal.html +2 -2
  235. mage_ai/server/frontend_dist_base_path_template/test.html +2 -2
  236. mage_ai/server/frontend_dist_base_path_template/triggers.html +2 -2
  237. mage_ai/server/frontend_dist_base_path_template/version-control.html +2 -2
  238. mage_ai/server/scheduler_manager.py +9 -0
  239. mage_ai/server/server.py +47 -17
  240. mage_ai/server/utils/output_display.py +2 -2
  241. mage_ai/server/websocket_server.py +1 -0
  242. mage_ai/services/aws/ecs/ecs.py +1 -0
  243. mage_ai/services/k8s/config.py +4 -4
  244. mage_ai/services/k8s/utils.py +97 -0
  245. mage_ai/settings/keys/auth.py +2 -0
  246. mage_ai/settings/server.py +1 -1
  247. mage_ai/shared/parsers.py +6 -1
  248. mage_ai/streaming/sources/influxdb.py +2 -0
  249. mage_ai/streaming/sources/kafka.py +1 -1
  250. mage_ai/tests/api/endpoints/mixins.py +10 -9
  251. mage_ai/tests/api/endpoints/test_seeds.py +24 -0
  252. mage_ai/tests/api/operations/base/mixins.py +1 -1
  253. mage_ai/tests/api/operations/test_sessions.py +53 -2
  254. mage_ai/tests/api/resources/test_pipeline_resource.py +2 -2
  255. mage_ai/tests/authentication/oauth/test_utils.py +1 -1
  256. mage_ai/tests/authentication/providers/test_okta.py +43 -0
  257. mage_ai/tests/data_preparation/models/block/test_global_data_product.py +2 -0
  258. mage_ai/tests/orchestration/db/models/test_oauth.py +3 -3
  259. mage_ai/tests/orchestration/queue/test_process_queue.py +1 -0
  260. mage_ai/tests/orchestration/triggers/test_global_data_product.py +138 -136
  261. mage_ai/tests/server/test_server.py +27 -4
  262. mage_ai/tests/services/k8s/test_job_manager.py +9 -6
  263. mage_ai/version_control/branch/utils.py +2 -1
  264. mage_ai/version_control/models.py +3 -2
  265. {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/METADATA +5 -5
  266. {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/RECORD +272 -264
  267. mage_ai/server/frontend_dist/_next/static/chunks/1557-01f0843dc6ac4971.js +0 -1
  268. mage_ai/server/frontend_dist/_next/static/chunks/2717-b5f9575799b594d5.js +0 -1
  269. mage_ai/server/frontend_dist/_next/static/chunks/3548-961ff79ca70038c7.js +0 -1
  270. mage_ai/server/frontend_dist/_next/static/chunks/5699-6efc749f2f8ddd20.js +0 -1
  271. mage_ai/server/frontend_dist/_next/static/chunks/7361-18d9d8be96e1ce97.js +0 -1
  272. mage_ai/server/frontend_dist/_next/static/chunks/7966-f07b2913f7326b50.js +0 -1
  273. mage_ai/server/frontend_dist/_next/static/chunks/9624-59b2f803f9c88cd6.js +0 -1
  274. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-08790743315de36a.js +0 -1
  275. mage_ai/server/frontend_dist/_next/static/chunks/pages/index-13760bb72d823b69.js +0 -1
  276. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-852d403c7bda21b3.js +0 -1
  277. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +0 -1
  278. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +0 -1
  279. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-bd0aff5a5ed8888c.js +0 -1
  280. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-d1ee961387c58b7f.js +0 -1
  281. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-f028ef3880ed856c.js +0 -1
  282. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-ceb06e1616ee9610.js +0 -1
  283. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-8ff16ef9566e911a.js +0 -1
  284. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/settings-74d76300942dcee8.js +0 -1
  285. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-d7a8bc51bb7a1082.js +0 -1
  286. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-79a4cf66a623e667.js +0 -1
  287. mage_ai/server/frontend_dist/_next/static/chunks/pages/sign-in-f59d34429fe022ee.js +0 -1
  288. mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +0 -1
  289. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-01f0843dc6ac4971.js +0 -1
  290. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-b5f9575799b594d5.js +0 -1
  291. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-961ff79ca70038c7.js +0 -1
  292. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-6efc749f2f8ddd20.js +0 -1
  293. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-18d9d8be96e1ce97.js +0 -1
  294. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7966-f07b2913f7326b50.js +0 -1
  295. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-59b2f803f9c88cd6.js +0 -1
  296. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-08790743315de36a.js +0 -1
  297. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/index-13760bb72d823b69.js +0 -1
  298. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-852d403c7bda21b3.js +0 -1
  299. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +0 -1
  300. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +0 -1
  301. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-bd0aff5a5ed8888c.js +0 -1
  302. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-d1ee961387c58b7f.js +0 -1
  303. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-f028ef3880ed856c.js +0 -1
  304. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-ceb06e1616ee9610.js +0 -1
  305. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-8ff16ef9566e911a.js +0 -1
  306. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-74d76300942dcee8.js +0 -1
  307. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-d7a8bc51bb7a1082.js +0 -1
  308. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-79a4cf66a623e667.js +0 -1
  309. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-f59d34429fe022ee.js +0 -1
  310. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +0 -1
  311. /mage_ai/server/frontend_dist/_next/static/{i8pymuJDTVHdWjUP1QSh1 → RhDiJSkcjCsh4xxX4BFBk}/_ssgManifest.js +0 -0
  312. /mage_ai/server/frontend_dist_base_path_template/_next/static/{CKCvjsYCf2imD2X8zAOBf → TdpLLFome13qvM0gXvpHs}/_ssgManifest.js +0 -0
  313. {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/LICENSE +0 -0
  314. {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/WHEEL +0 -0
  315. {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/entry_points.txt +0 -0
  316. {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/top_level.txt +0 -0
@@ -32,6 +32,7 @@ BackfillPolicy.allow_actions([
32
32
 
33
33
  BackfillPolicy.allow_read([
34
34
  'pipeline_run_dates',
35
+ 'run_status_counts',
35
36
  'total_run_count',
36
37
  ] + BackfillPresenter.default_attributes, scopes=[
37
38
  OauthScope.CLIENT_PRIVATE,
@@ -150,6 +150,7 @@ PipelinePolicy.allow_query(
150
150
  PipelinePolicy.allow_query([
151
151
  'from_history_days',
152
152
  'include_schedules',
153
+ 'repo_path',
153
154
  'search',
154
155
  'status[]',
155
156
  'tag[]',
@@ -54,6 +54,7 @@ WorkspacePolicy.allow_write([
54
54
  'storage_access_mode',
55
55
  'storage_request_size',
56
56
  'task_definition',
57
+ 'update_workspace_settings',
57
58
  ], scopes=[
58
59
  OauthScope.CLIENT_PRIVATE,
59
60
  ], on_action=[
@@ -49,6 +49,7 @@ class BackfillPresenter(BasePresenter):
49
49
  ):
50
50
  pipeline_run_dates = preview_run_dates(self.model)
51
51
  data['total_run_count'] = len(pipeline_run_dates)
52
+ data['run_status_counts'] = self.model.pipeline_run_status_counts
52
53
  if include_preview_runs:
53
54
  start_idx = offset
54
55
  end_idx = start_idx + limit
@@ -1,16 +1,22 @@
1
1
  import os
2
- from typing import Dict, List
2
+ from typing import Dict, List, Tuple
3
3
 
4
4
  from mage_ai.api.errors import ApiError
5
5
  from mage_ai.api.resources.GenericResource import GenericResource
6
6
  from mage_ai.authentication.oauth.constants import ProviderName
7
7
  from mage_ai.data_preparation.git import REMOTE_NAME, Git, api
8
8
  from mage_ai.data_preparation.git.clients.base import Client as GitClient
9
- from mage_ai.data_preparation.git.utils import get_provider_from_remote_url
9
+ from mage_ai.data_preparation.git.utils import (
10
+ get_oauth_access_token_for_user,
11
+ get_provider_from_remote_url,
12
+ )
10
13
  from mage_ai.data_preparation.preferences import get_preferences
14
+ from mage_ai.server.logger import Logger
11
15
  from mage_ai.shared.path_fixer import remove_base_repo_path
12
16
  from mage_ai.shared.strings import capitalize_remove_underscore_lower
13
17
 
18
+ logger = Logger().new_server_logger(__name__)
19
+
14
20
 
15
21
  def build_file_object(obj):
16
22
  arr = []
@@ -53,7 +59,7 @@ class GitBranchResource(GenericResource):
53
59
  if remote_url:
54
60
  provider = get_provider_from_remote_url(remote_url)
55
61
 
56
- access_token = api.get_access_token_for_user(user, provider=provider)
62
+ access_token = get_oauth_access_token_for_user(user, provider=provider)
57
63
  if access_token:
58
64
  branches = GitClient.get_client_for_provider(provider)(
59
65
  access_token.token
@@ -64,7 +70,15 @@ class GitBranchResource(GenericResource):
64
70
  arr += [dict(name=branch) for branch in git_manager.branches]
65
71
 
66
72
  if include_remote_branches:
67
- pass
73
+ try:
74
+ git_manager.fetch()
75
+ mage_remote = git_manager.origin
76
+ arr += [
77
+ dict(name=ref.name)
78
+ for ref in mage_remote.refs
79
+ ]
80
+ except Exception:
81
+ logger.warning('Failed to fetch remote branches')
68
82
 
69
83
  return self.build_result_set(
70
84
  arr,
@@ -123,32 +137,27 @@ class GitBranchResource(GenericResource):
123
137
  **kwargs,
124
138
  )
125
139
 
126
- async def update(self, payload, **kwargs):
127
- query = kwargs.get('query') or {}
128
-
129
- git_manager = self.get_git_manager(user=self.current_user)
130
- action_type = payload.get('action_type')
131
- action_payload = payload.get('action_payload', dict())
132
- action_remote = action_payload.get('remote', None)
133
- action_branch = action_payload.get('branch', None)
134
-
135
- files = payload.get('files', None)
136
- message = payload.get('message', None)
137
-
140
+ @classmethod
141
+ def get_oauth_config(
142
+ cls,
143
+ remote_url: str = None,
144
+ remote_name: str = None,
145
+ user=None,
146
+ ) -> Tuple[str, str, str, Dict]:
147
+ git_manager = cls.get_git_manager(user=user)
138
148
  url = None
139
- remote_url = query.get('remote_url', None)
140
149
  if remote_url:
141
- url = remote_url[0]
142
- elif action_remote:
143
- remote = git_manager.repo.remotes[action_remote]
150
+ url = remote_url
151
+ elif remote_name:
152
+ remote = git_manager.repo.remotes[remote_name]
144
153
  url = list(remote.urls)[0]
145
154
 
146
155
  provider = ProviderName.GITHUB
147
156
  if url:
148
157
  provider = get_provider_from_remote_url(url)
149
158
 
150
- access_token = api.get_access_token_for_user(
151
- self.current_user, provider=provider
159
+ access_token = get_oauth_access_token_for_user(
160
+ user, provider=provider
152
161
  )
153
162
  http_access_token = git_manager.get_access_token()
154
163
 
@@ -159,7 +168,7 @@ class GitBranchResource(GenericResource):
159
168
  user_from_api = api.get_user(token, provider=provider)
160
169
  # Default to mage user email if no email is returned from API
161
170
  email = user_from_api.get(
162
- 'email', self.current_user.email if self.current_user else None
171
+ 'email', user.email if user else None
163
172
  )
164
173
  config_overwrite = dict(
165
174
  username=user_from_api.get('username'),
@@ -168,6 +177,30 @@ class GitBranchResource(GenericResource):
168
177
  elif http_access_token:
169
178
  token = http_access_token
170
179
 
180
+ return token, provider, url, config_overwrite
181
+
182
+ async def update(self, payload, **kwargs):
183
+ query = kwargs.get('query') or {}
184
+
185
+ git_manager = self.get_git_manager(user=self.current_user)
186
+ action_type = payload.get('action_type')
187
+ action_payload = payload.get('action_payload', dict())
188
+ action_remote = action_payload.get('remote', None)
189
+ action_branch = action_payload.get('branch', None)
190
+
191
+ files = payload.get('files', None)
192
+ message = payload.get('message', None)
193
+
194
+ remote_url = query.get('remote_url', None)
195
+ if remote_url:
196
+ remote_url = remote_url[0]
197
+
198
+ token, provider, url, config_overwrite = self.get_oauth_config(
199
+ remote_url=remote_url,
200
+ remote_name=action_remote,
201
+ user=self.current_user,
202
+ )
203
+
171
204
  # Recreate git manager with updated config
172
205
  git_manager = self.get_git_manager(
173
206
  user=self.current_user, config_overwrite=config_overwrite
@@ -1,7 +1,7 @@
1
1
  from typing import Dict, List
2
2
 
3
3
  from mage_ai.api.resources.GitBranchResource import GitBranchResource
4
- from mage_ai.data_preparation.git import REMOTE_NAME, Git
4
+ from mage_ai.data_preparation.git import REMOTE_NAME, Git, api
5
5
  from mage_ai.data_preparation.sync import AuthType
6
6
 
7
7
 
@@ -17,6 +17,34 @@ class GitCustomBranchResource(GitBranchResource):
17
17
  user=user,
18
18
  )
19
19
 
20
+ @classmethod
21
+ def create(cls, payload, user, **kwargs):
22
+ branch = payload.get('name')
23
+ remote = payload.get('remote')
24
+
25
+ token, _, url, config_overwrite = cls.get_oauth_config(
26
+ remote_name=remote,
27
+ user=user,
28
+ )
29
+
30
+ git_manager = cls.get_git_manager(
31
+ user=user, config_overwrite=config_overwrite
32
+ )
33
+
34
+ if remote:
35
+ api.switch_branch(
36
+ remote,
37
+ url,
38
+ branch,
39
+ token,
40
+ config_overwrite=config_overwrite,
41
+ user=user,
42
+ )
43
+ else:
44
+ git_manager.switch_branch(branch)
45
+
46
+ return cls(dict(name=git_manager.current_branch), user, **kwargs)
47
+
20
48
  @classmethod
21
49
  async def member(self, pk, user, **kwargs):
22
50
  resource = await GitBranchResource.member(pk, user, **kwargs)
@@ -20,7 +20,7 @@ from mage_ai.authentication.oauth.utils import (
20
20
  refresh_token_for_client,
21
21
  )
22
22
  from mage_ai.authentication.providers.constants import NAME_TO_PROVIDER
23
- from mage_ai.data_preparation.git.api import get_oauth_client_id
23
+ from mage_ai.data_preparation.git.utils import get_oauth_client_id
24
24
  from mage_ai.orchestration.db import safe_db_query
25
25
  from mage_ai.orchestration.db.models.oauth import Oauth2AccessToken, Oauth2Application
26
26
 
@@ -119,6 +119,12 @@ class PipelineResource(BaseResource):
119
119
  if include_schedules:
120
120
  include_schedules = include_schedules[0]
121
121
 
122
+ repo_path = query.get('repo_path', [None])
123
+ if repo_path:
124
+ repo_path = repo_path[0]
125
+ if not repo_path:
126
+ repo_path = get_repo_path(root_project=False)
127
+
122
128
  search_query = query.get('search', [None])
123
129
  if search_query:
124
130
  search_query = search_query[0]
@@ -174,11 +180,11 @@ class PipelineResource(BaseResource):
174
180
 
175
181
  if NO_TAGS_QUERY in tags:
176
182
  pipeline_uuids_with_tags = set(cache.get_all_pipeline_uuids_with_tags())
177
- all_pipeline_uuids = set(Pipeline.get_all_pipelines(get_repo_path()))
183
+ all_pipeline_uuids = set(Pipeline.get_all_pipelines(repo_path))
178
184
  pipeline_uuids_without_tags = list(all_pipeline_uuids - pipeline_uuids_with_tags)
179
185
  pipeline_uuids = pipeline_uuids + pipeline_uuids_without_tags
180
186
  else:
181
- pipeline_uuids = Pipeline.get_all_pipelines(get_repo_path())
187
+ pipeline_uuids = Pipeline.get_all_pipelines(repo_path)
182
188
 
183
189
  if search_query:
184
190
  pipeline_uuids = list(filter(
@@ -206,7 +212,7 @@ class PipelineResource(BaseResource):
206
212
 
207
213
  async def get_pipeline(uuid: str) -> Pipeline:
208
214
  try:
209
- return await Pipeline.get_async(uuid)
215
+ return await Pipeline.get_async(uuid, repo_path=repo_path)
210
216
  except Exception as err:
211
217
  err_message = f'Error loading pipeline {uuid}: {err}.'
212
218
  if err.__class__.__name__ == 'OSError' and 'Too many open files' in err.strerror:
@@ -217,7 +223,7 @@ class PipelineResource(BaseResource):
217
223
 
218
224
  def get_pipeline_with_config(uuid, config: Dict) -> Pipeline:
219
225
  try:
220
- return Pipeline(uuid, config=config)
226
+ return Pipeline(uuid, config=config, repo_path=repo_path)
221
227
  except Exception as err:
222
228
  err_message = f'Error loading pipeline sync {uuid}: {err}.'
223
229
  if err.__class__.__name__ == 'OSError' and 'Too many open files' in err.strerror:
@@ -239,7 +245,7 @@ class PipelineResource(BaseResource):
239
245
  ))
240
246
  else:
241
247
  for uuid in pipeline_uuids:
242
- pipeline_dict = cache.get_model(dict(uuid=uuid))
248
+ pipeline_dict = cache.get_model(dict(uuid=uuid, repo_path=repo_path))
243
249
  if pipeline_dict and pipeline_dict.get('pipeline'):
244
250
  pipeline = get_pipeline_with_config(uuid, pipeline_dict['pipeline'])
245
251
  if pipeline:
@@ -1,3 +1,4 @@
1
+ from sqlalchemy import and_, desc, func
1
2
  from sqlalchemy.orm import selectinload
2
3
 
3
4
  from mage_ai.api.operations.constants import META_KEY_LIMIT, META_KEY_OFFSET
@@ -5,7 +6,10 @@ from mage_ai.api.resources.DatabaseResource import DatabaseResource
5
6
  from mage_ai.api.utils import get_query_timestamps
6
7
  from mage_ai.cache.tag import TagCache
7
8
  from mage_ai.data_preparation.models.block.utils import get_all_descendants
8
- from mage_ai.data_preparation.models.constants import PipelineType
9
+ from mage_ai.data_preparation.models.constants import (
10
+ PIPELINE_RUN_STATUS_LAST_RUN_FAILED,
11
+ PipelineType,
12
+ )
9
13
  from mage_ai.data_preparation.models.pipeline import Pipeline
10
14
  from mage_ai.data_preparation.models.triggers import (
11
15
  ScheduleInterval,
@@ -94,9 +98,38 @@ class PipelineRunResource(DatabaseResource):
94
98
 
95
99
  repo_pipeline_schedule_ids = [s.id for s in PipelineSchedule.repo_query]
96
100
 
101
+ if status == PIPELINE_RUN_STATUS_LAST_RUN_FAILED:
102
+ """
103
+ The initial query below is used to get the last pipeline run retry
104
+ (or individual pipeline run if there are no retries) for each
105
+ grouping of pipeline runs with the same execution_date,
106
+ pipeline_uuid, and pipeline_schedule_id.
107
+ """
108
+ latest_pipeline_runs = PipelineRun.select(
109
+ PipelineRun.id,
110
+ func.row_number()
111
+ .over(
112
+ partition_by=(
113
+ PipelineRun.execution_date,
114
+ PipelineRun.pipeline_schedule_id,
115
+ PipelineRun.pipeline_uuid,
116
+ ),
117
+ order_by=desc(PipelineRun.id))
118
+ .label('row_number')
119
+ ).cte(name='latest_pipeline_runs')
120
+ query = (PipelineRun.select(
121
+ PipelineRun,
122
+ )
123
+ .join(latest_pipeline_runs, and_(
124
+ PipelineRun.id == latest_pipeline_runs.c.id,
125
+ latest_pipeline_runs.c.row_number == 1,
126
+ ))
127
+ )
128
+ else:
129
+ query = PipelineRun.query
130
+
97
131
  results = (
98
- PipelineRun
99
- .query
132
+ query
100
133
  .filter(PipelineRun.pipeline_schedule_id.in_(repo_pipeline_schedule_ids))
101
134
  .options(selectinload(PipelineRun.block_runs))
102
135
  .options(selectinload(PipelineRun.pipeline_schedule))
@@ -120,8 +153,12 @@ class PipelineRunResource(DatabaseResource):
120
153
  results = results.filter(PipelineRun.pipeline_uuid.in_(pipeline_uuids))
121
154
  if pipeline_uuids_with_tags:
122
155
  results = results.filter(PipelineRun.pipeline_uuid.in_(pipeline_uuids_with_tags))
123
- if status is not None:
156
+
157
+ if status == PIPELINE_RUN_STATUS_LAST_RUN_FAILED:
158
+ results = results.filter(PipelineRun.status == PipelineRun.PipelineRunStatus.FAILED)
159
+ elif status is not None:
124
160
  results = results.filter(PipelineRun.status == status)
161
+
125
162
  if statuses:
126
163
  results = results.filter(PipelineRun.status.in_(statuses))
127
164
  if start_timestamp is not None:
@@ -403,6 +403,8 @@ class PipelineScheduleResource(DatabaseResource):
403
403
  if updated_status == ScheduleStatus.ACTIVE and self.model.status == ScheduleStatus.INACTIVE:
404
404
  payload['last_enabled_at'] = datetime.now(tz=pytz.UTC)
405
405
 
406
+ old_name = self.model.name
407
+
406
408
  resource = super().update(payload)
407
409
  updated_model = resource.model
408
410
 
@@ -424,11 +426,13 @@ class PipelineScheduleResource(DatabaseResource):
424
426
  update_only_if_exists = (
425
427
  not pipeline.should_save_trigger_in_code_automatically()
426
428
  )
429
+ old_trigger_name = old_name if old_name != updated_model.name else None
427
430
 
428
431
  add_or_update_trigger_for_pipeline_and_persist(
429
432
  trigger,
430
433
  pipeline.uuid,
431
434
  update_only_if_exists=update_only_if_exists,
435
+ old_trigger_name=old_trigger_name,
432
436
  )
433
437
 
434
438
  return self
@@ -3,9 +3,11 @@ from typing import Dict
3
3
  from mage_ai.api.errors import ApiError
4
4
  from mage_ai.api.resources.GenericResource import GenericResource
5
5
  from mage_ai.authentication.oauth.constants import ProviderName
6
- from mage_ai.data_preparation.git import api
7
6
  from mage_ai.data_preparation.git.clients.base import Client as GitClient
8
- from mage_ai.data_preparation.git.utils import get_provider_from_remote_url
7
+ from mage_ai.data_preparation.git.utils import (
8
+ get_oauth_access_token_for_user,
9
+ get_provider_from_remote_url,
10
+ )
9
11
 
10
12
 
11
13
  def pull_request_to_dict(pr) -> Dict:
@@ -40,7 +42,7 @@ class PullRequestResource(GenericResource):
40
42
  if remote_url:
41
43
  provider = get_provider_from_remote_url(remote_url)
42
44
 
43
- access_token = api.get_access_token_for_user(user, provider=provider)
45
+ access_token = get_oauth_access_token_for_user(user, provider=provider)
44
46
 
45
47
  if access_token:
46
48
  arr = GitClient.get_client_for_provider(provider)(
@@ -84,7 +86,7 @@ class PullRequestResource(GenericResource):
84
86
  remote_url = payload.get('remote_url')
85
87
  provider = get_provider_from_remote_url(remote_url)
86
88
 
87
- access_token = api.get_access_token_for_user(user, provider=provider)
89
+ access_token = get_oauth_access_token_for_user(user, provider=provider)
88
90
  if not access_token:
89
91
  error.update(
90
92
  dict(
@@ -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)
@@ -123,14 +123,15 @@ class WorkspaceResource(GenericResource):
123
123
  error = ApiError.RESOURCE_ERROR.copy()
124
124
  try:
125
125
  action = payload.pop('action')
126
- args = ignore_keys(payload, ['name', 'cluster_type'])
127
126
  if action == 'stop':
128
- workspace.stop(**args)
127
+ workspace.stop(**payload)
129
128
  elif action == 'resume':
130
- workspace.resume(**args)
129
+ workspace.resume(**payload)
130
+ elif action == 'patch':
131
+ workspace.update(payload)
131
132
  elif action == 'add_to_ingress':
132
133
  if isinstance(workspace, KubernetesWorkspace):
133
- workspace.add_to_ingress(**args)
134
+ workspace.add_to_ingress(**payload)
134
135
  else:
135
136
  raise Exception('This workspace does not support ingress.')
136
137
  except Exception as ex:
@@ -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