mage-ai 0.9.69__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 (243) 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/WorkspaceResource.py +5 -4
  13. mage_ai/cache/block_action_object/__init__.py +1 -1
  14. mage_ai/cluster_manager/kubernetes/workload_manager.py +52 -1
  15. mage_ai/cluster_manager/workspace/base.py +6 -0
  16. mage_ai/cluster_manager/workspace/kubernetes.py +22 -1
  17. mage_ai/command_center/applications/utils.py +2 -2
  18. mage_ai/command_center/presenters/text.py +1 -1
  19. mage_ai/data_preparation/executors/k8s_block_executor.py +30 -7
  20. mage_ai/data_preparation/executors/k8s_pipeline_executor.py +30 -7
  21. mage_ai/data_preparation/executors/streaming_pipeline_executor.py +1 -1
  22. mage_ai/data_preparation/git/__init__.py +50 -22
  23. mage_ai/data_preparation/git/api.py +62 -7
  24. mage_ai/data_preparation/git/utils.py +45 -21
  25. mage_ai/data_preparation/models/block/__init__.py +24 -5
  26. mage_ai/data_preparation/models/block/dynamic/utils.py +9 -4
  27. mage_ai/data_preparation/models/block/global_data_product/__init__.py +25 -2
  28. mage_ai/data_preparation/models/block/remote/__init__.py +0 -0
  29. mage_ai/data_preparation/models/block/remote/models.py +58 -0
  30. mage_ai/data_preparation/models/block/utils.py +38 -0
  31. mage_ai/data_preparation/models/constants.py +2 -0
  32. mage_ai/data_preparation/models/global_data_product/__init__.py +12 -0
  33. mage_ai/data_preparation/models/pipeline.py +9 -5
  34. mage_ai/data_preparation/models/triggers/__init__.py +4 -2
  35. mage_ai/data_preparation/storage/local_storage.py +12 -6
  36. mage_ai/orchestration/db/migrations/versions/42a14d6143f1_update_token_column_type.py +54 -0
  37. mage_ai/orchestration/db/models/oauth.py +10 -9
  38. mage_ai/orchestration/db/models/schedules.py +21 -0
  39. mage_ai/orchestration/notification/sender.py +37 -15
  40. mage_ai/orchestration/pipeline_scheduler_original.py +32 -25
  41. mage_ai/orchestration/triggers/api.py +29 -1
  42. mage_ai/orchestration/triggers/global_data_product.py +9 -4
  43. mage_ai/orchestration/triggers/utils.py +10 -1
  44. mage_ai/orchestration/utils/resources.py +3 -0
  45. mage_ai/server/api/downloads.py +4 -1
  46. mage_ai/server/api/runs.py +151 -0
  47. mage_ai/server/constants.py +1 -1
  48. mage_ai/server/frontend_dist/404.html +6 -6
  49. mage_ai/server/frontend_dist/_next/static/{_krrrgup_C-dPOpX36S8I → RhDiJSkcjCsh4xxX4BFBk}/_buildManifest.js +1 -1
  50. mage_ai/server/frontend_dist/_next/static/chunks/1557-b3502f3f1aa92ac7.js +1 -0
  51. mage_ai/server/frontend_dist/_next/static/chunks/{3548-fa0792ddb88f4646.js → 3548-9d26185b3fb663b1.js} +1 -1
  52. mage_ai/server/frontend_dist/_next/static/chunks/7966-b9b85ba10667e654.js +1 -0
  53. mage_ai/server/frontend_dist/_next/static/chunks/9624-8b8e100079ab69e1.js +1 -0
  54. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-2a69553d8c6eeb53.js +1 -0
  55. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-4bfc84ff07d7656f.js +1 -0
  56. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-3edc6270c5b0e962.js → frontend_dist/_next/static/chunks/pages/pipeline-runs-6d183f91a2ff6668.js} +1 -1
  57. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-7181b086c93784d2.js +1 -0
  58. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-38e1fbcfbfc1014e.js +1 -0
  59. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-b645a6d13ab9fe3a.js +1 -0
  60. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-eb11c5390c982b49.js +1 -0
  61. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-1bdfda8edc9cf4a8.js → triggers-4612d15a65c35912.js} +1 -1
  62. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-32985f3f7c7dd3ab.js +1 -0
  63. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-349af617d05f001b.js +1 -0
  64. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-60d01d3887e31136.js +1 -0
  65. 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
  66. mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-3433c8b22e8342aa.js +1 -0
  67. mage_ai/server/frontend_dist/block-layout.html +2 -2
  68. mage_ai/server/frontend_dist/compute.html +2 -2
  69. mage_ai/server/frontend_dist/files.html +2 -2
  70. mage_ai/server/frontend_dist/global-data-products/[...slug].html +2 -2
  71. mage_ai/server/frontend_dist/global-data-products.html +2 -2
  72. mage_ai/server/frontend_dist/global-hooks/[...slug].html +2 -2
  73. mage_ai/server/frontend_dist/global-hooks.html +2 -2
  74. mage_ai/server/frontend_dist/index.html +2 -2
  75. mage_ai/server/frontend_dist/manage/files.html +2 -2
  76. mage_ai/server/frontend_dist/manage/settings.html +2 -2
  77. mage_ai/server/frontend_dist/manage/users/[user].html +2 -2
  78. mage_ai/server/frontend_dist/manage/users/new.html +2 -2
  79. mage_ai/server/frontend_dist/manage/users.html +2 -2
  80. mage_ai/server/frontend_dist/manage.html +2 -2
  81. mage_ai/server/frontend_dist/oauth.html +2 -2
  82. mage_ai/server/frontend_dist/overview.html +2 -2
  83. mage_ai/server/frontend_dist/pipeline-runs.html +2 -2
  84. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +2 -2
  85. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +2 -2
  86. mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +2 -2
  87. mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +2 -2
  88. mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +2 -2
  89. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +2 -2
  90. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
  91. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +2 -2
  92. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +2 -2
  93. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +2 -2
  94. mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +2 -2
  95. mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +2 -2
  96. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +2 -2
  97. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +2 -2
  98. mage_ai/server/frontend_dist/pipelines/[pipeline].html +2 -2
  99. mage_ai/server/frontend_dist/pipelines.html +2 -2
  100. mage_ai/server/frontend_dist/platform/global-hooks/[...slug].html +2 -2
  101. mage_ai/server/frontend_dist/platform/global-hooks.html +2 -2
  102. mage_ai/server/frontend_dist/settings/account/profile.html +2 -2
  103. mage_ai/server/frontend_dist/settings/platform/preferences.html +2 -2
  104. mage_ai/server/frontend_dist/settings/platform/settings.html +2 -2
  105. mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +2 -2
  106. mage_ai/server/frontend_dist/settings/workspace/permissions.html +2 -2
  107. mage_ai/server/frontend_dist/settings/workspace/preferences.html +2 -2
  108. mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +2 -2
  109. mage_ai/server/frontend_dist/settings/workspace/roles.html +2 -2
  110. mage_ai/server/frontend_dist/settings/workspace/sync-data.html +2 -2
  111. mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +2 -2
  112. mage_ai/server/frontend_dist/settings/workspace/users.html +2 -2
  113. mage_ai/server/frontend_dist/settings.html +2 -2
  114. mage_ai/server/frontend_dist/sign-in.html +6 -6
  115. mage_ai/server/frontend_dist/templates/[...slug].html +2 -2
  116. mage_ai/server/frontend_dist/templates.html +2 -2
  117. mage_ai/server/frontend_dist/terminal.html +2 -2
  118. mage_ai/server/frontend_dist/test.html +2 -2
  119. mage_ai/server/frontend_dist/triggers.html +2 -2
  120. mage_ai/server/frontend_dist/version-control.html +2 -2
  121. mage_ai/server/frontend_dist_base_path_template/404.html +6 -6
  122. mage_ai/server/frontend_dist_base_path_template/_next/static/{KLL5mirre9d7_ZeEpaw3s → TdpLLFome13qvM0gXvpHs}/_buildManifest.js +1 -1
  123. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-b3502f3f1aa92ac7.js +1 -0
  124. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{3548-fa0792ddb88f4646.js → 3548-9d26185b3fb663b1.js} +1 -1
  125. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7966-b9b85ba10667e654.js +1 -0
  126. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-8b8e100079ab69e1.js +1 -0
  127. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-2a69553d8c6eeb53.js +1 -0
  128. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-4bfc84ff07d7656f.js +1 -0
  129. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipeline-runs-3edc6270c5b0e962.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-6d183f91a2ff6668.js} +1 -1
  130. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-7181b086c93784d2.js +1 -0
  131. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-38e1fbcfbfc1014e.js +1 -0
  132. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-b645a6d13ab9fe3a.js +1 -0
  133. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-eb11c5390c982b49.js +1 -0
  134. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-1bdfda8edc9cf4a8.js → triggers-4612d15a65c35912.js} +1 -1
  135. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-32985f3f7c7dd3ab.js +1 -0
  136. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-349af617d05f001b.js +1 -0
  137. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-60d01d3887e31136.js +1 -0
  138. 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
  139. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-3433c8b22e8342aa.js +1 -0
  140. mage_ai/server/frontend_dist_base_path_template/block-layout.html +2 -2
  141. mage_ai/server/frontend_dist_base_path_template/compute.html +2 -2
  142. mage_ai/server/frontend_dist_base_path_template/files.html +2 -2
  143. mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +2 -2
  144. mage_ai/server/frontend_dist_base_path_template/global-data-products.html +2 -2
  145. mage_ai/server/frontend_dist_base_path_template/global-hooks/[...slug].html +2 -2
  146. mage_ai/server/frontend_dist_base_path_template/global-hooks.html +2 -2
  147. mage_ai/server/frontend_dist_base_path_template/index.html +2 -2
  148. mage_ai/server/frontend_dist_base_path_template/manage/files.html +2 -2
  149. mage_ai/server/frontend_dist_base_path_template/manage/settings.html +2 -2
  150. mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +2 -2
  151. mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +2 -2
  152. mage_ai/server/frontend_dist_base_path_template/manage/users.html +2 -2
  153. mage_ai/server/frontend_dist_base_path_template/manage.html +2 -2
  154. mage_ai/server/frontend_dist_base_path_template/oauth.html +2 -2
  155. mage_ai/server/frontend_dist_base_path_template/overview.html +2 -2
  156. mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +2 -2
  157. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +2 -2
  158. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +2 -2
  159. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/dashboard.html +2 -2
  160. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +2 -2
  161. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +2 -2
  162. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +2 -2
  163. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
  164. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +2 -2
  165. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +2 -2
  166. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +2 -2
  167. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +2 -2
  168. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +2 -2
  169. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +2 -2
  170. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +2 -2
  171. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +2 -2
  172. mage_ai/server/frontend_dist_base_path_template/pipelines.html +2 -2
  173. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks/[...slug].html +2 -2
  174. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks.html +2 -2
  175. mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +2 -2
  176. mage_ai/server/frontend_dist_base_path_template/settings/platform/preferences.html +2 -2
  177. mage_ai/server/frontend_dist_base_path_template/settings/platform/settings.html +2 -2
  178. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions/[...slug].html +2 -2
  179. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions.html +2 -2
  180. mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +2 -2
  181. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles/[...slug].html +2 -2
  182. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles.html +2 -2
  183. mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +2 -2
  184. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users/[...slug].html +2 -2
  185. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +2 -2
  186. mage_ai/server/frontend_dist_base_path_template/settings.html +2 -2
  187. mage_ai/server/frontend_dist_base_path_template/sign-in.html +6 -6
  188. mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +2 -2
  189. mage_ai/server/frontend_dist_base_path_template/templates.html +2 -2
  190. mage_ai/server/frontend_dist_base_path_template/terminal.html +2 -2
  191. mage_ai/server/frontend_dist_base_path_template/test.html +2 -2
  192. mage_ai/server/frontend_dist_base_path_template/triggers.html +2 -2
  193. mage_ai/server/frontend_dist_base_path_template/version-control.html +2 -2
  194. mage_ai/server/scheduler_manager.py +3 -1
  195. mage_ai/server/server.py +35 -12
  196. mage_ai/server/utils/output_display.py +2 -2
  197. mage_ai/services/aws/ecs/ecs.py +1 -0
  198. mage_ai/services/k8s/config.py +4 -4
  199. mage_ai/services/k8s/utils.py +97 -0
  200. mage_ai/shared/parsers.py +6 -1
  201. mage_ai/tests/api/operations/base/mixins.py +1 -1
  202. mage_ai/tests/api/resources/test_pipeline_resource.py +2 -2
  203. mage_ai/tests/authentication/oauth/test_utils.py +1 -1
  204. mage_ai/tests/data_preparation/models/block/test_global_data_product.py +2 -0
  205. mage_ai/tests/orchestration/triggers/test_global_data_product.py +138 -136
  206. mage_ai/tests/server/test_server.py +19 -0
  207. mage_ai/tests/services/k8s/test_job_manager.py +9 -6
  208. mage_ai/version_control/branch/utils.py +2 -1
  209. mage_ai/version_control/models.py +3 -2
  210. {mage_ai-0.9.69.dist-info → mage_ai-0.9.70.dist-info}/METADATA +2 -2
  211. {mage_ai-0.9.69.dist-info → mage_ai-0.9.70.dist-info}/RECORD +217 -212
  212. mage_ai/server/frontend_dist/_next/static/chunks/1557-df144fbd8b2208c3.js +0 -1
  213. mage_ai/server/frontend_dist/_next/static/chunks/7966-f07b2913f7326b50.js +0 -1
  214. mage_ai/server/frontend_dist/_next/static/chunks/9624-59b2f803f9c88cd6.js +0 -1
  215. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-d9c89527266296f7.js +0 -1
  216. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-852d403c7bda21b3.js +0 -1
  217. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +0 -1
  218. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +0 -1
  219. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-e1dd1ed71d26c10d.js +0 -1
  220. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-f028ef3880ed856c.js +0 -1
  221. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-503049734a8b082f.js +0 -1
  222. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-5b26eeda8aed8a7b.js +0 -1
  223. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-8b793b3b696a2cd3.js +0 -1
  224. mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +0 -1
  225. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-df144fbd8b2208c3.js +0 -1
  226. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7966-f07b2913f7326b50.js +0 -1
  227. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-59b2f803f9c88cd6.js +0 -1
  228. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-d9c89527266296f7.js +0 -1
  229. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-852d403c7bda21b3.js +0 -1
  230. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +0 -1
  231. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +0 -1
  232. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-e1dd1ed71d26c10d.js +0 -1
  233. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-f028ef3880ed856c.js +0 -1
  234. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-503049734a8b082f.js +0 -1
  235. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-5b26eeda8aed8a7b.js +0 -1
  236. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-8b793b3b696a2cd3.js +0 -1
  237. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +0 -1
  238. /mage_ai/server/frontend_dist/_next/static/{_krrrgup_C-dPOpX36S8I → RhDiJSkcjCsh4xxX4BFBk}/_ssgManifest.js +0 -0
  239. /mage_ai/server/frontend_dist_base_path_template/_next/static/{KLL5mirre9d7_ZeEpaw3s → TdpLLFome13qvM0gXvpHs}/_ssgManifest.js +0 -0
  240. {mage_ai-0.9.69.dist-info → mage_ai-0.9.70.dist-info}/LICENSE +0 -0
  241. {mage_ai-0.9.69.dist-info → mage_ai-0.9.70.dist-info}/WHEEL +0 -0
  242. {mage_ai-0.9.69.dist-info → mage_ai-0.9.70.dist-info}/entry_points.txt +0 -0
  243. {mage_ai-0.9.69.dist-info → mage_ai-0.9.70.dist-info}/top_level.txt +0 -0
