mage-ai 0.9.67__py3-none-any.whl → 0.9.69__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (307) hide show
  1. mage_ai/api/monitors/BaseMonitor.py +1 -2
  2. mage_ai/api/policies/PipelinePolicy.py +2 -0
  3. mage_ai/api/resources/BlockLayoutItemResource.py +2 -2
  4. mage_ai/api/resources/BlockResource.py +4 -3
  5. mage_ai/api/resources/CacheItemResource.py +1 -1
  6. mage_ai/api/resources/GitBranchResource.py +3 -5
  7. mage_ai/api/resources/PageBlockLayoutResource.py +2 -2
  8. mage_ai/api/resources/PipelineResource.py +13 -0
  9. mage_ai/api/resources/PipelineRunResource.py +10 -1
  10. mage_ai/api/resources/SeedResource.py +2 -1
  11. mage_ai/api/resources/SessionResource.py +13 -1
  12. mage_ai/authentication/permissions/constants.py +2 -0
  13. mage_ai/authentication/permissions/seed.py +32 -21
  14. mage_ai/authentication/providers/active_directory.py +4 -3
  15. mage_ai/authentication/providers/okta.py +22 -83
  16. mage_ai/cache/tag.py +3 -0
  17. mage_ai/cluster_manager/manage.py +4 -1
  18. mage_ai/data_preparation/executors/k8s_block_executor.py +8 -1
  19. mage_ai/data_preparation/executors/k8s_pipeline_executor.py +12 -1
  20. mage_ai/data_preparation/executors/streaming_pipeline_executor.py +77 -7
  21. mage_ai/data_preparation/logging/gcs_logger_manager.py +7 -4
  22. mage_ai/data_preparation/models/block/__init__.py +28 -71
  23. mage_ai/data_preparation/models/block/block_factory.py +77 -0
  24. mage_ai/data_preparation/models/block/data_integration/mixins.py +16 -5
  25. mage_ai/data_preparation/models/block/dbt/block.py +5 -7
  26. mage_ai/data_preparation/models/block/dynamic/child.py +3 -0
  27. mage_ai/data_preparation/models/block/dynamic/variables.py +2 -2
  28. mage_ai/data_preparation/models/block/extension/utils.py +1 -0
  29. mage_ai/data_preparation/models/block/global_data_product/__init__.py +9 -3
  30. mage_ai/data_preparation/models/block/integration/__init__.py +13 -9
  31. mage_ai/data_preparation/models/block/platform/mixins.py +1 -1
  32. mage_ai/data_preparation/models/block/sql/__init__.py +1 -1
  33. mage_ai/data_preparation/models/pipeline.py +102 -19
  34. mage_ai/data_preparation/models/utils.py +6 -0
  35. mage_ai/data_preparation/models/variable.py +18 -4
  36. mage_ai/data_preparation/repo_manager.py +3 -2
  37. mage_ai/data_preparation/shared/utils.py +1 -1
  38. mage_ai/data_preparation/storage/gcs_storage.py +1 -1
  39. mage_ai/data_preparation/templates/constants.py +7 -0
  40. mage_ai/data_preparation/templates/data_exporters/mysql.py +2 -2
  41. mage_ai/data_preparation/templates/data_exporters/oracledb.py +27 -0
  42. mage_ai/data_preparation/templates/repo/metadata.yaml +1 -0
  43. mage_ai/io/bigquery.py +131 -58
  44. mage_ai/io/export_utils.py +3 -0
  45. mage_ai/io/mysql.py +38 -6
  46. mage_ai/io/oracledb.py +138 -3
  47. mage_ai/io/snowflake.py +152 -29
  48. mage_ai/io/sql.py +4 -0
  49. mage_ai/orchestration/db/__init__.py +2 -2
  50. mage_ai/orchestration/db/models/oauth.py +4 -4
  51. mage_ai/orchestration/db/models/schedules.py +10 -3
  52. mage_ai/orchestration/job_manager.py +6 -0
  53. mage_ai/orchestration/notification/sender.py +8 -0
  54. mage_ai/orchestration/pipeline_scheduler_original.py +26 -7
  55. mage_ai/orchestration/queue/celery_queue.py +8 -1
  56. mage_ai/orchestration/queue/process_queue.py +67 -4
  57. mage_ai/orchestration/queue/queue.py +8 -0
  58. mage_ai/server/constants.py +1 -1
  59. mage_ai/server/frontend_dist/404.html +2 -2
  60. mage_ai/server/{frontend_dist_base_path_template/_next/static/khKiaJtwrslgMmp4YSa1f → frontend_dist/_next/static/_krrrgup_C-dPOpX36S8I}/_buildManifest.js +1 -1
  61. mage_ai/server/frontend_dist/_next/static/chunks/1557-df144fbd8b2208c3.js +1 -0
  62. mage_ai/server/frontend_dist/_next/static/chunks/2717-d9200be634dd6766.js +1 -0
  63. mage_ai/server/frontend_dist/_next/static/chunks/3548-fa0792ddb88f4646.js +1 -0
  64. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/4241-ccd0126f5750cc35.js → frontend_dist/_next/static/chunks/4241-4499461184ba0d23.js} +1 -1
  65. mage_ai/server/frontend_dist/_next/static/chunks/5627-237a3de578538022.js +1 -0
  66. mage_ai/server/frontend_dist/_next/static/chunks/5699-6d708c6b2153ea08.js +1 -0
  67. mage_ai/server/frontend_dist/_next/static/chunks/7361-8a23dd8360593e7a.js +1 -0
  68. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/7966-5c1786fb7e7a48f5.js → frontend_dist/_next/static/chunks/7966-f07b2913f7326b50.js} +1 -1
  69. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-d9c89527266296f7.js +1 -0
  70. mage_ai/server/frontend_dist/_next/static/chunks/pages/index-4e12783b064c1cfe.js +1 -0
  71. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/[user]-8bbfa0c19b5e4cb3.js +1 -0
  72. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-852d403c7bda21b3.js +1 -0
  73. mage_ai/server/frontend_dist/_next/static/chunks/pages/overview-597b74828bf105db.js +1 -0
  74. mage_ai/server/frontend_dist/_next/static/chunks/pages/{pipeline-runs-a66b4c7641ae03eb.js → pipeline-runs-3edc6270c5b0e962.js} +1 -1
  75. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +1 -0
  76. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-e1dd1ed71d26c10d.js +1 -0
  77. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-1417ad1c821d720a.js +1 -0
  78. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-59aca25a5b1d3998.js +1 -0
  79. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-90abafc7ed61c582.js +1 -0
  80. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-1bdfda8edc9cf4a8.js +1 -0
  81. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-3591d035bb3bb2b8.js +1 -0
  82. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-503049734a8b082f.js +1 -0
  83. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/settings-c2e9ef989c8bfa73.js +1 -0
  84. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-5b26eeda8aed8a7b.js +1 -0
  85. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-79a4cf66a623e667.js → frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-8b793b3b696a2cd3.js} +1 -1
  86. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{users-86814e581acaf5db.js → users-a4db8710f703c729.js} +1 -1
  87. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-7d38b2f7c3e918a1.js → frontend_dist/_next/static/chunks/pages/sign-in-09414a8b66fb6f06.js} +1 -1
  88. mage_ai/server/frontend_dist/_next/static/chunks/pages/triggers-9cba3211434a8966.js +1 -0
  89. mage_ai/server/frontend_dist/block-layout.html +2 -2
  90. mage_ai/server/frontend_dist/compute.html +2 -2
  91. mage_ai/server/frontend_dist/files.html +2 -2
  92. mage_ai/server/frontend_dist/global-data-products/[...slug].html +2 -2
  93. mage_ai/server/frontend_dist/global-data-products.html +2 -2
  94. mage_ai/server/frontend_dist/global-hooks/[...slug].html +2 -2
  95. mage_ai/server/frontend_dist/global-hooks.html +2 -2
  96. mage_ai/server/frontend_dist/index.html +2 -2
  97. mage_ai/server/frontend_dist/manage/files.html +2 -2
  98. mage_ai/server/frontend_dist/manage/settings.html +2 -2
  99. mage_ai/server/frontend_dist/manage/users/[user].html +2 -2
  100. mage_ai/server/frontend_dist/manage/users/new.html +2 -2
  101. mage_ai/server/frontend_dist/manage/users.html +2 -2
  102. mage_ai/server/frontend_dist/manage.html +2 -2
  103. mage_ai/server/frontend_dist/oauth.html +3 -3
  104. mage_ai/server/frontend_dist/overview.html +2 -2
  105. mage_ai/server/frontend_dist/pipeline-runs.html +2 -2
  106. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +2 -2
  107. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +2 -2
  108. mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +2 -2
  109. mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +2 -2
  110. mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +2 -2
  111. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +2 -2
  112. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
  113. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +2 -2
  114. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +2 -2
  115. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +2 -2
  116. mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +2 -2
  117. mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +2 -2
  118. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +2 -2
  119. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +2 -2
  120. mage_ai/server/frontend_dist/pipelines/[pipeline].html +2 -2
  121. mage_ai/server/frontend_dist/pipelines.html +2 -2
  122. mage_ai/server/frontend_dist/platform/global-hooks/[...slug].html +2 -2
  123. mage_ai/server/frontend_dist/platform/global-hooks.html +2 -2
  124. mage_ai/server/frontend_dist/settings/account/profile.html +2 -2
  125. mage_ai/server/frontend_dist/settings/platform/preferences.html +2 -2
  126. mage_ai/server/frontend_dist/settings/platform/settings.html +2 -2
  127. mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +2 -2
  128. mage_ai/server/frontend_dist/settings/workspace/permissions.html +2 -2
  129. mage_ai/server/frontend_dist/settings/workspace/preferences.html +2 -2
  130. mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +2 -2
  131. mage_ai/server/frontend_dist/settings/workspace/roles.html +2 -2
  132. mage_ai/server/frontend_dist/settings/workspace/sync-data.html +2 -2
  133. mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +2 -2
  134. mage_ai/server/frontend_dist/settings/workspace/users.html +2 -2
  135. mage_ai/server/frontend_dist/settings.html +2 -2
  136. mage_ai/server/frontend_dist/sign-in.html +5 -5
  137. mage_ai/server/frontend_dist/templates/[...slug].html +2 -2
  138. mage_ai/server/frontend_dist/templates.html +2 -2
  139. mage_ai/server/frontend_dist/terminal.html +2 -2
  140. mage_ai/server/frontend_dist/test.html +2 -2
  141. mage_ai/server/frontend_dist/triggers.html +2 -2
  142. mage_ai/server/frontend_dist/version-control.html +2 -2
  143. mage_ai/server/frontend_dist_base_path_template/404.html +2 -2
  144. mage_ai/server/{frontend_dist/_next/static/vPsMu6Fi2zrHaf2fRXKRO → frontend_dist_base_path_template/_next/static/KLL5mirre9d7_ZeEpaw3s}/_buildManifest.js +1 -1
  145. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-df144fbd8b2208c3.js +1 -0
  146. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-d9200be634dd6766.js +1 -0
  147. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-fa0792ddb88f4646.js +1 -0
  148. mage_ai/server/{frontend_dist/_next/static/chunks/4241-ccd0126f5750cc35.js → frontend_dist_base_path_template/_next/static/chunks/4241-4499461184ba0d23.js} +1 -1
  149. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5627-237a3de578538022.js +1 -0
  150. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-6d708c6b2153ea08.js +1 -0
  151. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-8a23dd8360593e7a.js +1 -0
  152. mage_ai/server/{frontend_dist/_next/static/chunks/7966-5c1786fb7e7a48f5.js → frontend_dist_base_path_template/_next/static/chunks/7966-f07b2913f7326b50.js} +1 -1
  153. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-d9c89527266296f7.js +1 -0
  154. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/index-4e12783b064c1cfe.js +1 -0
  155. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/[user]-8bbfa0c19b5e4cb3.js +1 -0
  156. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-852d403c7bda21b3.js +1 -0
  157. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/overview-597b74828bf105db.js +1 -0
  158. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{pipeline-runs-a66b4c7641ae03eb.js → pipeline-runs-3edc6270c5b0e962.js} +1 -1
  159. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +1 -0
  160. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-e1dd1ed71d26c10d.js +1 -0
  161. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-1417ad1c821d720a.js +1 -0
  162. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-59aca25a5b1d3998.js +1 -0
  163. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-90abafc7ed61c582.js +1 -0
  164. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-1bdfda8edc9cf4a8.js +1 -0
  165. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-3591d035bb3bb2b8.js +1 -0
  166. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-503049734a8b082f.js +1 -0
  167. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-c2e9ef989c8bfa73.js +1 -0
  168. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-5b26eeda8aed8a7b.js +1 -0
  169. mage_ai/server/{frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-79a4cf66a623e667.js → frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-8b793b3b696a2cd3.js} +1 -1
  170. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{users-86814e581acaf5db.js → users-a4db8710f703c729.js} +1 -1
  171. mage_ai/server/{frontend_dist/_next/static/chunks/pages/sign-in-7d38b2f7c3e918a1.js → frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-09414a8b66fb6f06.js} +1 -1
  172. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/triggers-9cba3211434a8966.js +1 -0
  173. mage_ai/server/frontend_dist_base_path_template/block-layout.html +2 -2
  174. mage_ai/server/frontend_dist_base_path_template/compute.html +2 -2
  175. mage_ai/server/frontend_dist_base_path_template/files.html +2 -2
  176. mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +2 -2
  177. mage_ai/server/frontend_dist_base_path_template/global-data-products.html +2 -2
  178. mage_ai/server/frontend_dist_base_path_template/global-hooks/[...slug].html +2 -2
  179. mage_ai/server/frontend_dist_base_path_template/global-hooks.html +2 -2
  180. mage_ai/server/frontend_dist_base_path_template/index.html +2 -2
  181. mage_ai/server/frontend_dist_base_path_template/manage/files.html +2 -2
  182. mage_ai/server/frontend_dist_base_path_template/manage/settings.html +2 -2
  183. mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +2 -2
  184. mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +2 -2
  185. mage_ai/server/frontend_dist_base_path_template/manage/users.html +2 -2
  186. mage_ai/server/frontend_dist_base_path_template/manage.html +2 -2
  187. mage_ai/server/frontend_dist_base_path_template/oauth.html +3 -3
  188. mage_ai/server/frontend_dist_base_path_template/overview.html +2 -2
  189. mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +2 -2
  190. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +2 -2
  191. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +2 -2
  192. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/dashboard.html +2 -2
  193. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +2 -2
  194. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +2 -2
  195. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +2 -2
  196. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
  197. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +2 -2
  198. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +2 -2
  199. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +2 -2
  200. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +2 -2
  201. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +2 -2
  202. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +2 -2
  203. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +2 -2
  204. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +2 -2
  205. mage_ai/server/frontend_dist_base_path_template/pipelines.html +2 -2
  206. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks/[...slug].html +2 -2
  207. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks.html +2 -2
  208. mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +2 -2
  209. mage_ai/server/frontend_dist_base_path_template/settings/platform/preferences.html +2 -2
  210. mage_ai/server/frontend_dist_base_path_template/settings/platform/settings.html +2 -2
  211. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions/[...slug].html +2 -2
  212. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions.html +2 -2
  213. mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +2 -2
  214. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles/[...slug].html +2 -2
  215. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles.html +2 -2
  216. mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +2 -2
  217. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users/[...slug].html +2 -2
  218. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +2 -2
  219. mage_ai/server/frontend_dist_base_path_template/settings.html +2 -2
  220. mage_ai/server/frontend_dist_base_path_template/sign-in.html +5 -5
  221. mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +2 -2
  222. mage_ai/server/frontend_dist_base_path_template/templates.html +2 -2
  223. mage_ai/server/frontend_dist_base_path_template/terminal.html +2 -2
  224. mage_ai/server/frontend_dist_base_path_template/test.html +2 -2
  225. mage_ai/server/frontend_dist_base_path_template/triggers.html +2 -2
  226. mage_ai/server/frontend_dist_base_path_template/version-control.html +2 -2
  227. mage_ai/server/scheduler_manager.py +7 -0
  228. mage_ai/server/server.py +12 -5
  229. mage_ai/server/websocket_server.py +1 -0
  230. mage_ai/services/k8s/job_manager.py +8 -0
  231. mage_ai/services/slack/slack.py +10 -1
  232. mage_ai/services/spark/spark.py +9 -2
  233. mage_ai/settings/backends.py +8 -8
  234. mage_ai/settings/keys/auth.py +2 -0
  235. mage_ai/settings/models/configuration_option.py +10 -9
  236. mage_ai/settings/server.py +1 -1
  237. mage_ai/shared/files.py +19 -1
  238. mage_ai/shared/path_fixer.py +3 -0
  239. mage_ai/streaming/sources/base.py +5 -0
  240. mage_ai/streaming/sources/influxdb.py +2 -0
  241. mage_ai/streaming/sources/kafka.py +2 -1
  242. mage_ai/streaming/sources/mongodb.py +4 -0
  243. mage_ai/tests/api/endpoints/mixins.py +10 -9
  244. mage_ai/tests/api/endpoints/test_seeds.py +24 -0
  245. mage_ai/tests/api/operations/test_sessions.py +53 -2
  246. mage_ai/tests/authentication/providers/test_okta.py +43 -0
  247. mage_ai/tests/data_preparation/models/block/dbt/test_block.py +2 -2
  248. mage_ai/tests/data_preparation/models/block/dbt/test_block_sql.py +1 -1
  249. mage_ai/tests/data_preparation/models/block/dbt/test_block_yaml.py +1 -1
  250. mage_ai/tests/data_preparation/models/test_block.py +2 -1
  251. mage_ai/tests/orchestration/db/models/test_oauth.py +3 -3
  252. mage_ai/tests/orchestration/queue/test_process_queue.py +1 -0
  253. mage_ai/tests/orchestration/test_pipeline_scheduler.py +2 -0
  254. mage_ai/tests/server/test_server.py +8 -4
  255. mage_ai/tests/settings/models/test_configuration_option.py +2 -2
  256. {mage_ai-0.9.67.dist-info → mage_ai-0.9.69.dist-info}/METADATA +6 -6
  257. {mage_ai-0.9.67.dist-info → mage_ai-0.9.69.dist-info}/RECORD +263 -259
  258. mage_ai/server/frontend_dist/_next/static/chunks/1557-01f0843dc6ac4971.js +0 -1
  259. mage_ai/server/frontend_dist/_next/static/chunks/181-e61915415a976861.js +0 -1
  260. mage_ai/server/frontend_dist/_next/static/chunks/2717-b5f9575799b594d5.js +0 -1
  261. mage_ai/server/frontend_dist/_next/static/chunks/3548-13563a1ff815f922.js +0 -1
  262. mage_ai/server/frontend_dist/_next/static/chunks/5699-6efc749f2f8ddd20.js +0 -1
  263. mage_ai/server/frontend_dist/_next/static/chunks/7361-18d9d8be96e1ce97.js +0 -1
  264. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-1c1ffd928f5a00f7.js +0 -1
  265. mage_ai/server/frontend_dist/_next/static/chunks/pages/index-b7b8695a7f9efde2.js +0 -1
  266. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/[user]-d3a5fd3119fdb1e4.js +0 -1
  267. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-42789d698d28a92f.js +0 -1
  268. mage_ai/server/frontend_dist/_next/static/chunks/pages/overview-d05040edba41b2ac.js +0 -1
  269. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-aaf393c86fc1bda3.js +0 -1
  270. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-36377e679da2cd91.js +0 -1
  271. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-1b688d61f8efe07a.js +0 -1
  272. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-ed3331d22d5cff7d.js +0 -1
  273. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-d94e48bad89ba1e0.js +0 -1
  274. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-f508c2f261297724.js +0 -1
  275. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-f99e99aa8f45529c.js +0 -1
  276. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-6826000cdffc36b8.js +0 -1
  277. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/settings-74d76300942dcee8.js +0 -1
  278. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-dde29a463495cebb.js +0 -1
  279. mage_ai/server/frontend_dist/_next/static/chunks/pages/triggers-ab98a7b3a678669e.js +0 -1
  280. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-01f0843dc6ac4971.js +0 -1
  281. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/181-e61915415a976861.js +0 -1
  282. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-b5f9575799b594d5.js +0 -1
  283. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-13563a1ff815f922.js +0 -1
  284. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-6efc749f2f8ddd20.js +0 -1
  285. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-18d9d8be96e1ce97.js +0 -1
  286. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-1c1ffd928f5a00f7.js +0 -1
  287. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/index-b7b8695a7f9efde2.js +0 -1
  288. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/[user]-d3a5fd3119fdb1e4.js +0 -1
  289. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-42789d698d28a92f.js +0 -1
  290. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/overview-d05040edba41b2ac.js +0 -1
  291. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-aaf393c86fc1bda3.js +0 -1
  292. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-36377e679da2cd91.js +0 -1
  293. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-1b688d61f8efe07a.js +0 -1
  294. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-ed3331d22d5cff7d.js +0 -1
  295. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-d94e48bad89ba1e0.js +0 -1
  296. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-f508c2f261297724.js +0 -1
  297. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-f99e99aa8f45529c.js +0 -1
  298. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-6826000cdffc36b8.js +0 -1
  299. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-74d76300942dcee8.js +0 -1
  300. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-dde29a463495cebb.js +0 -1
  301. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/triggers-ab98a7b3a678669e.js +0 -1
  302. /mage_ai/server/frontend_dist/_next/static/{vPsMu6Fi2zrHaf2fRXKRO → _krrrgup_C-dPOpX36S8I}/_ssgManifest.js +0 -0
  303. /mage_ai/server/frontend_dist_base_path_template/_next/static/{khKiaJtwrslgMmp4YSa1f → KLL5mirre9d7_ZeEpaw3s}/_ssgManifest.js +0 -0
  304. {mage_ai-0.9.67.dist-info → mage_ai-0.9.69.dist-info}/LICENSE +0 -0
  305. {mage_ai-0.9.67.dist-info → mage_ai-0.9.69.dist-info}/WHEEL +0 -0
  306. {mage_ai-0.9.67.dist-info → mage_ai-0.9.69.dist-info}/entry_points.txt +0 -0
  307. {mage_ai-0.9.67.dist-info → mage_ai-0.9.69.dist-info}/top_level.txt +0 -0
