mage-ai 0.9.68__py3-none-any.whl → 0.9.70__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mage-ai might be problematic. Click here for more details.
- mage_ai/api/policies/BackfillPolicy.py +1 -0
- mage_ai/api/policies/PipelinePolicy.py +1 -0
- mage_ai/api/policies/WorkspacePolicy.py +1 -0
- mage_ai/api/presenters/BackfillPresenter.py +1 -0
- mage_ai/api/resources/GitBranchResource.py +56 -23
- mage_ai/api/resources/GitCustomBranchResource.py +29 -1
- mage_ai/api/resources/OauthResource.py +1 -1
- mage_ai/api/resources/PipelineResource.py +11 -5
- mage_ai/api/resources/PipelineRunResource.py +41 -4
- mage_ai/api/resources/PipelineScheduleResource.py +4 -0
- mage_ai/api/resources/PullRequestResource.py +6 -4
- mage_ai/api/resources/SeedResource.py +2 -1
- mage_ai/api/resources/SessionResource.py +13 -1
- mage_ai/api/resources/WorkspaceResource.py +5 -4
- mage_ai/authentication/permissions/constants.py +2 -0
- mage_ai/authentication/permissions/seed.py +32 -21
- mage_ai/authentication/providers/active_directory.py +4 -3
- mage_ai/authentication/providers/okta.py +22 -83
- mage_ai/cache/block_action_object/__init__.py +1 -1
- mage_ai/cluster_manager/kubernetes/workload_manager.py +52 -1
- mage_ai/cluster_manager/workspace/base.py +6 -0
- mage_ai/cluster_manager/workspace/kubernetes.py +22 -1
- mage_ai/command_center/applications/utils.py +2 -2
- mage_ai/command_center/presenters/text.py +1 -1
- mage_ai/data_preparation/executors/k8s_block_executor.py +30 -7
- mage_ai/data_preparation/executors/k8s_pipeline_executor.py +30 -7
- mage_ai/data_preparation/executors/streaming_pipeline_executor.py +78 -8
- mage_ai/data_preparation/git/__init__.py +50 -22
- mage_ai/data_preparation/git/api.py +62 -7
- mage_ai/data_preparation/git/utils.py +45 -21
- mage_ai/data_preparation/models/block/__init__.py +31 -8
- mage_ai/data_preparation/models/block/data_integration/mixins.py +16 -5
- mage_ai/data_preparation/models/block/dynamic/child.py +3 -0
- mage_ai/data_preparation/models/block/dynamic/utils.py +9 -4
- mage_ai/data_preparation/models/block/dynamic/variables.py +2 -2
- mage_ai/data_preparation/models/block/extension/utils.py +1 -0
- mage_ai/data_preparation/models/block/global_data_product/__init__.py +25 -2
- mage_ai/data_preparation/models/block/integration/__init__.py +1 -1
- mage_ai/data_preparation/models/block/remote/__init__.py +0 -0
- mage_ai/data_preparation/models/block/remote/models.py +58 -0
- mage_ai/data_preparation/models/block/sql/__init__.py +1 -1
- mage_ai/data_preparation/models/block/utils.py +38 -0
- mage_ai/data_preparation/models/constants.py +2 -0
- mage_ai/data_preparation/models/global_data_product/__init__.py +12 -0
- mage_ai/data_preparation/models/pipeline.py +31 -11
- mage_ai/data_preparation/models/triggers/__init__.py +4 -2
- mage_ai/data_preparation/models/utils.py +6 -0
- mage_ai/data_preparation/models/variable.py +18 -4
- mage_ai/data_preparation/repo_manager.py +3 -2
- mage_ai/data_preparation/shared/utils.py +1 -1
- mage_ai/data_preparation/storage/local_storage.py +12 -6
- mage_ai/data_preparation/templates/data_exporters/mysql.py +2 -2
- mage_ai/data_preparation/templates/data_exporters/oracledb.py +27 -0
- mage_ai/data_preparation/templates/repo/metadata.yaml +1 -0
- mage_ai/io/bigquery.py +131 -58
- mage_ai/io/mysql.py +38 -6
- mage_ai/io/snowflake.py +152 -29
- mage_ai/orchestration/db/migrations/versions/42a14d6143f1_update_token_column_type.py +54 -0
- mage_ai/orchestration/db/models/oauth.py +14 -13
- mage_ai/orchestration/db/models/schedules.py +30 -2
- mage_ai/orchestration/job_manager.py +6 -0
- mage_ai/orchestration/notification/sender.py +37 -15
- mage_ai/orchestration/pipeline_scheduler_original.py +48 -31
- mage_ai/orchestration/queue/celery_queue.py +8 -1
- mage_ai/orchestration/queue/process_queue.py +67 -4
- mage_ai/orchestration/queue/queue.py +8 -0
- mage_ai/orchestration/triggers/api.py +29 -1
- mage_ai/orchestration/triggers/global_data_product.py +9 -4
- mage_ai/orchestration/triggers/utils.py +10 -1
- mage_ai/orchestration/utils/resources.py +3 -0
- mage_ai/server/api/downloads.py +4 -1
- mage_ai/server/api/runs.py +151 -0
- mage_ai/server/constants.py +1 -1
- mage_ai/server/frontend_dist/404.html +6 -6
- mage_ai/server/frontend_dist/_next/static/{i8pymuJDTVHdWjUP1QSh1 → RhDiJSkcjCsh4xxX4BFBk}/_buildManifest.js +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/1557-b3502f3f1aa92ac7.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/2717-d9200be634dd6766.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/3548-9d26185b3fb663b1.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/5699-6d708c6b2153ea08.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/7361-8a23dd8360593e7a.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/7966-b9b85ba10667e654.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/9624-8b8e100079ab69e1.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-2a69553d8c6eeb53.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/index-4e12783b064c1cfe.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-4bfc84ff07d7656f.js +1 -0
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-a66b4c7641ae03eb.js → frontend_dist/_next/static/chunks/pages/pipeline-runs-6d183f91a2ff6668.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-7181b086c93784d2.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-38e1fbcfbfc1014e.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-b645a6d13ab9fe3a.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-59aca25a5b1d3998.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-eb11c5390c982b49.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-cb88fd075a357fcf.js → triggers-4612d15a65c35912.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-3591d035bb3bb2b8.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-32985f3f7c7dd3ab.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/settings-c2e9ef989c8bfa73.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-349af617d05f001b.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-60d01d3887e31136.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{users-86814e581acaf5db.js → users-a4db8710f703c729.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/sign-in-09414a8b66fb6f06.js +1 -0
- 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
- mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-3433c8b22e8342aa.js +1 -0
- mage_ai/server/frontend_dist/block-layout.html +2 -2
- mage_ai/server/frontend_dist/compute.html +2 -2
- mage_ai/server/frontend_dist/files.html +2 -2
- mage_ai/server/frontend_dist/global-data-products/[...slug].html +2 -2
- mage_ai/server/frontend_dist/global-data-products.html +2 -2
- mage_ai/server/frontend_dist/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist/global-hooks.html +2 -2
- mage_ai/server/frontend_dist/index.html +2 -2
- mage_ai/server/frontend_dist/manage/files.html +2 -2
- mage_ai/server/frontend_dist/manage/settings.html +2 -2
- mage_ai/server/frontend_dist/manage/users/[user].html +2 -2
- mage_ai/server/frontend_dist/manage/users/new.html +2 -2
- mage_ai/server/frontend_dist/manage/users.html +2 -2
- mage_ai/server/frontend_dist/manage.html +2 -2
- mage_ai/server/frontend_dist/oauth.html +2 -2
- mage_ai/server/frontend_dist/overview.html +2 -2
- mage_ai/server/frontend_dist/pipeline-runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline].html +2 -2
- mage_ai/server/frontend_dist/pipelines.html +2 -2
- mage_ai/server/frontend_dist/platform/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist/platform/global-hooks.html +2 -2
- mage_ai/server/frontend_dist/settings/account/profile.html +2 -2
- mage_ai/server/frontend_dist/settings/platform/preferences.html +2 -2
- mage_ai/server/frontend_dist/settings/platform/settings.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/permissions.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/preferences.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/roles.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/sync-data.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/users.html +2 -2
- mage_ai/server/frontend_dist/settings.html +2 -2
- mage_ai/server/frontend_dist/sign-in.html +6 -6
- mage_ai/server/frontend_dist/templates/[...slug].html +2 -2
- mage_ai/server/frontend_dist/templates.html +2 -2
- mage_ai/server/frontend_dist/terminal.html +2 -2
- mage_ai/server/frontend_dist/test.html +2 -2
- mage_ai/server/frontend_dist/triggers.html +2 -2
- mage_ai/server/frontend_dist/version-control.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/404.html +6 -6
- mage_ai/server/frontend_dist_base_path_template/_next/static/{CKCvjsYCf2imD2X8zAOBf → TdpLLFome13qvM0gXvpHs}/_buildManifest.js +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-b3502f3f1aa92ac7.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-d9200be634dd6766.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-9d26185b3fb663b1.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-6d708c6b2153ea08.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-8a23dd8360593e7a.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7966-b9b85ba10667e654.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-8b8e100079ab69e1.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-2a69553d8c6eeb53.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/index-4e12783b064c1cfe.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-4bfc84ff07d7656f.js +1 -0
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipeline-runs-a66b4c7641ae03eb.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-6d183f91a2ff6668.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-7181b086c93784d2.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-38e1fbcfbfc1014e.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-b645a6d13ab9fe3a.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-59aca25a5b1d3998.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-eb11c5390c982b49.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-cb88fd075a357fcf.js → triggers-4612d15a65c35912.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-3591d035bb3bb2b8.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-32985f3f7c7dd3ab.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-c2e9ef989c8bfa73.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-349af617d05f001b.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-60d01d3887e31136.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{users-86814e581acaf5db.js → users-a4db8710f703c729.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-09414a8b66fb6f06.js +1 -0
- 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
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-3433c8b22e8342aa.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/block-layout.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/compute.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/files.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-data-products.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-hooks.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/index.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/files.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/oauth.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/overview.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/dashboard.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/platform/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/platform/global-hooks.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/platform/preferences.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/platform/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/users/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/sign-in.html +6 -6
- mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/templates.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/terminal.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/test.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/triggers.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/version-control.html +2 -2
- mage_ai/server/scheduler_manager.py +9 -0
- mage_ai/server/server.py +47 -17
- mage_ai/server/utils/output_display.py +2 -2
- mage_ai/server/websocket_server.py +1 -0
- mage_ai/services/aws/ecs/ecs.py +1 -0
- mage_ai/services/k8s/config.py +4 -4
- mage_ai/services/k8s/utils.py +97 -0
- mage_ai/settings/keys/auth.py +2 -0
- mage_ai/settings/server.py +1 -1
- mage_ai/shared/parsers.py +6 -1
- mage_ai/streaming/sources/influxdb.py +2 -0
- mage_ai/streaming/sources/kafka.py +1 -1
- mage_ai/tests/api/endpoints/mixins.py +10 -9
- mage_ai/tests/api/endpoints/test_seeds.py +24 -0
- mage_ai/tests/api/operations/base/mixins.py +1 -1
- mage_ai/tests/api/operations/test_sessions.py +53 -2
- mage_ai/tests/api/resources/test_pipeline_resource.py +2 -2
- mage_ai/tests/authentication/oauth/test_utils.py +1 -1
- mage_ai/tests/authentication/providers/test_okta.py +43 -0
- mage_ai/tests/data_preparation/models/block/test_global_data_product.py +2 -0
- mage_ai/tests/orchestration/db/models/test_oauth.py +3 -3
- mage_ai/tests/orchestration/queue/test_process_queue.py +1 -0
- mage_ai/tests/orchestration/triggers/test_global_data_product.py +138 -136
- mage_ai/tests/server/test_server.py +27 -4
- mage_ai/tests/services/k8s/test_job_manager.py +9 -6
- mage_ai/version_control/branch/utils.py +2 -1
- mage_ai/version_control/models.py +3 -2
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/METADATA +5 -5
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/RECORD +272 -264
- mage_ai/server/frontend_dist/_next/static/chunks/1557-01f0843dc6ac4971.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/2717-b5f9575799b594d5.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/3548-961ff79ca70038c7.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/5699-6efc749f2f8ddd20.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/7361-18d9d8be96e1ce97.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/7966-f07b2913f7326b50.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/9624-59b2f803f9c88cd6.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-08790743315de36a.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/index-13760bb72d823b69.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-852d403c7bda21b3.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-bd0aff5a5ed8888c.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-d1ee961387c58b7f.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-f028ef3880ed856c.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-ceb06e1616ee9610.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-8ff16ef9566e911a.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/settings-74d76300942dcee8.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-d7a8bc51bb7a1082.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-79a4cf66a623e667.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/sign-in-f59d34429fe022ee.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-01f0843dc6ac4971.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-b5f9575799b594d5.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-961ff79ca70038c7.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-6efc749f2f8ddd20.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-18d9d8be96e1ce97.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7966-f07b2913f7326b50.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-59b2f803f9c88cd6.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-08790743315de36a.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/index-13760bb72d823b69.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-852d403c7bda21b3.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-bd0aff5a5ed8888c.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-d1ee961387c58b7f.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-f028ef3880ed856c.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-ceb06e1616ee9610.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-8ff16ef9566e911a.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-74d76300942dcee8.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-d7a8bc51bb7a1082.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-79a4cf66a623e667.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-f59d34429fe022ee.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +0 -1
- /mage_ai/server/frontend_dist/_next/static/{i8pymuJDTVHdWjUP1QSh1 → RhDiJSkcjCsh4xxX4BFBk}/_ssgManifest.js +0 -0
- /mage_ai/server/frontend_dist_base_path_template/_next/static/{CKCvjsYCf2imD2X8zAOBf → TdpLLFome13qvM0gXvpHs}/_ssgManifest.js +0 -0
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/LICENSE +0 -0
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/WHEEL +0 -0
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/entry_points.txt +0 -0
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/top_level.txt +0 -0
mage_ai/server/server.py
CHANGED
|
@@ -13,6 +13,7 @@ from typing import Optional, Union
|
|
|
13
13
|
import pytz
|
|
14
14
|
import tornado.ioloop
|
|
15
15
|
import tornado.web
|
|
16
|
+
from sqlalchemy import or_
|
|
16
17
|
from tornado import autoreload
|
|
17
18
|
from tornado.ioloop import PeriodicCallback
|
|
18
19
|
from tornado.log import enable_pretty_logging
|
|
@@ -42,7 +43,7 @@ from mage_ai.data_preparation.repo_manager import (
|
|
|
42
43
|
)
|
|
43
44
|
from mage_ai.data_preparation.shared.constants import MANAGE_ENV_VAR
|
|
44
45
|
from mage_ai.orchestration.constants import Entity
|
|
45
|
-
from mage_ai.orchestration.db import db_connection, set_db_schema
|
|
46
|
+
from mage_ai.orchestration.db import db_connection, safe_db_query, set_db_schema
|
|
46
47
|
from mage_ai.orchestration.db.database_manager import database_manager
|
|
47
48
|
from mage_ai.orchestration.db.models.oauth import Oauth2Application, Role, User
|
|
48
49
|
from mage_ai.orchestration.utils.distributed_lock import DistributedLock
|
|
@@ -55,6 +56,7 @@ from mage_ai.server.api.events import (
|
|
|
55
56
|
ApiEventMatcherDetailHandler,
|
|
56
57
|
ApiEventMatcherListHandler,
|
|
57
58
|
)
|
|
59
|
+
from mage_ai.server.api.runs import ApiRunHandler
|
|
58
60
|
from mage_ai.server.api.triggers import ApiTriggerPipelineHandler
|
|
59
61
|
from mage_ai.server.api.v1 import (
|
|
60
62
|
ApiChildDetailHandler,
|
|
@@ -321,6 +323,16 @@ def make_app(
|
|
|
321
323
|
ApiTriggerPipelineHandler,
|
|
322
324
|
),
|
|
323
325
|
|
|
326
|
+
# Run a single block and get a response immediately
|
|
327
|
+
(
|
|
328
|
+
r'/api/runs',
|
|
329
|
+
ApiRunHandler,
|
|
330
|
+
),
|
|
331
|
+
(
|
|
332
|
+
r'/api/runs/(?P<token>\w+)',
|
|
333
|
+
ApiRunHandler,
|
|
334
|
+
),
|
|
335
|
+
|
|
324
336
|
# Download block output
|
|
325
337
|
(
|
|
326
338
|
r'/api/pipelines/(?P<pipeline_uuid>\w+)/block_outputs/'
|
|
@@ -444,6 +456,7 @@ def make_app(
|
|
|
444
456
|
)
|
|
445
457
|
|
|
446
458
|
|
|
459
|
+
@safe_db_query
|
|
447
460
|
def initialize_user_authentication(project_type: ProjectType) -> Oauth2Application:
|
|
448
461
|
logger.info('User authentication is enabled.')
|
|
449
462
|
# We need to sleep for a few seconds after creating all the tables or else there
|
|
@@ -452,12 +465,14 @@ def initialize_user_authentication(project_type: ProjectType) -> Oauth2Applicati
|
|
|
452
465
|
|
|
453
466
|
# Create new roles on existing users. This should only need to be run once.
|
|
454
467
|
if project_type == ProjectType.SUB:
|
|
468
|
+
project_uuid = get_project_uuid()
|
|
469
|
+
project_uuid_truncated = project_uuid[:8]
|
|
455
470
|
Role.create_default_roles(
|
|
456
471
|
entity=Entity.PROJECT,
|
|
457
|
-
entity_id=
|
|
458
|
-
|
|
472
|
+
entity_id=project_uuid,
|
|
473
|
+
name_func=lambda role: f'{role}_{project_uuid_truncated}',
|
|
459
474
|
)
|
|
460
|
-
default_owner_role = Role.get_role(f'{
|
|
475
|
+
default_owner_role = Role.get_role(f'{Role.DefaultRole.OWNER}_{project_uuid_truncated}')
|
|
461
476
|
else:
|
|
462
477
|
Role.create_default_roles()
|
|
463
478
|
default_owner_role = Role.get_role(Role.DefaultRole.OWNER)
|
|
@@ -469,19 +484,29 @@ def initialize_user_authentication(project_type: ProjectType) -> Oauth2Applicati
|
|
|
469
484
|
if not legacy_owner_user and len(owner_users) == 0:
|
|
470
485
|
logger.info('User with owner permission doesn’t exist, creating owner user.')
|
|
471
486
|
if AUTHENTICATION_MODE.lower() == 'ldap':
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
487
|
+
username = get_settings_value(LDAP_ADMIN_USERNAME, 'admin')
|
|
488
|
+
user = User.query.filter(User.username == username).first()
|
|
489
|
+
if not user:
|
|
490
|
+
user = User.create(
|
|
491
|
+
roles_new=[default_owner_role],
|
|
492
|
+
username=get_settings_value(LDAP_ADMIN_USERNAME, 'admin'),
|
|
493
|
+
)
|
|
476
494
|
else:
|
|
477
495
|
password_salt = generate_salt()
|
|
478
|
-
user = User.
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
496
|
+
user = User.query.filter(
|
|
497
|
+
or_(
|
|
498
|
+
User.email == 'admin@admin.com',
|
|
499
|
+
User.username == 'admin',
|
|
500
|
+
),
|
|
501
|
+
).first()
|
|
502
|
+
if not user:
|
|
503
|
+
user = User.create(
|
|
504
|
+
email='admin@admin.com',
|
|
505
|
+
password_hash=create_bcrypt_hash('admin', password_salt),
|
|
506
|
+
password_salt=password_salt,
|
|
507
|
+
roles_new=[default_owner_role],
|
|
508
|
+
username='admin',
|
|
509
|
+
)
|
|
485
510
|
owner_user = user
|
|
486
511
|
else:
|
|
487
512
|
if legacy_owner_user and not legacy_owner_user.roles_new:
|
|
@@ -573,7 +598,7 @@ async def main(
|
|
|
573
598
|
from mage_ai.data_preparation.sync.git_sync import GitSync
|
|
574
599
|
sync_config = GitConfig.load(config=preferences.sync_config)
|
|
575
600
|
sync = GitSync(sync_config, setup_repo=True)
|
|
576
|
-
if sync_config.sync_on_start:
|
|
601
|
+
if sync_config.remote_repo_link and sync_config.sync_on_start is True:
|
|
577
602
|
try:
|
|
578
603
|
sync.sync_data()
|
|
579
604
|
logger.info(
|
|
@@ -671,7 +696,12 @@ async def main(
|
|
|
671
696
|
update_settings_on_metadata_change()
|
|
672
697
|
observer = Observer()
|
|
673
698
|
event_handler = MetadataEventHandler()
|
|
674
|
-
|
|
699
|
+
metadata_file = get_metadata_path(root_project=True)
|
|
700
|
+
if not os.path.exists(metadata_file):
|
|
701
|
+
os.makedirs(os.path.dirname(metadata_file), exist_ok=True)
|
|
702
|
+
with open(metadata_file, 'w') as f:
|
|
703
|
+
f.write('')
|
|
704
|
+
observer.schedule(event_handler, path=metadata_file)
|
|
675
705
|
observer.start()
|
|
676
706
|
|
|
677
707
|
get_messages(
|
|
@@ -208,7 +208,7 @@ def __custom_output():
|
|
|
208
208
|
ignore_nan=True,
|
|
209
209
|
)
|
|
210
210
|
return print(f'[__internal_output__]{{_json_string}}')
|
|
211
|
-
elif bool({has_reduce_output}):
|
|
211
|
+
elif bool({has_reduce_output}) and bool({is_dynamic_child}):
|
|
212
212
|
_json_string = simplejson.dumps(
|
|
213
213
|
transform_output_for_display_reduce_output(
|
|
214
214
|
_internal_output_return,
|
|
@@ -460,7 +460,7 @@ def execute_custom_code():
|
|
|
460
460
|
|
|
461
461
|
output = block_output['output'] or []
|
|
462
462
|
|
|
463
|
-
if {widget} or is_dynamic_block(block) or is_dynamic_child
|
|
463
|
+
if {widget} or is_dynamic_block(block) or is_dynamic_child:
|
|
464
464
|
return output
|
|
465
465
|
else:
|
|
466
466
|
return find(lambda val: val is not None, output)
|
mage_ai/services/aws/ecs/ecs.py
CHANGED
|
@@ -16,6 +16,7 @@ def run_task(
|
|
|
16
16
|
response = client.run_task(**ecs_config.get_task_config(command=command))
|
|
17
17
|
|
|
18
18
|
print(json.dumps(response, indent=4, default=str))
|
|
19
|
+
wait_for_completion = False if ecs_config.wait_timeout == -1 else wait_for_completion
|
|
19
20
|
|
|
20
21
|
if wait_for_completion:
|
|
21
22
|
arn = response['tasks'][0]['taskArn']
|
mage_ai/services/k8s/config.py
CHANGED
|
@@ -2,7 +2,6 @@ from dataclasses import dataclass
|
|
|
2
2
|
from typing import Dict
|
|
3
3
|
|
|
4
4
|
from kubernetes.client import (
|
|
5
|
-
V1Affinity,
|
|
6
5
|
V1Container,
|
|
7
6
|
V1EnvVar,
|
|
8
7
|
V1LocalObjectReference,
|
|
@@ -15,8 +14,9 @@ from kubernetes.client import (
|
|
|
15
14
|
)
|
|
16
15
|
|
|
17
16
|
from mage_ai.services.k8s.constants import CONFIG_FILE, DEFAULT_NAMESPACE
|
|
17
|
+
from mage_ai.services.k8s.utils import parse_affinity_config
|
|
18
18
|
from mage_ai.shared.config import BaseConfig
|
|
19
|
-
from mage_ai.shared.hash import
|
|
19
|
+
from mage_ai.shared.hash import get_safe_value
|
|
20
20
|
|
|
21
21
|
# import traceback
|
|
22
22
|
|
|
@@ -61,8 +61,8 @@ class K8sExecutorConfig(BaseConfig):
|
|
|
61
61
|
executor_config.pod.get('service_account_name') or DEFAULT_SERVICE_ACCOUNT_NAME
|
|
62
62
|
)
|
|
63
63
|
if executor_config.pod.get('affinity'):
|
|
64
|
-
affinity
|
|
65
|
-
|
|
64
|
+
# Convert the affinity to a V1Affinity object
|
|
65
|
+
affinity = parse_affinity_config(executor_config.pod['affinity'])
|
|
66
66
|
|
|
67
67
|
if executor_config.pod.get('tolerations'):
|
|
68
68
|
tolerations += [V1Toleration(**e) for e in executor_config.pod['tolerations']]
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from kubernetes import client
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def parse_affinity_config(affinity_config):
|
|
5
|
+
if not affinity_config:
|
|
6
|
+
return None
|
|
7
|
+
|
|
8
|
+
affinity = client.V1Affinity(
|
|
9
|
+
node_affinity=parse_node_affinity(affinity_config.get('nodeAffinity')),
|
|
10
|
+
pod_affinity=parse_pod_affinity(affinity_config.get('podAffinity')),
|
|
11
|
+
pod_anti_affinity=parse_pod_anti_affinity(affinity_config.get('podAntiAffinity'))
|
|
12
|
+
)
|
|
13
|
+
return affinity
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def parse_node_affinity(node_affinity_config):
|
|
17
|
+
if not node_affinity_config:
|
|
18
|
+
return None
|
|
19
|
+
|
|
20
|
+
required_terms = node_affinity_config.get(
|
|
21
|
+
'requiredDuringSchedulingIgnoredDuringExecution', {}).get('nodeSelectorTerms', [])
|
|
22
|
+
node_selector_terms = []
|
|
23
|
+
for term in required_terms:
|
|
24
|
+
match_expressions = [client.V1NodeSelectorRequirement(
|
|
25
|
+
key=expr.get('key'),
|
|
26
|
+
operator=expr.get('operator'),
|
|
27
|
+
values=expr.get('values')
|
|
28
|
+
) for expr in term.get('matchExpressions', [])]
|
|
29
|
+
|
|
30
|
+
node_selector_terms.append(client.V1NodeSelectorTerm(match_expressions=match_expressions))
|
|
31
|
+
|
|
32
|
+
return client.V1NodeAffinity(
|
|
33
|
+
required_during_scheduling_ignored_during_execution=client.V1NodeSelector(
|
|
34
|
+
node_selector_terms=node_selector_terms)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def parse_pod_affinity(pod_affinity_config):
|
|
39
|
+
if not pod_affinity_config:
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
required_terms = pod_affinity_config.get('requiredDuringSchedulingIgnoredDuringExecution', [])
|
|
43
|
+
preferred_terms = pod_affinity_config.get('preferredDuringSchedulingIgnoredDuringExecution', [])
|
|
44
|
+
|
|
45
|
+
required_affinity = parse_affinity_term_list(required_terms)
|
|
46
|
+
preferred_affinity = parse_affinity_term_list(preferred_terms)
|
|
47
|
+
|
|
48
|
+
return client.V1PodAffinity(
|
|
49
|
+
required_during_scheduling_ignored_during_execution=required_affinity,
|
|
50
|
+
preferred_during_scheduling_ignored_during_execution=preferred_affinity
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def parse_pod_anti_affinity(pod_anti_affinity_config):
|
|
55
|
+
if not pod_anti_affinity_config:
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
required_terms = pod_anti_affinity_config.get(
|
|
59
|
+
'requiredDuringSchedulingIgnoredDuringExecution', [])
|
|
60
|
+
preferred_terms = pod_anti_affinity_config.get(
|
|
61
|
+
'preferredDuringSchedulingIgnoredDuringExecution', [])
|
|
62
|
+
|
|
63
|
+
required_affinity = parse_affinity_term_list(required_terms)
|
|
64
|
+
preferred_affinity = parse_affinity_term_list(preferred_terms)
|
|
65
|
+
|
|
66
|
+
return client.V1PodAntiAffinity(
|
|
67
|
+
required_during_scheduling_ignored_during_execution=required_affinity,
|
|
68
|
+
preferred_during_scheduling_ignored_during_execution=preferred_affinity
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def parse_affinity_term_list(term_list):
|
|
73
|
+
if not term_list:
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
affinity_term_list = []
|
|
77
|
+
for term in term_list:
|
|
78
|
+
label_selector = client.V1LabelSelector(
|
|
79
|
+
match_labels=term.get('labelSelector', {}).get('matchLabels', {}),
|
|
80
|
+
match_expressions=[client.V1LabelSelectorRequirement(
|
|
81
|
+
key=expr.get('key'),
|
|
82
|
+
operator=expr.get('operator'),
|
|
83
|
+
values=expr.get('values')
|
|
84
|
+
) for expr in term.get('labelSelector', {}).get('matchExpressions', [])]
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
topology_key = term.get('topologyKey')
|
|
88
|
+
|
|
89
|
+
affinity_term_list.append(client.V1WeightedPodAffinityTerm(
|
|
90
|
+
weight=term.get('weight'),
|
|
91
|
+
pod_affinity_term=client.V1PodAffinityTerm(
|
|
92
|
+
label_selector=label_selector,
|
|
93
|
+
topology_key=topology_key
|
|
94
|
+
)
|
|
95
|
+
))
|
|
96
|
+
|
|
97
|
+
return affinity_term_list
|
mage_ai/settings/keys/auth.py
CHANGED
mage_ai/settings/server.py
CHANGED
|
@@ -79,7 +79,7 @@ SERVER_LOGGING_TEMPLATE = os.getenv(
|
|
|
79
79
|
'%(levelname)s:%(name)s:%(message)s',
|
|
80
80
|
)
|
|
81
81
|
|
|
82
|
-
INITIAL_METADATA = os.getenv('INITIAL_METADATA'
|
|
82
|
+
INITIAL_METADATA = os.getenv('INITIAL_METADATA')
|
|
83
83
|
|
|
84
84
|
DISABLE_AUTO_BROWSER_OPEN = get_bool_value(os.getenv('DISABLE_AUTO_BROWSER_OPEN', 'False'))
|
|
85
85
|
|
mage_ai/shared/parsers.py
CHANGED
|
@@ -25,11 +25,13 @@ MAX_ITEMS_IN_SAMPLE_OUTPUT = 20
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def encode_complex(obj):
|
|
28
|
+
from mage_ai.shared.models import BaseDataClass
|
|
29
|
+
|
|
28
30
|
if isinstance(obj, set):
|
|
29
31
|
return list(obj)
|
|
30
32
|
elif isinstance(obj, BaseModel):
|
|
31
33
|
return obj.__class__.__name__
|
|
32
|
-
elif obj.__class__.__name__ == 'BaseDataClass':
|
|
34
|
+
elif obj.__class__.__name__ == 'BaseDataClass' or isinstance(obj, BaseDataClass):
|
|
33
35
|
return obj.to_dict()
|
|
34
36
|
elif isinstance(obj, Enum):
|
|
35
37
|
return obj.value
|
|
@@ -59,6 +61,9 @@ def encode_complex(obj):
|
|
|
59
61
|
return obj.to_dict(orient='records')
|
|
60
62
|
elif isinstance(obj, pd.Series):
|
|
61
63
|
return obj.to_list()
|
|
64
|
+
# Convert pandas._libs.missing.NAType to None
|
|
65
|
+
elif isinstance(obj, pd._libs.missing.NAType):
|
|
66
|
+
return None
|
|
62
67
|
|
|
63
68
|
return obj
|
|
64
69
|
|
|
@@ -164,6 +164,7 @@ class InfluxDbSource(BaseSource):
|
|
|
164
164
|
'data': {record.get_field(): record.get_value()},
|
|
165
165
|
'metadata': {
|
|
166
166
|
'time': int(1e3 * record.get_time().timestamp()),
|
|
167
|
+
'measurement': record['_measurement'],
|
|
167
168
|
'tags': {
|
|
168
169
|
k: v
|
|
169
170
|
for k, v in record.values.items()
|
|
@@ -201,6 +202,7 @@ class InfluxDbSource(BaseSource):
|
|
|
201
202
|
'data': {record.get_field(): record.get_value()},
|
|
202
203
|
'metadata': {
|
|
203
204
|
'time': int(1e3 * record.get_time().timestamp()),
|
|
205
|
+
'measurement': record['_measurement'],
|
|
204
206
|
'tags': {
|
|
205
207
|
k: v
|
|
206
208
|
for k, v in record.values.items()
|
|
@@ -257,7 +257,7 @@ class KafkaSource(BaseSource):
|
|
|
257
257
|
|
|
258
258
|
def test_connection(self):
|
|
259
259
|
self.consumer._client.check_version(timeout=5)
|
|
260
|
-
self._print('Test
|
|
260
|
+
self._print('Test connection successfully.')
|
|
261
261
|
|
|
262
262
|
def __deserialize_message(self, message):
|
|
263
263
|
if self.config.serde_config is None:
|
|
@@ -813,7 +813,7 @@ class BaseAPIEndpointTest(AsyncDBTestCase):
|
|
|
813
813
|
validation = assert_after_create_count(self)
|
|
814
814
|
self.assertTrue(validation)
|
|
815
815
|
else:
|
|
816
|
-
after_count = len(get_resource().model_class.all())
|
|
816
|
+
after_count = len(get_resource(resource).model_class.all())
|
|
817
817
|
self.assertEqual(
|
|
818
818
|
after_count,
|
|
819
819
|
before_count + 1 if after_create_count is None else after_create_count,
|
|
@@ -1096,7 +1096,7 @@ class BaseAPIEndpointTest(AsyncDBTestCase):
|
|
|
1096
1096
|
|
|
1097
1097
|
self.assertTrue(validation)
|
|
1098
1098
|
else:
|
|
1099
|
-
after_count = len(get_resource().model_class.all())
|
|
1099
|
+
after_count = len(get_resource(resource).model_class.all())
|
|
1100
1100
|
self.assertEqual(
|
|
1101
1101
|
after_count,
|
|
1102
1102
|
before_count - 1 if after_delete_count is None else after_delete_count,
|
|
@@ -1132,15 +1132,16 @@ class BaseAPIEndpointTest(AsyncDBTestCase):
|
|
|
1132
1132
|
)
|
|
1133
1133
|
RolePermission.create(permission_id=permission.id, role_id=role.id)
|
|
1134
1134
|
else:
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1135
|
+
e_name = entity_name(resource)
|
|
1136
|
+
if getattr(EntityName, e_name, None) is not None:
|
|
1137
|
+
permission = Permission.create(
|
|
1138
|
+
access=access_for_permissions,
|
|
1139
|
+
entity_name=EntityName(e_name),
|
|
1140
|
+
options=permission_options,
|
|
1141
|
+
)
|
|
1142
|
+
RolePermission.create(permission_id=permission.id, role_id=role.id)
|
|
1140
1143
|
|
|
1141
1144
|
if permission_settings:
|
|
1142
1145
|
for permission_setting in permission_settings:
|
|
1143
1146
|
permission_more = Permission.create(**permission_setting)
|
|
1144
1147
|
RolePermission.create(permission_id=permission_more.id, role_id=role.id)
|
|
1145
|
-
|
|
1146
|
-
RolePermission.create(permission_id=permission.id, role_id=role.id)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from mage_ai.authentication.permissions.constants import PermissionAccess
|
|
2
|
+
from mage_ai.tests.api.endpoints.mixins import (
|
|
3
|
+
BaseAPIEndpointTest,
|
|
4
|
+
build_create_endpoint_tests,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SeedAPIEndpointTest(BaseAPIEndpointTest):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
build_create_endpoint_tests(
|
|
13
|
+
SeedAPIEndpointTest,
|
|
14
|
+
resource='seed',
|
|
15
|
+
authentication_accesses=[PermissionAccess.OWNER],
|
|
16
|
+
permissions_accesses=[PermissionAccess.OWNER],
|
|
17
|
+
build_payload=lambda self: dict(
|
|
18
|
+
roles=True,
|
|
19
|
+
permissions=True,
|
|
20
|
+
policy_names=['Block', 'Pipeline', 'Download'],
|
|
21
|
+
),
|
|
22
|
+
assert_before_create_count=lambda self: True,
|
|
23
|
+
assert_after_create_count=lambda self: True,
|
|
24
|
+
)
|
|
@@ -178,7 +178,9 @@ class SessionOperationTests(BaseApiTestCase):
|
|
|
178
178
|
@patch('mage_ai.api.resources.SessionResource.AUTHENTICATION_MODE', 'ldap')
|
|
179
179
|
@patch.object(LDAPConnection, 'authorize')
|
|
180
180
|
@patch.object(LDAPConnection, 'authenticate')
|
|
181
|
-
async def test_ldap_login_with_role_mapping(
|
|
181
|
+
async def test_ldap_login_with_role_mapping(
|
|
182
|
+
self, mock_authenticate, mock_authorize
|
|
183
|
+
):
|
|
182
184
|
mock_authenticate.return_value = (
|
|
183
185
|
True,
|
|
184
186
|
"Yami_Sukehiro",
|
|
@@ -188,7 +190,9 @@ class SessionOperationTests(BaseApiTestCase):
|
|
|
188
190
|
|
|
189
191
|
Role.create_default_roles()
|
|
190
192
|
|
|
191
|
-
with patch.dict(
|
|
193
|
+
with patch.dict(
|
|
194
|
+
os.environ, dict(LDAP_ROLES_MAPPING=json.dumps(dict(Admin=['Admin'])))
|
|
195
|
+
):
|
|
192
196
|
username = self.faker.email()
|
|
193
197
|
operation = self.build_operation(
|
|
194
198
|
action=constants.CREATE,
|
|
@@ -208,3 +212,50 @@ class SessionOperationTests(BaseApiTestCase):
|
|
|
208
212
|
user = User.query.filter(User.username == username).first()
|
|
209
213
|
self.assertIsNotNone(user)
|
|
210
214
|
self.assertEqual(user.roles_new[0].name, 'Admin')
|
|
215
|
+
|
|
216
|
+
@patch('mage_ai.api.resources.SessionResource.AUTHENTICATION_MODE', 'ldap')
|
|
217
|
+
@patch.object(LDAPConnection, 'authorize')
|
|
218
|
+
@patch.object(LDAPConnection, 'authenticate')
|
|
219
|
+
async def test_ldap_update_roles_on_login(self, mock_authenticate, mock_authorize):
|
|
220
|
+
mock_authenticate.return_value = (
|
|
221
|
+
True,
|
|
222
|
+
"Yami_Sukehiro",
|
|
223
|
+
dict(memberOf=['Admin']),
|
|
224
|
+
)
|
|
225
|
+
mock_authorize.return_value = True
|
|
226
|
+
|
|
227
|
+
Role.create_default_roles()
|
|
228
|
+
|
|
229
|
+
username = self.faker.email()
|
|
230
|
+
|
|
231
|
+
User.create(
|
|
232
|
+
username=username,
|
|
233
|
+
email=username,
|
|
234
|
+
roles_new=[Role.query.filter(Role.name == 'Admin').first()],
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
with patch.dict(
|
|
238
|
+
os.environ,
|
|
239
|
+
dict(
|
|
240
|
+
LDAP_ROLES_MAPPING=json.dumps(dict(Admin=['Editor'])),
|
|
241
|
+
UPDATE_ROLES_ON_LOGIN='1',
|
|
242
|
+
),
|
|
243
|
+
):
|
|
244
|
+
operation = self.build_operation(
|
|
245
|
+
action=constants.CREATE,
|
|
246
|
+
payload=dict(
|
|
247
|
+
session=dict(
|
|
248
|
+
email=username,
|
|
249
|
+
password="black bull",
|
|
250
|
+
)
|
|
251
|
+
),
|
|
252
|
+
resource='sessions',
|
|
253
|
+
user=None,
|
|
254
|
+
)
|
|
255
|
+
await operation.execute()
|
|
256
|
+
|
|
257
|
+
mock_authenticate.assert_called_once_with(username, "black bull")
|
|
258
|
+
mock_authorize.assert_called_once_with("Yami_Sukehiro")
|
|
259
|
+
user = User.query.filter(User.username == username).first()
|
|
260
|
+
self.assertIsNotNone(user)
|
|
261
|
+
self.assertEqual(user.roles_new[0].name, 'Editor')
|
|
@@ -129,7 +129,7 @@ class PipelineResourceTest(BaseApiTestCase):
|
|
|
129
129
|
)
|
|
130
130
|
|
|
131
131
|
mock_get_async.assert_has_calls(
|
|
132
|
-
[call.get_async(uuid) for uuid in [
|
|
132
|
+
[call.get_async(uuid, repo_path=self.repo_path) for uuid in [
|
|
133
133
|
self.pipeline1.uuid,
|
|
134
134
|
self.pipeline2.uuid,
|
|
135
135
|
self.pipeline3.uuid,
|
|
@@ -172,7 +172,7 @@ class PipelineResourceTest(BaseApiTestCase):
|
|
|
172
172
|
)
|
|
173
173
|
|
|
174
174
|
mock_get_async.assert_has_calls(
|
|
175
|
-
[call.get_async(uuid) for uuid in [
|
|
175
|
+
[call.get_async(uuid, repo_path=self.repo_path) for uuid in [
|
|
176
176
|
self.pipeline3.uuid,
|
|
177
177
|
]],
|
|
178
178
|
any_order=True,
|
|
@@ -10,7 +10,7 @@ from mage_ai.authentication.oauth.utils import (
|
|
|
10
10
|
access_tokens_for_client,
|
|
11
11
|
refresh_token_for_client,
|
|
12
12
|
)
|
|
13
|
-
from mage_ai.data_preparation.git.
|
|
13
|
+
from mage_ai.data_preparation.git.utils import get_oauth_client_id
|
|
14
14
|
from mage_ai.orchestration.db.models.oauth import Oauth2AccessToken, Oauth2Application
|
|
15
15
|
from mage_ai.tests.base_test import AsyncDBTestCase
|
|
16
16
|
from mage_ai.tests.factory import create_user
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import unittest
|
|
3
|
+
from unittest.mock import patch
|
|
4
|
+
|
|
5
|
+
from mage_ai.authentication.providers.okta import OktaProvider
|
|
6
|
+
|
|
7
|
+
test_parameters = [
|
|
8
|
+
('https://samples.auth0.com', 'test-client-id', 'test-client-secret'),
|
|
9
|
+
('samples.auth0.com', 'test-client-id', 'test-client-secret'),
|
|
10
|
+
('', 'test-client-id', 'test-client-secret'),
|
|
11
|
+
('samples.auth0.com', '', ''),
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class OktaProviderTest(unittest.TestCase):
|
|
16
|
+
def setUp(self):
|
|
17
|
+
self.authorization_endpoint = 'https://samples.auth0.com/authorize'
|
|
18
|
+
self.token_endpoint = 'https://samples.auth0.com/oauth/token'
|
|
19
|
+
self.userinfo_endpoint = 'https://samples.auth0.com/userinfo'
|
|
20
|
+
|
|
21
|
+
def test_okta_provider_initialization(self):
|
|
22
|
+
for url, id, secret in test_parameters:
|
|
23
|
+
with self.subTest():
|
|
24
|
+
with patch.dict(
|
|
25
|
+
os.environ,
|
|
26
|
+
dict(
|
|
27
|
+
OKTA_DOMAIN_URL=url,
|
|
28
|
+
OKTA_CLIENT_ID=id,
|
|
29
|
+
OKTA_CLIENT_SECRET=secret,
|
|
30
|
+
),
|
|
31
|
+
):
|
|
32
|
+
if not all([url, id]):
|
|
33
|
+
with self.assertRaises(ValueError):
|
|
34
|
+
provider = OktaProvider()
|
|
35
|
+
else:
|
|
36
|
+
provider = OktaProvider()
|
|
37
|
+
self.assertEqual(
|
|
38
|
+
provider.authorization_endpoint, self.authorization_endpoint
|
|
39
|
+
)
|
|
40
|
+
self.assertEqual(provider.token_endpoint, self.token_endpoint)
|
|
41
|
+
self.assertEqual(
|
|
42
|
+
provider.userinfo_endpoint, self.userinfo_endpoint
|
|
43
|
+
)
|
|
@@ -17,7 +17,7 @@ class RoleTests(DBTestCase):
|
|
|
17
17
|
Role.create_default_roles(
|
|
18
18
|
entity=Entity.PROJECT,
|
|
19
19
|
entity_id=test_entity_id,
|
|
20
|
-
|
|
20
|
+
name_func=lambda x: f'test_{x}',
|
|
21
21
|
)
|
|
22
22
|
owner = Role.query.filter(Role.name == 'test_Owner').one_or_none()
|
|
23
23
|
|
|
@@ -29,12 +29,12 @@ class RoleTests(DBTestCase):
|
|
|
29
29
|
Role.create_default_roles(
|
|
30
30
|
entity=Entity.PROJECT,
|
|
31
31
|
entity_id=test_entity_id,
|
|
32
|
-
|
|
32
|
+
name_func=lambda x: f'test-2_{x}',
|
|
33
33
|
)
|
|
34
34
|
Role.create_default_roles(
|
|
35
35
|
entity=Entity.PROJECT,
|
|
36
36
|
entity_id=test_entity_id2,
|
|
37
|
-
|
|
37
|
+
name_func=lambda x: f'test-2_{x}',
|
|
38
38
|
)
|
|
39
39
|
owner = Role.query.filter(Role.name == 'test-2_Owner').one_or_none()
|
|
40
40
|
|
|
@@ -13,6 +13,7 @@ class ProcessQueueTests(TestCase):
|
|
|
13
13
|
def setUp(self):
|
|
14
14
|
queue_config = QueueConfig.load(config=dict(concurrency=100))
|
|
15
15
|
self.queue = ProcessQueue(queue_config=queue_config)
|
|
16
|
+
self.queue.start()
|
|
16
17
|
|
|
17
18
|
def test_init(self):
|
|
18
19
|
self.assertEqual(self.queue.size, 100)
|