@@ -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
@@ -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
@@ -99,6 +99,7 @@ class Pipeline:
99
99
  self.description = description
100
100
  self.executor_config = dict()
101
101
  self.executor_type = None
102
+ self._rendered_executor_type = None # Render template variables
102
103
  self.extensions = {}
103
104
  self.name = None
104
105
  self.notification_config = dict()
@@ -1768,10 +1769,13 @@ class Pipeline:
1768
1769
  def get_executable_blocks(self):
1769
1770
  return [b for b in self.blocks_by_uuid.values() if b.executable]
1770
1771
 
1771
- def get_executor_type(self) -> str:
1772
- if self.executor_type:
1773
- return Template(self.executor_type).render(**get_template_vars())
1774
- 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
1775
1779
 
1776
1780
  def has_block(self, block_uuid: str, block_type: str = None, extension_uuid: str = None):
1777
1781
  if extension_uuid:
@@ -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)
@@ -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:
@@ -0,0 +1,54 @@
1
+ """Update token column type
2
+
3
+ Revision ID: 42a14d6143f1
4
+ Revises: b9a2d6d0a2c7
5
+ Create Date: 2024-04-12 15:19:52.639580
6
+
7
+ """
8
+ from alembic import op
9
+ import sqlalchemy as sa
10
+
11
+
12
+ # revision identifiers, used by Alembic.
13
+ revision = '42a14d6143f1'
14
+ down_revision = 'b9a2d6d0a2c7'
15
+ branch_labels = None
16
+ depends_on = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ # ### commands auto generated by Alembic - please adjust! ###
21
+ with op.batch_alter_table('oauth2_access_token', schema=None) as batch_op:
22
+ batch_op.alter_column(
23
+ 'token',
24
+ existing_type=sa.String(length=255),
25
+ type_=sa.Text(),
26
+ existing_nullable=True,
27
+ )
28
+ batch_op.alter_column(
29
+ 'refresh_token',
30
+ existing_type=sa.String(length=255),
31
+ type_=sa.Text(),
32
+ existing_nullable=True,
33
+ )
34
+
35
+ # ### end Alembic commands ###
36
+
37
+
38
+ def downgrade() -> None:
39
+ # ### commands auto generated by Alembic - please adjust! ###
40
+ with op.batch_alter_table('oauth2_access_token', schema=None) as batch_op:
41
+ batch_op.alter_column(
42
+ 'token',
43
+ existing_type=sa.Text(),
44
+ type_=sa.String(length=255),
45
+ existing_nullable=True,
46
+ )
47
+ batch_op.alter_column(
48
+ 'refresh_token',
49
+ existing_type=sa.String(length=255),
50
+ type_=sa.Text(),
51
+ existing_nullable=True,
52
+ )
53
+
54
+ # ### end Alembic commands ###
@@ -12,6 +12,7 @@ from sqlalchemy import (
12
12
  ForeignKey,
13
13
  Integer,
14
14
  String,
15
+ Text,
15
16
  and_,
16
17
  asc,
17
18
  func,
@@ -324,7 +325,7 @@ class Role(BaseModel):
324
325
  @classmethod
325
326
  @safe_db_query
326
327
  def create_default_roles(
327
- self,
328
+ cls,
328
329
  entity: Entity = None,
329
330
  entity_id: str = None,
330
331
  name_func: Callable[[str], str] = None,
@@ -342,18 +343,18 @@ class Role(BaseModel):
342
343
  entity = Entity.GLOBAL
343
344
  permissions = Permission.create_default_permissions(entity=entity, entity_id=entity_id)
344
345
  mapping = {
345
- self.DefaultRole.OWNER: Permission.Access.OWNER,
346
- self.DefaultRole.ADMIN: Permission.Access.ADMIN,
347
- self.DefaultRole.EDITOR: Permission.Access.EDITOR,
348
- self.DefaultRole.VIEWER: Permission.Access.VIEWER,
346
+ cls.DefaultRole.OWNER: Permission.Access.OWNER,
347
+ cls.DefaultRole.ADMIN: Permission.Access.ADMIN,
348
+ cls.DefaultRole.EDITOR: Permission.Access.EDITOR,
349
+ cls.DefaultRole.VIEWER: Permission.Access.VIEWER,
349
350
  }
350
351
  for name, access in mapping.items():
351
352
  role_name = name
352
353
  if name_func is not None:
353
354
  role_name = name_func(name)
354
- role = self.query.filter(self.name == role_name).first()
355
+ role = cls.query.filter(Role.name == role_name).first()
355
356
  if not role:
356
- self.create(
357
+ cls.create(
357
358
  name=role_name,
358
359
  permissions=[
359
360
  Permission.query.filter(
@@ -880,10 +881,10 @@ class Oauth2AccessToken(BaseModel):
880
881
  expires = Column(DateTime(timezone=True))
881
882
  oauth2_application = relationship(Oauth2Application, back_populates='oauth2_access_tokens')
882
883
  oauth2_application_id = Column(Integer, ForeignKey('oauth2_application.id'))
883
- token = Column(String(255), index=True, unique=True)
884
+ token = Column(Text, index=True, unique=True)
884
885
  user = relationship(User, back_populates='oauth2_access_tokens')
885
886
  user_id = Column(Integer, ForeignKey('user.id'))
886
- refresh_token = Column(String(255))
887
+ refresh_token = Column(Text)
887
888
 
888
889
  def is_valid(self) -> bool:
889
890
  return self.token and \
@@ -1913,3 +1913,24 @@ class Backfill(BaseModel):
1913
1913
  Backfill.pipeline_schedule_id.in_(pipeline_schedule_ids),
1914
1914
  )
1915
1915
  return []
1916
+
1917
+ @property
1918
+ def pipeline_run_status_counts(self) -> Dict:
1919
+ status_counts = dict()
1920
+ execution_dates_counted = set()
1921
+
1922
+ # Sort the pipeline runs by id in reverse order so the first pipeline run
1923
+ # checked is the latest pipeline run created for a given execution date.
1924
+ pipeline_runs_sorted = sorted(
1925
+ self.pipeline_runs,
1926
+ key=lambda pr: (pr.execution_date, pr.id),
1927
+ reverse=True,
1928
+ )
1929
+
1930
+ for pr in pipeline_runs_sorted:
1931
+ # Only count a pipeline run once per execution date
1932
+ if pr.execution_date not in execution_dates_counted:
1933
+ status_counts[pr.status] = (status_counts.get(pr.status) or 0) + 1
1934
+ execution_dates_counted.add(pr.execution_date)
1935
+
1936
+ return status_counts
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import traceback
2
3
  from typing import Dict
3
4
 
4
5
  from mage_ai.orchestration.notification.config import (
@@ -61,33 +62,54 @@ class NotificationSender:
61
62
  if summary is None:
62
63
  return
63
64
  if self.config.slack_config is not None and self.config.slack_config.is_valid:
64
- send_slack_message(self.config.slack_config, details or summary, title)
65
+ try:
66
+ send_slack_message(self.config.slack_config, details or summary, title)
67
+ except Exception:
68
+ traceback.print_exc()
65
69
 
66
70
  if self.config.teams_config is not None and self.config.teams_config.is_valid:
67
- send_teams_message(self.config.teams_config, summary)
71
+ try:
72
+ send_teams_message(self.config.teams_config, summary)
73
+ except Exception:
74
+ traceback.print_exc()
68
75
 
69
76
  if self.config.discord_config is not None and self.config.discord_config.is_valid:
70
- send_discord_message(self.config.discord_config, summary, title)
77
+ try:
78
+ send_discord_message(self.config.discord_config, summary, title)
79
+ except Exception:
80
+ traceback.print_exc()
71
81
 
72
82
  if self.config.telegram_config is not None and self.config.telegram_config.is_valid:
73
- send_telegram_message(self.config.telegram_config, summary, title)
83
+ try:
84
+ send_telegram_message(self.config.telegram_config, summary, title)
85
+ except Exception:
86
+ traceback.print_exc()
74
87
 
75
88
  if self.config.google_chat_config is not None and self.config.google_chat_config.is_valid:
76
- send_google_chat_message(self.config.google_chat_config, summary)
89
+ try:
90
+ send_google_chat_message(self.config.google_chat_config, summary)
91
+ except Exception:
92
+ traceback.print_exc()
77
93
 
78
94
  if self.config.email_config is not None and title is not None:
79
- send_email(
80
- self.config.email_config,
81
- subject=title,
82
- message=details or summary,
83
- )
95
+ try:
96
+ send_email(
97
+ self.config.email_config,
98
+ subject=title,
99
+ message=details or summary,
100
+ )
101
+ except Exception:
102
+ traceback.print_exc()
84
103
 
85
104
  if self.config.opsgenie_config is not None and self.config.opsgenie_config.is_valid:
86
- send_opsgenie_alert(
87
- self.config.opsgenie_config,
88
- message=title,
89
- description=details or summary,
90
- )
105
+ try:
106
+ send_opsgenie_alert(
107
+ self.config.opsgenie_config,
108
+ message=title,
109
+ description=details or summary,
110
+ )
111
+ except Exception:
112
+ traceback.print_exc()
91
113
 
92
114
  def send_pipeline_run_success_message(self, pipeline, pipeline_run) -> None:
93
115
  if AlertOn.PIPELINE_RUN_SUCCESS in self.config.alert_on:
@@ -284,8 +284,7 @@ class PipelineScheduler:
284
284
  or PipelineRun.PipelineRunStatus.FAILED
285
285
  )
286
286
  self.pipeline_run.update(status=status)
287
-
288
- self.on_pipeline_run_failure('Pipeline run timed out.')
287
+ self.on_pipeline_run_failure('Pipeline run timed out.', status=status)
289
288
  elif self.pipeline_run.any_blocks_failed() and not self.allow_blocks_to_fail:
290
289
  self.pipeline_run.update(
291
290
  status=PipelineRun.PipelineRunStatus.FAILED)
@@ -318,30 +317,38 @@ class PipelineScheduler:
318
317
  self.__schedule_blocks(block_runs)
319
318
 
320
319
  @safe_db_query
321
- def on_pipeline_run_failure(self, error_msg: str) -> None:
322
- failed_block_runs = self.pipeline_run.failed_block_runs
323
- stacktrace = None
324
- for br in failed_block_runs:
325
- if br.metrics:
326
- message = br.metrics.get('error', {}).get('message')
327
- if message:
328
- message_split = message.split('\n')
329
- # Truncate the error message if it has too many lines, set max
330
- # lines at 50
331
- if len(message_split) > 50:
332
- message_split = message_split[-50:]
333
- message_split.insert(0, '... (error truncated)')
334
- message = '\n'.join(message_split)
335
- stacktrace = f'Error for block {br.block_uuid}:\n{message}'
336
- break
337
-
320
+ def on_pipeline_run_failure(
321
+ self,
322
+ error_msg: str,
323
+ status=PipelineRun.PipelineRunStatus.FAILED,
324
+ ) -> None:
338
325
  asyncio.run(UsageStatisticLogger().pipeline_run_ended(self.pipeline_run))
339
- self.notification_sender.send_pipeline_run_failure_message(
340
- pipeline=self.pipeline,
341
- pipeline_run=self.pipeline_run,
342
- error=error_msg,
343
- stacktrace=stacktrace,
344
- )
326
+
327
+ if status == PipelineRun.PipelineRunStatus.FAILED:
328
+ # Only send notification when pipeline run status is FAILED
329
+ failed_block_runs = self.pipeline_run.failed_block_runs
330
+ stacktrace = None
331
+ for br in failed_block_runs:
332
+ if br.metrics:
333
+ message = br.metrics.get('error', {}).get('message')
334
+ if message:
335
+ message_split = message.split('\n')
336
+ # Truncate the error message if it has too many lines, set max
337
+ # lines at 50
338
+ if len(message_split) > 50:
339
+ message_split = message_split[-50:]
340
+ message_split.insert(0, '... (error truncated)')
341
+ message = '\n'.join(message_split)
342
+ stacktrace = f'Error for block {br.block_uuid}:\n{message}'
343
+ break
344
+
345
+ self.notification_sender.send_pipeline_run_failure_message(
346
+ pipeline=self.pipeline,
347
+ pipeline_run=self.pipeline_run,
348
+ error=error_msg,
349
+ stacktrace=stacktrace,
350
+ )
351
+
345
352
  # Cancel block runs that are still in progress for the pipeline run.
346
353
  cancel_block_runs_and_jobs(self.pipeline_run, self.pipeline)
347
354
 
@@ -1,7 +1,8 @@
1
1
  from datetime import datetime
2
- from typing import Dict, Optional
2
+ from typing import Dict, List, Optional, Union
3
3
 
4
4
  from mage_ai.api.resources.PipelineScheduleResource import PipelineScheduleResource
5
+ from mage_ai.data_preparation.models.block.remote.models import RemoteBlock
5
6
  from mage_ai.data_preparation.models.pipeline import Pipeline
6
7
  from mage_ai.data_preparation.models.triggers import ScheduleStatus, ScheduleType
7
8
  from mage_ai.orchestration.db.models.schedules import PipelineRun, PipelineSchedule
@@ -25,11 +26,21 @@ def trigger_pipeline(
25
26
  poll_timeout: Optional[float] = None,
26
27
  schedule_name: str = None,
27
28
  verbose: bool = True,
29
+ remote_blocks: List[Union[Dict, RemoteBlock]] = None,
30
+ return_remote_blocks: bool = False,
28
31
  _should_schedule: bool = False, # For internal use only (e.g. running hooks from notebook).
29
32
  ) -> PipelineRun:
30
33
  if variables is None:
31
34
  variables = {}
32
35
 
36
+ if remote_blocks:
37
+ arr = []
38
+ for remote_block in remote_blocks:
39
+ if isinstance(remote_block, dict):
40
+ remote_block = RemoteBlock.load(**remote_block)
41
+ arr.append(remote_block.to_dict())
42
+ variables['remote_blocks'] = arr
43
+
33
44
  pipeline = Pipeline.get(pipeline_uuid, all_projects=project_platform_activated())
34
45
 
35
46
  pipeline_schedule = __fetch_or_create_pipeline_schedule(pipeline, schedule_name=schedule_name)
@@ -50,6 +61,23 @@ def trigger_pipeline(
50
61
  verbose=verbose,
51
62
  )
52
63
 
64
+ if return_remote_blocks and pipeline_run and pipeline_run.pipeline:
65
+ pipeline = pipeline_run.pipeline
66
+
67
+ return [
68
+ dict(
69
+ remote_blocks=[
70
+ RemoteBlock.load(
71
+ block_uuid=block.uuid,
72
+ execution_partition=pipeline_run.execution_partition,
73
+ pipeline_uuid=pipeline.uuid,
74
+ repo_path=pipeline.repo_path,
75
+ )
76
+ for block in pipeline.blocks_by_uuid.values()
77
+ ],
78
+ ),
79
+ ]
80
+
53
81
  return pipeline_run
54
82
 
55
83
 
@@ -3,9 +3,10 @@ import json
3
3
  from datetime import datetime, timedelta, timezone
4
4
  from logging import Logger
5
5
  from time import sleep
6
- from typing import Dict, List, Optional
6
+ from typing import Dict, List, Optional, Union
7
7
 
8
8
  from mage_ai.data_preparation.logging.logger import DictLogger
9
+ from mage_ai.data_preparation.models.block.remote.models import RemoteBlock
9
10
  from mage_ai.data_preparation.models.global_data_product import GlobalDataProduct
10
11
  from mage_ai.data_preparation.models.triggers import ScheduleStatus, ScheduleType
11
12
  from mage_ai.orchestration.db import safe_db_query
@@ -145,6 +146,7 @@ def trigger_and_check_status(
145
146
  round_number: int = 0,
146
147
  verbose: bool = True,
147
148
  should_schedule: bool = False,
149
+ remote_blocks: List[Union[Dict, RemoteBlock]] = None,
148
150
  ):
149
151
  tags = merge_dict(logging_tags, dict(
150
152
  block_uuid=block.uuid if block else None,
@@ -163,6 +165,7 @@ def trigger_and_check_status(
163
165
  print(log_message)
164
166
  print(json.dumps(tags, indent=2))
165
167
 
168
+ pipeline_run = None
166
169
  pipeline_run_created = None
167
170
  tries = 0
168
171
 
@@ -305,6 +308,7 @@ def trigger_and_check_status(
305
308
  global_data_product.pipeline,
306
309
  pipeline_schedule,
307
310
  dict(variables=variables),
311
+ remote_blocks=remote_blocks,
308
312
  should_schedule=should_schedule,
309
313
  )
310
314
  if pipeline_run_created:
@@ -312,11 +316,9 @@ def trigger_and_check_status(
312
316
  f'Created pipeline run {pipeline_run_created.id} for '
313
317
  f'global data product {global_data_product.uuid}.'
314
318
  )
319
+ pipeline_run = pipeline_run_created
315
320
 
316
321
  lock.release_lock(__lock_key_for_creating_pipeline_run(global_data_product))
317
-
318
- if pipeline_run_created:
319
- break
320
322
  else:
321
323
  __log(
322
324
  f'No pipeline run for global data product {global_data_product.uuid} '
@@ -339,6 +341,7 @@ def trigger_and_check_status(
339
341
  poll_timeout=poll_timeout,
340
342
  verbose=verbose,
341
343
  should_schedule=should_schedule,
344
+ remote_blocks=remote_blocks,
342
345
  )
343
346
  break
344
347
  else:
@@ -359,6 +362,8 @@ def trigger_and_check_status(
359
362
  else:
360
363
  break
361
364
 
365
+ return pipeline_run or pipeline_run_created
366
+
362
367
 
363
368
  def fetch_or_create_pipeline_schedule(global_data_product: GlobalDataProduct) -> PipelineSchedule:
364
369
  pipeline_uuid = global_data_product.object_uuid
@@ -1,7 +1,8 @@
1
1
  from datetime import datetime, timedelta
2
2
  from time import sleep
3
- from typing import Dict, Optional
3
+ from typing import Dict, List, Optional, Union
4
4
 
5
+ from mage_ai.data_preparation.models.block.remote.models import RemoteBlock
5
6
  from mage_ai.data_preparation.models.pipeline import Pipeline
6
7
  from mage_ai.data_preparation.models.triggers import ScheduleStatus
7
8
  from mage_ai.orchestration.db import db_connection, safe_db_query
@@ -84,6 +85,7 @@ def create_and_start_pipeline_run(
84
85
  pipeline_schedule: PipelineSchedule,
85
86
  payload: Dict = None,
86
87
  should_schedule: bool = False,
88
+ remote_blocks: List[Union[Dict, RemoteBlock]] = None,
87
89
  ) -> PipelineRun:
88
90
  if payload is None:
89
91
  payload = {}
@@ -94,6 +96,13 @@ def create_and_start_pipeline_run(
94
96
  payload,
95
97
  )
96
98
 
99
+ if remote_blocks:
100
+ variables = configured_payload.get('variables', {})
101
+ if variables.get('remote_blocks'):
102
+ remote_blocks = variables.get('remote_blocks', []) + remote_blocks
103
+ variables['remote_blocks'] = remote_blocks
104
+ configured_payload['variables'] = variables
105
+
97
106
  pipeline_run = PipelineRun.create(**configured_payload)
98
107
 
99
108
  # Do not start the pipeline run immediately due to concurrency control