@@ -2,9 +2,12 @@ import asyncio
2
2
  import copy
3
3
  import logging
4
4
  import os
5
+ import traceback
5
6
  from contextlib import redirect_stderr, redirect_stdout
7
+ from datetime import datetime
6
8
  from typing import Callable, Dict, List, Union
7
9
 
10
+ import pytz
8
11
  import yaml
9
12
  from jinja2 import Template
10
13
 
@@ -12,9 +15,14 @@ from mage_ai.data_preparation.executors.pipeline_executor import PipelineExecuto
12
15
  from mage_ai.data_preparation.logging.logger import DictLogger
13
16
  from mage_ai.data_preparation.models.constants import BlockLanguage, BlockType
14
17
  from mage_ai.data_preparation.models.pipeline import Pipeline
18
+ from mage_ai.data_preparation.shared.retry import RetryConfig
15
19
  from mage_ai.data_preparation.shared.stream import StreamToLogger
16
20
  from mage_ai.data_preparation.shared.utils import get_template_vars
21
+ from mage_ai.orchestration.db import safe_db_query
22
+ from mage_ai.orchestration.db.models.schedules import PipelineRun
17
23
  from mage_ai.shared.hash import merge_dict
24
+ from mage_ai.shared.retry import retry
25
+ from mage_ai.usage_statistics.logger import UsageStatisticLogger
18
26
 
