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
@@ -2,6 +2,7 @@ from logging import Logger
2
2
  from typing import Dict, List
3
3
 
4
4
  from mage_ai.data_preparation.models.block import Block
5
+ from mage_ai.data_preparation.models.block.remote.models import RemoteBlock
5
6
 
6
7
 
7
8
  class GlobalDataProductBlock(Block):
@@ -37,13 +38,35 @@ class GlobalDataProductBlock(Block):
37
38
  # Avoid circular dependency of Pipeline class
38
39
  from mage_ai.orchestration.triggers import global_data_product as trigger
39
40
 
40
- trigger.trigger_and_check_status(
41
- self.get_global_data_product(),
41
+ global_data_product = self.get_global_data_product()
42
+
43
+ pipeline_run = trigger.trigger_and_check_status(
44
+ global_data_product,
42
45
  block=self,
43
46
  from_notebook=from_notebook,
44
47
  logger=logger,
45
48
  logging_tags=logging_tags,
49
+ poll_interval=30,
50
+ remote_blocks=global_vars.get('remote_blocks'),
46
51
  variables=global_vars.get('variables') if global_vars else None,
47
52
  )
48
53
 
54
+ if pipeline_run and pipeline_run.pipeline:
55
+ pipeline = pipeline_run.pipeline
56
+
57
+ arr = []
58
+
59
+ for block in global_data_product.get_blocks():
60
+ if block and block.uuid:
61
+ arr.append(RemoteBlock.load(
62
+ block_uuid=block.uuid,
63
+ execution_partition=pipeline_run.execution_partition,
64
+ pipeline_uuid=pipeline.uuid,
65
+ repo_path=pipeline.repo_path,
66
+ ))
67
+
68
+ return [
69
+ dict(remote_blocks=arr),
70
+ ]
71
+
49
72
  return []
@@ -155,7 +155,7 @@ class IntegrationBlock(Block):
155
155
  proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
156
156
 
157
157
  for line in proc.stdout:
158
- f.write(line.decode()),
158
+ f.write(line.decode())
159
159
  print_log_from_line(
160
160
  line,
161
161
  config=config,
@@ -0,0 +1,58 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any, List
3
+
4
+ from mage_ai.shared.models import BaseDataClass
5
+
6
+
7
+ @dataclass
8
+ class RemoteBlock(BaseDataClass):
9
+ block_uuid: str
10
+ execution_partition: str = None
11
+ pipeline_uuid: str = None
12
+ repo_path: str = None
13
+
14
+ def __post_init__(self):
15
+ self._block = None
16
+ self._pipeline = None
17
+
18
+ @property
19
+ def pipeline(self):
20
+ from mage_ai.data_preparation.models.pipeline import Pipeline
21
+
22
+ if self._pipeline:
23
+ return self._pipeline
24
+
25
+ self._pipeline = Pipeline.get(
26
+ self.pipeline_uuid,
27
+ all_projects=True,
28
+ check_if_exists=False,
29
+ repo_path=self.repo_path,
30
+ use_repo_path=True if self.repo_path else False,
31
+ )
32
+
33
+ return self._pipeline
34
+
35
+ @property
36
+ def block(self):
37
+ if self._block:
38
+ return self._block
39
+
40
+ self._block = self.pipeline.get_block(self.block_uuid)
41
+
42
+ return self._block
43
+
44
+ @property
45
+ def variable_uuids(self):
46
+ return self.block.output_variables(execution_partition=self.execution_partition)
47
+
48
+ def get_outputs(self, **kwargs) -> List[Any]:
49
+ arr = []
50
+ for variable_uuid in self.variable_uuids:
51
+ output = self.pipeline.get_block_variable(
52
+ self.block_uuid,
53
+ variable_uuid,
54
+ partition=self.execution_partition,
55
+ )
56
+ arr.append(output)
57
+
58
+ return arr
@@ -337,7 +337,7 @@ def execute_sql_code(
337
337
  if should_query:
338
338
  return [
339
339
  loader.load(
340
- f'SELECT * FROM {table_name}',
340
+ f'SELECT * FROM {schema}.{table_name}',
341
341
  verbose=False,
342
342
  ),
343
343
  ]
@@ -29,6 +29,7 @@ from mage_ai.data_preparation.models.block.dynamic.utils import (
29
29
  from mage_ai.data_preparation.models.block.dynamic.utils import (
30
30
  should_reduce_output as should_reduce_output_original,
31
31
  )
32
+ from mage_ai.data_preparation.models.block.remote.models import RemoteBlock
32
33
  from mage_ai.data_preparation.models.constants import (
33
34
  DATAFRAME_ANALYSIS_MAX_COLUMNS,
34
35
  BlockType,
@@ -393,6 +394,7 @@ def fetch_input_variables(
393
394
  dynamic_block_flags: List[DynamicBlockFlag] = None,
394
395
  metadata: Dict = None,
395
396
  upstream_block_uuids_override: List[str] = None,
397
+ current_block=None,
396
398
  ) -> Tuple[List, List, List]:
397
399
  """
398
400
  Fetches the input variables for a block.
@@ -510,6 +512,22 @@ def fetch_input_variables(
510
512
  if BlockType.GLOBAL_DATA_PRODUCT == upstream_block.type:
511
513
  global_data_product = upstream_block.get_global_data_product()
512
514
  input_vars[idx] = global_data_product.get_outputs()
515
+
516
+ mds = {}
517
+ variable_uuids = upstream_block.output_variables(
518
+ execution_partition=execution_partition,
519
+ )
520
+ for variable_uuid in variable_uuids:
521
+ md = pipeline.get_block_variable(
522
+ upstream_block_uuid,
523
+ variable_uuid,
524
+ partition=execution_partition,
525
+ )
526
+ if isinstance(md, dict):
527
+ mds.update(md)
528
+
529
+ kwargs_vars.append(mds)
530
+
513
531
  continue
514
532
 
515
533
  # Block output variables for upstream_block_uuid
@@ -743,6 +761,26 @@ def fetch_input_variables(
743
761
  __uuid='output_variables'
744
762
  )
745
763
 
764
+ if kwargs_vars:
765
+ kwargs_vars2 = []
766
+
767
+ remote_blocks_output = []
768
+ for kwargs in kwargs_vars:
769
+ for remote_block_dict in kwargs.get('remote_blocks', []):
770
+ # Global data products only need the remote block information, not the output
771
+ if current_block and BlockType.GLOBAL_DATA_PRODUCT == current_block.type:
772
+ output = remote_block_dict
773
+ else:
774
+ output = RemoteBlock.load(**remote_block_dict).get_outputs()
775
+ remote_blocks_output.append(output)
776
+
777
+ for kwargs in kwargs_vars:
778
+ if kwargs.get('remote_blocks'):
779
+ kwargs['remote_blocks'] = remote_blocks_output
780
+ kwargs_vars2.append(kwargs)
781
+
782
+ kwargs_vars = kwargs_vars2
783
+
746
784
  return input_vars, kwargs_vars, upstream_block_uuids_final
747
785
 
748
786
 
@@ -24,6 +24,8 @@ PREFERENCES_FILE = '.preferences.yaml'
24
24
  REPO_CONFIG_FILE = 'metadata.yaml'
25
25
  VARIABLE_DIR = '.variables'
26
26
 
27
+ PIPELINE_RUN_STATUS_LAST_RUN_FAILED = 'last_run_failed'
28
+
27
29
 
28
30
  class AIMode(str, Enum):
29
31
  OPEN_AI = 'open_ai'
@@ -75,6 +75,18 @@ class GlobalDataProduct:
75
75
 
76
76
  return self._object
77
77
 
78
+ def get_blocks(self) -> List:
79
+ arr = []
80
+
81
+ if not self.settings or len(self.settings) == 0:
82
+ return arr
83
+
84
+ if GlobalDataProductObjectType.PIPELINE == self.object_type:
85
+ for block_uuid in self.settings.keys():
86
+ arr.append(self.pipeline.get_block(block_uuid))
87
+
88
+ return arr
89
+
78
90
  def get_outputs(self, from_notebook: bool = False, global_vars: Dict = None, **kwargs) -> Dict:
79
91
  data = {}
80
92
 
@@ -7,7 +7,7 @@ import tempfile
7
7
  import zipfile
8
8
  from datetime import datetime, timezone
9
9
  from io import BytesIO
10
- from typing import Any, Callable, Dict, List, Tuple, Union
10
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
11
11
 
12
12
  import aiofiles
13
13
  import pytz
@@ -62,6 +62,8 @@ from mage_ai.data_preparation.shared.utils import get_template_vars
62
62
  from mage_ai.data_preparation.templates.utils import copy_template_directory
63
63
  from mage_ai.data_preparation.variable_manager import VariableManager
64
64
  from mage_ai.orchestration.constants import Entity
65
+ from mage_ai.orchestration.notification.config import NotificationConfig
66
+ from mage_ai.orchestration.notification.sender import NotificationSender
65
67
  from mage_ai.settings.platform import build_repo_path_for_all_projects
66
68
  from mage_ai.settings.platform.constants import project_platform_activated
67
69
  from mage_ai.settings.repo import get_repo_path
@@ -97,6 +99,7 @@ class Pipeline:
97
99
  self.description = description
98
100
  self.executor_config = dict()
99
101
  self.executor_type = None
102
+ self._rendered_executor_type = None # Render template variables
100
103
  self.extensions = {}
101
104
  self.name = None
102
105
  self.notification_config = dict()
@@ -339,7 +342,7 @@ class Pipeline:
339
342
  duplicate_pipeline_dict['name'] = duplicate_pipeline_uuid
340
343
  safe_write(
341
344
  duplicate_pipeline.config_path,
342
- yaml.dump(duplicate_pipeline_dict)
345
+ yaml.dump(duplicate_pipeline_dict),
343
346
  )
344
347
 
345
348
  tags = duplicate_pipeline_dict.get('tags')
@@ -514,7 +517,7 @@ class Pipeline:
514
517
 
515
518
  if not os.path.exists(config_path):
516
519
  raise Exception(f'Pipeline {uuid} does not exist.')
517
- async with aiofiles.open(config_path, mode='r') as f:
520
+ async with aiofiles.open(config_path, mode='r', encoding='utf-8') as f:
518
521
  config = yaml.safe_load(await f.read()) or {}
519
522
 
520
523
  if PipelineType.INTEGRATION == config.get('type'):
@@ -673,6 +676,7 @@ class Pipeline:
673
676
  analyze_outputs: bool = False,
674
677
  build_block_output_stdout: Callable[..., object] = None,
675
678
  global_vars=None,
679
+ retry_config=None,
676
680
  run_sensors: bool = True,
677
681
  run_tests: bool = True,
678
682
  update_status: bool = True,
@@ -689,6 +693,7 @@ class Pipeline:
689
693
  StreamingPipelineExecutor(self).execute(
690
694
  build_block_output_stdout=build_block_output_stdout,
691
695
  global_vars=global_vars,
696
+ retry_config=retry_config,
692
697
  )
693
698
  else:
694
699
  root_blocks = []
@@ -715,7 +720,7 @@ class Pipeline:
715
720
  def get_config_from_yaml(self):
716
721
  if not os.path.exists(self.config_path):
717
722
  raise Exception(f'Pipeline {self.uuid} does not exist in repo_path {self.repo_path}.')
718
- with open(self.config_path) as fp:
723
+ with open(self.config_path, encoding='utf-8') as fp:
719
724
  config = yaml.full_load(fp) or {}
720
725
  return config
721
726
 
@@ -726,6 +731,16 @@ class Pipeline:
726
731
  config = json.load(f)
727
732
  return config
728
733
 
734
+ def get_notification_sender(self):
735
+ return NotificationSender(
736
+ NotificationConfig.load(
737
+ config=merge_dict(
738
+ self.repo_config.notification_config,
739
+ self.notification_config,
740
+ ),
741
+ ),
742
+ )
743
+
729
744
  def load_config_from_yaml(self):
730
745
  catalog = None
731
746
  if os.path.exists(self.catalog_config_path):
@@ -1294,7 +1309,9 @@ class Pipeline:
1294
1309
  file_path = (configuration.get('file_source') or {}).get('path')
1295
1310
  if file_path:
1296
1311
  # Check for block name with period to avoid replacing a directory name
1297
- new_file_path = file_path.replace(f'{block.name}.', f'{name}.')
1312
+ new_file_path = file_path.replace(
1313
+ f'{clean_name(block.name)}.', f'{clean_name(name)}.'
1314
+ )
1298
1315
  configuration['file_source']['path'] = new_file_path
1299
1316
  block_update_payload['configuration'] = configuration
1300
1317
  blocks_to_remove_from_cache.append(block.to_dict())
@@ -1752,10 +1769,13 @@ class Pipeline:
1752
1769
  def get_executable_blocks(self):
1753
1770
  return [b for b in self.blocks_by_uuid.values() if b.executable]
1754
1771
 
1755
- def get_executor_type(self) -> str:
1756
- if self.executor_type:
1757
- return Template(self.executor_type).render(**get_template_vars())
1758
- return self.executor_type
1772
+ def get_executor_type(self) -> Optional[str]:
1773
+ if self._rendered_executor_type is None:
1774
+ if self.executor_type:
1775
+ return Template(self.executor_type).render(**get_template_vars())
1776
+ else:
1777
+ self._rendered_executor_type = self.executor_type
1778
+ return self._rendered_executor_type
1759
1779
 
1760
1780
  def has_block(self, block_uuid: str, block_type: str = None, extension_uuid: str = None):
1761
1781
  if extension_uuid:
@@ -2132,7 +2152,7 @@ class Pipeline:
2132
2152
  'Blocks cannot be added or removed when saving content, please try again.',
2133
2153
  )
2134
2154
 
2135
- content = yaml.dump(pipeline_dict)
2155
+ content = yaml.dump(pipeline_dict, allow_unicode=True)
2136
2156
 
2137
2157
  safe_write(self.config_path, content)
2138
2158
 
@@ -2205,7 +2225,7 @@ class Pipeline:
2205
2225
  'Blocks cannot be added or removed when saving content, please try again.',
2206
2226
  )
2207
2227
 
2208
- content = yaml.dump(pipeline_dict)
2228
+ content = yaml.dump(pipeline_dict, allow_unicode=True)
2209
2229
 
2210
2230
  test_path = f'{self.config_path}.test'
2211
2231
  async with aiofiles.open(test_path, mode='w', encoding='utf-8') as fp:
@@ -206,6 +206,7 @@ def add_or_update_trigger_for_pipeline_and_persist(
206
206
  trigger: Trigger,
207
207
  pipeline_uuid: str,
208
208
  update_only_if_exists: bool = False,
209
+ old_trigger_name: str = None,
209
210
  ) -> Dict:
210
211
  trigger_configs_by_name = get_trigger_configs_by_name(pipeline_uuid)
211
212
 
@@ -214,13 +215,14 @@ def add_or_update_trigger_for_pipeline_and_persist(
214
215
  have, so we need to set "envs" on the updated trigger if it already exists.
215
216
  Otherwise, it will get overwritten when updating the trigger in code.
216
217
  """
217
- existing_trigger = trigger_configs_by_name.get(trigger.name)
218
+ trigger_name = trigger.name if old_trigger_name is None else old_trigger_name
219
+ existing_trigger = trigger_configs_by_name.get(trigger_name)
218
220
  if existing_trigger is not None:
219
221
  trigger.envs = existing_trigger.get('envs', [])
220
222
  elif update_only_if_exists:
221
223
  return None
222
224
 
223
- trigger_configs_by_name[trigger.name] = trigger.to_dict()
225
+ trigger_configs_by_name[trigger_name] = trigger.to_dict()
224
226
  yaml_config = dict(triggers=list(trigger_configs_by_name.values()))
225
227
  content = yaml.safe_dump(yaml_config)
226
228
  trigger_file_path = get_triggers_file_path(pipeline_uuid)
@@ -19,6 +19,12 @@ STRING_SERIALIZABLE_COLUMN_TYPES = set([
19
19
  'ObjectId',
20
20
  ])
21
21
 
22
+ AMBIGUOUS_COLUMN_TYPES = set([
23
+ 'mixed-integer',
24
+ 'complex',
25
+ 'unknown-array',
26
+ ])
27
+
22
28
  CAST_TYPE_COLUMN_TYPES = set([
23
29
  'Int64',
24
30
  'int64',
@@ -7,7 +7,7 @@ from typing import Any, Dict, List
7
7
  import numpy as np
8
8
  import pandas as pd
9
9
  import polars as pl
10
- from pandas.api.types import is_object_dtype
10
+ from pandas.api.types import infer_dtype, is_object_dtype
11
11
  from pandas.core.indexes.range import RangeIndex
12
12
 
13
13
  from mage_ai.data_cleaner.shared.utils import is_geo_dataframe, is_spark_dataframe
@@ -18,6 +18,7 @@ from mage_ai.data_preparation.models.constants import (
18
18
  VARIABLE_DIR,
19
19
  )
20
20
  from mage_ai.data_preparation.models.utils import ( # dask_from_pandas,
21
+ AMBIGUOUS_COLUMN_TYPES,
21
22
  STRING_SERIALIZABLE_COLUMN_TYPES,
22
23
  apply_transform_pandas,
23
24
  cast_column_types,
@@ -282,7 +283,9 @@ class Variable:
282
283
  else:
283
284
  self.__write_json(data)
284
285
 
285
- self.write_metadata()
286
+ if self.variable_type != VariableType.SPARK_DATAFRAME:
287
+ # Not write json file in spark data directory to avoid read error
288
+ self.write_metadata()
286
289
 
287
290
  async def write_data_async(self, data: Any) -> None:
288
291
  """
@@ -315,7 +318,9 @@ class Variable:
315
318
  else:
316
319
  await self.__write_json_async(data)
317
320
 
318
- self.write_metadata()
321
+ if self.variable_type != VariableType.SPARK_DATAFRAME:
322
+ # Not write json file in spark data directory to avoid read error
323
+ self.write_metadata()
319
324
 
320
325
  def write_metadata(self) -> None:
321
326
  """
@@ -578,10 +583,19 @@ class Variable:
578
583
  else:
579
584
  series_non_null = df_col.dropna()
580
585
  if len(series_non_null) > 0:
581
- coltype = type(series_non_null.iloc[0])
586
+ sample_element = series_non_null.iloc[0]
587
+ coltype = type(sample_element)
588
+ coltype_inferred = infer_dtype(series_non_null)
582
589
  if is_object_dtype(series_non_null.dtype):
583
590
  if coltype.__name__ in STRING_SERIALIZABLE_COLUMN_TYPES:
584
591
  cast_coltype = str
592
+ # If the column is a "primitive" type, i.e. int/bool/etc and there is
593
+ # a mix of types in the column, cast to string
594
+ elif (
595
+ not hasattr(sample_element, '__dict__')
596
+ and coltype_inferred in AMBIGUOUS_COLUMN_TYPES
597
+ ):
598
+ cast_coltype = str
585
599
  else:
586
600
  cast_coltype = coltype
587
601
  try:
@@ -310,8 +310,9 @@ def get_cluster_type(repo_path=None) -> Optional[ClusterType]:
310
310
 
311
311
  def set_project_uuid_from_metadata() -> None:
312
312
  global project_uuid
313
- if os.path.exists(get_metadata_path()):
314
- with open(get_metadata_path(), 'r', encoding='utf-8') as f:
313
+ metadata_path = get_metadata_path(root_project=True)
314
+ if os.path.exists(metadata_path):
315
+ with open(metadata_path, 'r', encoding='utf-8') as f:
315
316
  config = yml.load(f) or {}
316
317
  project_uuid = config.get('project_uuid')
317
318
 
@@ -25,7 +25,7 @@ def get_template_vars_no_db(include_python_libraries: Dict = None) -> Dict[str,
25
25
  try:
26
26
  from mage_ai.services.aws.secrets_manager.secrets_manager import get_secret
27
27
  kwargs['aws_secret_var'] = get_secret
28
- except ImportError:
28
+ except Exception:
29
29
  pass
30
30
 
31
31
  try:
@@ -92,12 +92,18 @@ class LocalStorage(BaseStorage):
92
92
  os.makedirs(dirname, exist_ok=True)
93
93
 
94
94
  with open(file_path, 'w') as file:
95
- simplejson.dump(
96
- data,
97
- file,
98
- default=encode_complex,
99
- ignore_nan=True,
100
- )
95
+ try:
96
+ simplejson.dump(
97
+ data,
98
+ file,
99
+ default=encode_complex,
100
+ ignore_nan=True,
101
+ )
102
+ except ValueError as err:
103
+ if is_debug():
104
+ raise err
105
+ else:
106
+ print(f'[ERROR] LocalStorage.write_json_file: {err}')
101
107
 
102
108
  async def write_json_file_async(self, file_path: str, data) -> None:
103
109
  async with aiofiles.open(file_path, mode='w') as file:
@@ -23,8 +23,8 @@ def export_data_to_mysql(df: DataFrame, **kwargs) -> None:
23
23
  with MySQL.with_config(ConfigFileLoader(config_path, config_profile)) as loader:
24
24
  loader.export(
25
25
  df,
26
- None,
27
- table_name,
26
+ schema_name=None,
27
+ table_name=table_name,
28
28
  index=False, # Specifies whether to include index in exported table
29
29
  if_exists='replace', # Specify resolution policy if table name already exists
30
30
  )
@@ -0,0 +1,27 @@
1
+ from os import path
2
+
3
+ from pandas import DataFrame
4
+
5
+ from mage_ai.settings.repo import get_repo_path
6
+ from mage_ai.io.config import ConfigFileLoader
7
+ from mage_ai.io.oracledb import OracleDB
8
+
9
+ if 'data_exporter' not in globals():
10
+ from mage_ai.data_preparation.decorators import data_exporter
11
+
12
+
13
+ @data_exporter
14
+ def export_data_to_oracledb(df: DataFrame, **kwargs) -> None:
15
+ """
16
+ Template to export data to Oracledb.
17
+ """
18
+ config_path = path.join(get_repo_path(), 'io_config.yaml')
19
+ config_profile = 'default'
20
+ table_name = 'your_table_name'
21
+
22
+ with OracleDB.with_config(ConfigFileLoader(config_path, config_profile)) as exporter:
23
+ exporter.export(
24
+ df,
25
+ table_name=table_name,
26
+ if_exists='replace', # Specify resolution policy if table name already exists
27
+ )
@@ -43,6 +43,7 @@ spark_config:
43
43
  # e.g. kwargs['context']['spark'] = spark_session
44
44
  custom_session_var_name: 'spark'
45
45
 
46
+ help_improve_mage: true
46
47
  notification_config:
47
48
  alert_on:
48
49
  - trigger_failure