19
27
 
20
28
  class StreamingPipelineExecutor(PipelineExecutor):
@@ -22,6 +30,7 @@ class StreamingPipelineExecutor(PipelineExecutor):
22
30
  super().__init__(pipeline, **kwargs)
23
31
  # TODO: Support custom log destination for streaming pipelines
24
32
  self.parse_and_validate_blocks()
33
+ self.retry_metadata = dict(attempts=0)
25
34
 
26
35
  def parse_and_validate_blocks(self):
27
36
  """
@@ -67,6 +76,8 @@ class StreamingPipelineExecutor(PipelineExecutor):
67
76
  self,
68
77
  build_block_output_stdout: Callable[..., object] = None,
69
78
  global_vars: Dict = None,
79
+ pipeline_run_id: int = None,
80
+ retry_config: Dict = None,
70
81
  **kwargs,
71
82
  ) -> None:
72
83
  # TODOs:
@@ -74,6 +85,7 @@ class StreamingPipelineExecutor(PipelineExecutor):
74
85
  # 2. Support flink pipeline
75
86
 
76
87
  tags = self.build_tags(**kwargs)
88
+ self.logging_tags = tags
77
89
  if build_block_output_stdout:
78
90
  stdout_logger = logging.getLogger('streaming_pipeline_executor')
79
91
  self.logger = DictLogger(stdout_logger)
@@ -82,24 +94,53 @@ class StreamingPipelineExecutor(PipelineExecutor):
82
94
  self.logger = DictLogger(self.logger_manager.logger, logging_tags=tags)
83
95
  stdout = StreamToLogger(self.logger, logging_tags=tags)
84
96
  try:
85
- with redirect_stdout(stdout):
86
- with redirect_stderr(stdout):
87
- self.__execute_in_python(
88
- build_block_output_stdout=build_block_output_stdout,
89
- global_vars=global_vars,
90
- )
97
+ if retry_config is None:
98
+ retry_config = self.pipeline.retry_config or dict()
99
+ infinite_retries = False if retry_config else False
100
+
101
+ if type(retry_config) is not RetryConfig:
102
+ retry_config = RetryConfig.load(config=retry_config)
103
+
104
+ @retry(
105
+ retries=retry_config.retries,
106
+ delay=retry_config.delay,
107
+ max_delay=retry_config.max_delay,
108
+ exponential_backoff=retry_config.exponential_backoff,
109
+ logger=self.logger,
110
+ logging_tags=self.logging_tags,
111
+ retry_metadata=self.retry_metadata,
112
+ )
113
+ def __execute_with_retry():
114
+ with redirect_stdout(stdout):
115
+ with redirect_stderr(stdout):
116
+ self.__execute_in_python(
117
+ build_block_output_stdout=build_block_output_stdout,
118
+ global_vars=global_vars,
119
+ pipeline_run_id=pipeline_run_id,
120
+ )
121
+ __execute_with_retry()
91
122
  except Exception as e:
92
123
  if not build_block_output_stdout:
93
124
  self.logger.exception(
94
125
  f'Failed to execute streaming pipeline {self.pipeline.uuid}',
95
126
  **merge_dict(dict(error=e), tags),
96
127
  )
128
+ if not infinite_retries:
129
+ # If pipeline retry config is present, fail the pipeline after the retries
130
+ self.__update_pipeline_run_status(
131
+ pipeline_run_id,
132
+ PipelineRun.PipelineRunStatus.FAILED,
133
+ error=e,
134
+ )
135
+
97
136
  raise e
98
137
 
99
138
  def __execute_in_python(
100
139
  self,
101
140
  build_block_output_stdout: Callable[..., object] = None,
102
- global_vars: Dict = None
141
+ global_vars: Dict = None,
142
+ pipeline_run_id: int = None,
143
+
103
144
  ):
104
145
  from mage_ai.streaming.sinks.sink_factory import SinkFactory
105
146
  from mage_ai.streaming.sources.base import SourceConsumeMethod
@@ -227,6 +268,35 @@ class StreamingPipelineExecutor(PipelineExecutor):
227
268
  for sink in sinks_by_uuid.values():
228
269
  sink.destroy()
229
270
 
271
+ @safe_db_query
272
+ def __update_pipeline_run_status(
273
+ self,
274
+ pipeline_run_id: int,
275
+ status: PipelineRun.PipelineRunStatus,
276
+ error: Exception = None,
277
+ ):
278
+ if not pipeline_run_id or not status:
279
+ return
280
+ pipeline_run = PipelineRun.query.get(pipeline_run_id)
281
+ pipeline_run.update(
282
+ status=status,
283
+ completed_at=datetime.now(tz=pytz.UTC),
284
+ )
285
+ if status == PipelineRun.PipelineRunStatus.FAILED:
286
+ asyncio.run(UsageStatisticLogger().pipeline_run_ended(pipeline_run))
287
+ error_msg = None
288
+ stacktrace = None
289
+ if error is not None:
290
+ error_msg = str(error)
291
+ stacktrace = traceback.format_exc()
292
+ notification_sender = self.pipeline.get_notification_sender()
293
+ notification_sender.send_pipeline_run_failure_message(
294
+ pipeline=self.pipeline,
295
+ pipeline_run=pipeline_run,
296
+ error=error_msg,
297
+ stacktrace=stacktrace,
298
+ )
299
+
230
300
  def __execute_in_flink(self):
231
301
  """
232
302
  TODO: Implement this method
@@ -10,9 +10,9 @@ from mage_ai.shared.config import BaseConfig
10
10
 
11
11
  @dataclass
12
12
  class GCSConfig(BaseConfig):
13
- path_to_credentials: str
14
13
  bucket: str
15
14
  prefix: str
15
+ path_to_credentials: str = None
16
16
 
17
17
 
18
18
  class GCSLoggerManager(LoggerManager):
@@ -23,9 +23,12 @@ class GCSLoggerManager(LoggerManager):
23
23
  ):
24
24
  super().__init__(repo_config=repo_config, **kwargs)
25
25
  self.gcs_config = GCSConfig.load(config=self.logging_config.destination_config)
26
- credentials = service_account.Credentials.from_service_account_file(
27
- self.gcs_config.path_to_credentials
28
- )
26
+ if self.gcs_config.path_to_credentials:
27
+ credentials = service_account.Credentials.from_service_account_file(
28
+ self.gcs_config.path_to_credentials
29
+ )
30
+ else:
31
+ credentials = None
29
32
  self.gcs_client = storage.Client(credentials=credentials)
30
33
 
31
34
  def create_log_filepath_dir(self, path):
@@ -90,7 +90,7 @@ from mage_ai.data_preparation.templates.data_integrations.utils import get_templ
90
90
  from mage_ai.data_preparation.templates.template import load_template
91
91
  from mage_ai.server.kernel_output_parser import DataType
92
92
  from mage_ai.services.spark.config import SparkConfig
93
- from mage_ai.services.spark.spark import get_spark_session
93
+ from mage_ai.services.spark.spark import SPARK_ENABLED, get_spark_session
94
94
  from mage_ai.settings.platform.constants import project_platform_activated
95
95
  from mage_ai.settings.repo import get_repo_path
96
96
  from mage_ai.shared.array import unique_by
@@ -723,41 +723,6 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
723
723
  widget=widget,
724
724
  )
725
725
 
726
- @classmethod
727
- def block_class_from_type(self, block_type: str, language=None, pipeline=None) -> 'Block':
728
- from mage_ai.data_preparation.models.block.constants import BLOCK_TYPE_TO_CLASS
729
- from mage_ai.data_preparation.models.block.integration import (
730
- DestinationBlock,
731
- SourceBlock,
732
- TransformerBlock,
733
- )
734
- from mage_ai.data_preparation.models.block.r import RBlock
735
- from mage_ai.data_preparation.models.block.sql import SQLBlock
736
- from mage_ai.data_preparation.models.widget import Widget
737
-
738
- if BlockType.CHART == block_type:
739
- return Widget
740
- elif BlockType.DBT == block_type:
741
- from mage_ai.data_preparation.models.block.dbt import DBTBlock
742
-
743
- return DBTBlock
744
- elif pipeline and PipelineType.INTEGRATION == pipeline.type:
745
- if BlockType.CALLBACK == block_type:
746
- return CallbackBlock
747
- elif BlockType.CONDITIONAL == block_type:
748
- return ConditionalBlock
749
- elif BlockType.DATA_LOADER == block_type:
750
- return SourceBlock
751
- elif BlockType.DATA_EXPORTER == block_type:
752
- return DestinationBlock
753
- else:
754
- return TransformerBlock
755
- elif BlockLanguage.SQL == language:
756
- return SQLBlock
757
- elif BlockLanguage.R == language:
758
- return RBlock
759
- return BLOCK_TYPE_TO_CLASS.get(block_type)
760
-
761
726
  @classmethod
762
727
  def create(
763
728
  self,
@@ -777,6 +742,8 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
777
742
  widget: bool = False,
778
743
  downstream_block_uuids: List[str] = None,
779
744
  ) -> 'Block':
745
+ from mage_ai.data_preparation.models.block.block_factory import BlockFactory
746
+
780
747
  """
781
748
  1. Create a new folder for block_type if not exist
782
749
  2. Create a new python file with code template
@@ -870,7 +837,11 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
870
837
  language,
871
838
  )
872
839
 
873
- block = self.block_class_from_type(block_type, pipeline=pipeline)(
840
+ block = BlockFactory.block_class_from_type(
841
+ block_type,
842
+ language=language,
843
+ pipeline=pipeline,
844
+ )(
874
845
  name,
875
846
  uuid,
876
847
  block_type,
@@ -931,43 +902,16 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
931
902
  block_uuids[t.value].append(f.split('.')[0])
932
903
  return block_uuids
933
904
 
934
- @classmethod
935
- def get_block(
936
- self,
937
- name,
938
- uuid,
939
- block_type,
940
- configuration=None,
941
- content=None,
942
- language=None,
943
- pipeline=None,
944
- status=BlockStatus.NOT_EXECUTED,
945
- ) -> 'Block':
946
- block_class = self.block_class_from_type(
947
- block_type,
948
- language=language,
949
- pipeline=pipeline,
950
- ) or Block
951
- return block_class(
952
- name,
953
- uuid,
954
- block_type,
955
- configuration=configuration,
956
- content=content,
957
- language=language,
958
- pipeline=pipeline,
959
- status=status,
960
- )
961
-
962
905
  @classmethod
963
906
  def get_block_from_file_path(self, file_path: str) -> 'Block':
964
907
  parts = get_path_parts(file_path)
965
908
 
966
909
  if parts and len(parts) >= 3:
910
+ from mage_ai.data_preparation.models.block.block_factory import BlockFactory
911
+
967
912
  # If file_path == transformers/test4.py
968
913
  # parts ==
969
914
  # ('/home/src/default_repo/default_platform2/project3', 'transformers', 'test4.py')
970
-
971
915
  # If project platform platform activated, then parts ==
972
916
  # ('/home/src', 'default_repo', 'data_loaders/astral_violet.py')
973
917
 
@@ -986,7 +930,7 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
986
930
  configuration = dict(file_path=file_path, file_source=dict(path=file_path))
987
931
  language = FILE_EXTENSION_TO_BLOCK_LANGUAGE.get(extension)
988
932
 
989
- return self.get_block(
933
+ return BlockFactory.get_block(
990
934
  block_uuid,
991
935
  block_uuid,
992
936
  block_type,
@@ -2445,8 +2389,10 @@ df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
2445
2389
  return Template(self.executor_type).render(**get_template_vars())
2446
2390
  return self.executor_type
2447
2391
 
2448
- def get_pipelines_from_cache(self) -> List[Dict]:
2449
- arr = BlockCache().get_pipelines(self)
2392
+ def get_pipelines_from_cache(self, block_cache: BlockCache = None) -> List[Dict]:
2393
+ if block_cache is None:
2394
+ block_cache = BlockCache()
2395
+ arr = block_cache.get_pipelines(self)
2450
2396
 
2451
2397
  return unique_by(
2452
2398
  arr,
@@ -2497,11 +2443,13 @@ df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
2497
2443
  def to_dict(
2498
2444
  self,
2499
2445
  include_block_catalog: bool = False,
2446
+ include_block_pipelines: bool = False,
2500
2447
  include_callback_blocks: bool = False,
2501
2448
  include_content: bool = False,
2502
2449
  include_outputs: bool = False,
2503
2450
  include_outputs_spark: bool = False,
2504
2451
  sample_count: int = None,
2452
+ block_cache: BlockCache = None,
2505
2453
  check_if_file_exists: bool = False,
2506
2454
  **kwargs,
2507
2455
  ) -> Dict:
@@ -2515,6 +2463,9 @@ df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
2515
2463
  if include_block_catalog and self.is_data_integration() and self.pipeline:
2516
2464
  data['catalog'] = self.get_catalog_from_file()
2517
2465
 
2466
+ if include_block_pipelines:
2467
+ data['pipelines'] = self.get_pipelines_from_cache(block_cache=block_cache)
2468
+
2518
2469
  if include_outputs:
2519
2470
  include_outputs_use = include_outputs
2520
2471
  if self.is_using_spark() and self.compute_management_enabled():
@@ -2551,6 +2502,7 @@ df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
2551
2502
  include_outputs: bool = False,
2552
2503
  include_outputs_spark: bool = False,
2553
2504
  sample_count: int = None,
2505
+ block_cache: BlockCache = None,
2554
2506
  check_if_file_exists: bool = False,
2555
2507
  **kwargs,
2556
2508
  ) -> Dict:
@@ -2596,7 +2548,7 @@ df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
2596
2548
  data['tags'] = self.tags()
2597
2549
 
2598
2550
  if include_block_pipelines:
2599
- data['pipelines'] = self.get_pipelines_from_cache()
2551
+ data['pipelines'] = self.get_pipelines_from_cache(block_cache=block_cache)
2600
2552
 
2601
2553
  return data
2602
2554
 
@@ -3093,6 +3045,8 @@ df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
3093
3045
  return global_vars
3094
3046
 
3095
3047
  def get_spark_session(self):
3048
+ if not SPARK_ENABLED:
3049
+ return None
3096
3050
  if self.spark_init and (not self.pipeline or
3097
3051
  not self.pipeline.spark_config):
3098
3052
  return self.spark
@@ -3520,12 +3474,15 @@ df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
3520
3474
 
3521
3475
  cache = BlockCache()
3522
3476
  if detach:
3477
+ from mage_ai.data_preparation.models.block.block_factory import (
3478
+ BlockFactory,
3479
+ )
3523
3480
  """"
3524
3481
  New block added to pipeline, so it must be added to the block cache.
3525
3482
  Old block no longer in pipeline, so it must be removed from block cache.
3526
3483
  """
3527
3484
  cache.add_pipeline(self, self.pipeline)
3528
- old_block = self.get_block(
3485
+ old_block = BlockFactory.get_block(
3529
3486
  old_uuid,
3530
3487
  old_uuid,
3531
3488
  self.type,
@@ -0,0 +1,77 @@
1
+ from mage_ai.data_preparation.models.block import Block, CallbackBlock, ConditionalBlock
2
+ from mage_ai.data_preparation.models.block.constants import BLOCK_TYPE_TO_CLASS
3
+ from mage_ai.data_preparation.models.block.integration import (
4
+ DestinationBlock,
5
+ SourceBlock,
6
+ TransformerBlock,
7
+ )
8
+ from mage_ai.data_preparation.models.block.r import RBlock
9
+ from mage_ai.data_preparation.models.block.sql import SQLBlock
10
+ from mage_ai.data_preparation.models.constants import (
11
+ BlockLanguage,
12
+ BlockStatus,
13
+ BlockType,
14
+ PipelineType,
15
+ )
16
+ from mage_ai.data_preparation.models.widget import Widget
17
+
18
+ try:
19
+ from mage_ai.data_preparation.models.block.dbt.block_sql import DBTBlockSQL
20
+ from mage_ai.data_preparation.models.block.dbt.block_yaml import DBTBlockYAML
21
+ except Exception:
22
+ print('DBT library not installed.')
23
+
24
+
25
+ class BlockFactory:
26
+ @classmethod
27
+ def block_class_from_type(self, block_type: str, language=None, pipeline=None) -> 'Block':
28
+ if BlockType.CHART == block_type:
29
+ return Widget
30
+ elif BlockType.DBT == block_type:
31
+ if language == BlockLanguage.YAML:
32
+ return DBTBlockYAML
33
+ return DBTBlockSQL
34
+ elif pipeline and PipelineType.INTEGRATION == pipeline.type:
35
+ if BlockType.CALLBACK == block_type:
36
+ return CallbackBlock
37
+ elif BlockType.CONDITIONAL == block_type:
38
+ return ConditionalBlock
39
+ elif BlockType.DATA_LOADER == block_type:
40
+ return SourceBlock
41
+ elif BlockType.DATA_EXPORTER == block_type:
42
+ return DestinationBlock
43
+ else:
44
+ return TransformerBlock
45
+ elif BlockLanguage.SQL == language:
46
+ return SQLBlock
47
+ elif BlockLanguage.R == language:
48
+ return RBlock
49
+ return BLOCK_TYPE_TO_CLASS.get(block_type)
50
+
51
+ @classmethod
52
+ def get_block(
53
+ self,
54
+ name,
55
+ uuid,
56
+ block_type,
57
+ configuration=None,
58
+ content=None,
59
+ language=None,
60
+ pipeline=None,
61
+ status=BlockStatus.NOT_EXECUTED,
62
+ ) -> 'Block':
63
+ block_class = BlockFactory.block_class_from_type(
64
+ block_type,
65
+ language=language,
66
+ pipeline=pipeline,
67
+ ) or Block
68
+ return block_class(
69
+ name,
70
+ uuid,
71
+ block_type,
72
+ configuration=configuration,
73
+ content=content,
74
+ language=language,
75
+ pipeline=pipeline,
76
+ status=status,
77
+ )
@@ -202,18 +202,29 @@ class DataIntegrationMixin:
202
202
  with open(catalog_full_path, mode='w') as f:
203
203
  f.write(json.dumps(catalog))
204
204
 
205
- def is_data_integration(self) -> bool:
205
+ def is_data_integration(self, pipeline_project: Project = None) -> bool:
206
206
  """
207
207
  Check if the block is a data integration block.
208
208
  If the data_integration_in_batch_pipeline feature is not enabled, return False.
209
209
 
210
+ Args:
211
+ pipeline_project (Project, optional): A cached Project value to avoid
212
+ looking it up many times when called inside loops. Defaults to None.
213
+
210
214
  Returns:
211
215
  bool: True if it's a data integration block, False otherwise.
212
216
  """
213
- if not self.pipeline or not \
214
- Project(self.pipeline.repo_config).is_feature_enabled(
215
- FeatureUUID.DATA_INTEGRATION_IN_BATCH_PIPELINE,
216
- ):
217
+ if not self.pipeline:
218
+
219
+ return False
220
+
221
+ actual_project: Project = pipeline_project
222
+ if not actual_project:
223
+ actual_project = Project(self.pipeline.repo_config)
224
+
225
+ if not actual_project.is_feature_enabled(
226
+ FeatureUUID.DATA_INTEGRATION_IN_BATCH_PIPELINE,
227
+ ):
217
228
 
218
229
  return False
219
230
 
@@ -26,19 +26,17 @@ from mage_ai.shared.parsers import encode_complex
26
26
 
27
27
 
28
28
  class DBTBlock(Block):
29
- def __new__(cls, *args, **kwargs) -> 'DBTBlock':
29
+ @classmethod
30
+ def create(cls, *args, **kwargs) -> 'DBTBlock':
30
31
  """
31
32
  Factory for the child blocks
32
33
  """
33
34
  # Import Child blocks here to prevent cycle import
34
35
  from mage_ai.data_preparation.models.block.dbt.block_sql import DBTBlockSQL
35
36
  from mage_ai.data_preparation.models.block.dbt.block_yaml import DBTBlockYAML
36
- if cls is DBTBlock:
37
- if kwargs.get('language', BlockLanguage.SQL) == BlockLanguage.YAML:
38
- return super(DBTBlock, cls).__new__(DBTBlockYAML)
39
- return super(DBTBlock, cls).__new__(DBTBlockSQL)
40
- else:
41
- return super(DBTBlock, cls).__new__(cls, *args, **kwargs)
37
+ if kwargs.get('language', BlockLanguage.SQL) == BlockLanguage.YAML:
38
+ return DBTBlockYAML(*args, **kwargs)
39
+ return DBTBlockSQL(*args, **kwargs)
42
40
 
43
41
  @property
44
42
  def base_project_path(self) -> Union[str, os.PathLike]:
@@ -229,3 +229,6 @@ class DynamicChildController:
229
229
  block_runs.append(block_run)
230
230
 
231
231
  return block_runs
232
+
233
+ def run_tests(self, **kwargs):
234
+ pass
@@ -6,7 +6,7 @@ from typing import Any, Dict, List, Tuple, Union
6
6
 
7
7
  import pandas as pd
8
8
 
9
- from mage_ai.data_preparation.models.constants import BlockLanguage
9
+ from mage_ai.data_preparation.models.constants import BlockLanguage, BlockType
10
10
  from mage_ai.data_preparation.models.variable import Variable
11
11
  from mage_ai.shared.memory import get_memory_usage, get_memory_usage_async
12
12
  from mage_ai.shared.strings import to_ordinal_integers
@@ -470,7 +470,7 @@ def fetch_input_variables_for_dynamic_upstream_blocks(
470
470
 
471
471
  # If dynamic child should reduce its output (which means it passes the entire
472
472
  # output to its downstream blocks):
473
- if should_reduce_output(upstream_block):
473
+ if should_reduce_output(upstream_block) and block.type != BlockType.EXTENSION:
474
474
  child_data = []
475
475
  metadata = {}
476
476
  for lazy_variable_set in lazy_variable_controller:
@@ -45,4 +45,5 @@ def handle_run_tests(
45
45
  global_vars=global_vars,
46
46
  logger=logger,
47
47
  logging_tags=logging_tags,
48
+ update_status=False,
48
49
  )
@@ -2,12 +2,15 @@ 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.global_data_product import GlobalDataProduct
6
- from mage_ai.orchestration.triggers import global_data_product as trigger
7
5
 
8
6
 
9
7
  class GlobalDataProductBlock(Block):
10
- def get_global_data_product(self) -> GlobalDataProduct:
8
+ def get_global_data_product(self):
9
+ # Avoid circular dependency of Pipeline class
10
+ from mage_ai.data_preparation.models.global_data_product import (
11
+ GlobalDataProduct,
12
+ )
13
+
11
14
  override_configuration = (self.configuration or {}).get('global_data_product', {})
12
15
  global_data_product = GlobalDataProduct.get(override_configuration.get('uuid'))
13
16
 
@@ -31,6 +34,9 @@ class GlobalDataProductBlock(Block):
31
34
  logging_tags: Dict = None,
32
35
  **kwargs,
33
36
  ) -> List:
37
+ # Avoid circular dependency of Pipeline class
38
+ from mage_ai.orchestration.triggers import global_data_product as trigger
39
+
34
40
  trigger.trigger_and_check_status(
35
41
  self.get_global_data_product(),
36
42
  block=self,
@@ -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,
@@ -416,9 +416,10 @@ class SourceBlock(IntegrationBlock):
416
416
  class DestinationBlock(IntegrationBlock):
417
417
  def to_dict(
418
418
  self,
419
- include_content=False,
420
- include_outputs=False,
421
- sample_count=None,
419
+ include_content: bool = False,
420
+ include_outputs: bool = False,
421
+ include_block_pipelines: bool = False,
422
+ sample_count: int = None,
422
423
  check_if_file_exists: bool = False,
423
424
  destination_table: str = None,
424
425
  state_stream: str = None,
@@ -444,6 +445,7 @@ class DestinationBlock(IntegrationBlock):
444
445
  super().to_dict(
445
446
  include_content=include_content,
446
447
  include_outputs=include_outputs,
448
+ include_block_pipelines=include_block_pipelines,
447
449
  sample_count=sample_count,
448
450
  check_if_file_exists=check_if_file_exists,
449
451
  ),
@@ -452,9 +454,10 @@ class DestinationBlock(IntegrationBlock):
452
454
 
453
455
  async def to_dict_async(
454
456
  self,
455
- include_content=False,
456
- include_outputs=False,
457
- sample_count=None,
457
+ include_content: bool = False,
458
+ include_outputs: bool = False,
459
+ include_block_pipelines: bool = False,
460
+ sample_count: int = None,
458
461
  check_if_file_exists: bool = False,
459
462
  destination_table: str = None,
460
463
  state_stream: str = None,
@@ -463,13 +466,14 @@ class DestinationBlock(IntegrationBlock):
463
466
  return self.to_dict(
464
467
  include_content=include_content,
465
468
  include_outputs=include_outputs,
469
+ include_block_pipelines=include_block_pipelines,
466
470
  sample_count=sample_count,
467
471
  check_if_file_exists=check_if_file_exists,
468
472
  destination_table=destination_table,
469
473
  state_stream=state_stream,
470
474
  )
471
475
 
472
- def update(self, data, update_state=False):
476
+ def update(self, data, update_state=False, **kwargs):
473
477
  if update_state:
474
478
  from mage_ai.data_preparation.models.pipelines.integration_pipeline import (
475
479
  IntegrationPipeline,
@@ -493,7 +497,7 @@ class DestinationBlock(IntegrationBlock):
493
497
  bookmark_values=bookmark_values
494
498
  )
495
499
 
496
- return super().update(data)
500
+ return super().update(data, **kwargs)
497
501
 
498
502
  def output_variables(self, execution_partition: str = None) -> List[str]:
499
503
  return []
@@ -233,7 +233,7 @@ class ProjectPlatformAccessible:
233
233
  ),
234
234
  )
235
235
 
236
- return block_class(
236
+ return block_class.create(
237
237
  name,
238
238
  uuid,
239
239
  block_type,
@@ -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
  ]