gooddata-pipelines 1.52.1.dev2__tar.gz → 1.52.1.dev3__tar.gz
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 gooddata-pipelines might be problematic. Click here for more details.
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/PKG-INFO +6 -5
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/README.md +4 -3
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/__init__.py +6 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/api/gooddata_api.py +27 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/backup_and_restore/backup_manager.py +6 -61
- gooddata_pipelines-1.52.1.dev3/gooddata_pipelines/backup_and_restore/base_manager.py +73 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/backup_and_restore/constants.py +5 -4
- gooddata_pipelines-1.52.1.dev3/gooddata_pipelines/backup_and_restore/models/storage.py +156 -0
- gooddata_pipelines-1.52.1.dev3/gooddata_pipelines/backup_and_restore/restore_manager.py +266 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/backup_and_restore/storage/base_storage.py +11 -0
- gooddata_pipelines-1.52.1.dev3/gooddata_pipelines/backup_and_restore/storage/local_storage.py +48 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/backup_and_restore/storage/s3_storage.py +25 -2
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/utils/utils.py +7 -4
- gooddata_pipelines-1.52.1.dev3/gooddata_pipelines/utils/decorators.py +30 -0
- gooddata_pipelines-1.52.1.dev3/gooddata_pipelines/utils/file_utils.py +63 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/pyproject.toml +2 -2
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/backup_and_restore/test_backup.py +5 -2
- gooddata_pipelines-1.52.1.dev3/tests/backup_and_restore/test_restore.py +346 -0
- gooddata_pipelines-1.52.1.dev3/tests/data/restore/test_conf.yaml +6 -0
- gooddata_pipelines-1.52.1.dev3/tests/data/restore/test_udf_root/filter1.yaml +7 -0
- gooddata_pipelines-1.52.1.dev3/tests/data/restore/test_udf_root/filter2.yaml +7 -0
- gooddata_pipelines-1.52.1.dev3/tests/data/restore/test_udf_root/user_data_filters/filter1.yaml +7 -0
- gooddata_pipelines-1.52.1.dev3/tests/data/restore/test_udf_root/user_data_filters/filter2.yaml +7 -0
- gooddata_pipelines-1.52.1.dev3/tests/utils/test_decorators.py +40 -0
- gooddata_pipelines-1.52.1.dev2/gooddata_pipelines/backup_and_restore/models/storage.py +0 -98
- gooddata_pipelines-1.52.1.dev2/gooddata_pipelines/backup_and_restore/storage/local_storage.py +0 -37
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/.gitignore +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/LICENSE.txt +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/Makefile +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/TODO.md +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/_version.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/api/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/api/exceptions.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/api/gooddata_api_wrapper.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/api/gooddata_sdk.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/api/utils.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/backup_and_restore/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/backup_and_restore/backup_input_processor.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/backup_and_restore/csv_reader.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/backup_and_restore/models/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/backup_and_restore/models/input_type.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/backup_and_restore/models/workspace_response.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/backup_and_restore/storage/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/ldm_extension/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/ldm_extension/input_processor.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/ldm_extension/input_validator.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/ldm_extension/ldm_extension_manager.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/ldm_extension/models/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/ldm_extension/models/aliases.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/ldm_extension/models/analytical_object.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/ldm_extension/models/custom_data_object.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/logger/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/logger/logger.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/assets/wdf_setting.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/user_data_filters/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/user_data_filters/models/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/user_data_filters/models/udf_models.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/user_data_filters/user_data_filters.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/users/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/users/models/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/users/models/permissions.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/users/models/user_groups.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/users/models/users.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/users/permissions.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/users/user_groups.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/users/users.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/workspaces/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/workspaces/models.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/workspaces/workspace.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/workspaces/workspace_data_filters.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/workspaces/workspace_data_parser.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/entities/workspaces/workspace_data_validator.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/generic/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/generic/config.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/generic/provision.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/provisioning.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/utils/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/utils/context_objects.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/provisioning/utils/exceptions.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/py.typed +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/utils/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/utils/rate_limiter.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/backup_and_restore/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/backup_and_restore/test_backup_input_processor.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/conftest.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_conf.yaml +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid1/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid1/analytics_model/analytical_dashboard_extensions/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid1/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid1/analytics_model/analytical_dashboards/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid1/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid1/analytics_model/dashboard_plugins/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid1/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid1/analytics_model/filter_contexts/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid1/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid1/analytics_model/metrics/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid1/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid1/analytics_model/visualization_objects/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid1/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid1/ldm/datasets/test.yaml +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid1/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid1/ldm/date_instances/testinstance.yaml +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid2/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid2/analytics_model/analytical_dashboard_extensions/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid2/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid2/analytics_model/analytical_dashboards/id.yaml +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid2/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid2/analytics_model/dashboard_plugins/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid2/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid2/analytics_model/filter_contexts/id.yaml +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid2/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid2/analytics_model/metrics/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid2/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid2/analytics_model/visualization_objects/test.yaml +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid2/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid2/ldm/datasets/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid2/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid2/ldm/date_instances/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid3/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid3/analytics_model/analytical_dashboard_extensions/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid3/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid3/analytics_model/analytical_dashboards/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid3/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid3/analytics_model/dashboard_plugins/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid3/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid3/analytics_model/filter_contexts/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid3/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid3/analytics_model/metrics/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid3/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid3/analytics_model/visualization_objects/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid3/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid3/ldm/datasets/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid3/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid3/ldm/date_instances/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_exports/services/wsid3/20230713-132759-1_3_1_dev5/gooddata_layouts/services/workspaces/wsid3/user_data_filters/.gitkeep +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/backup/test_local_conf.yaml +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/custom_fields/response_get_all_dashboards.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/custom_fields/response_get_all_metrics.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/custom_fields/response_get_all_visualizations.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/mock_responses.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/profiles.yaml +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/permissions/existing_upstream_permissions.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/permissions/permissions_expected_full_load.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/permissions/permissions_expected_incremental_load.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/permissions/permissions_input_full_load.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/permissions/permissions_input_incremental_load.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/users/existing_upstream_users.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/users/profile_response_content.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/users/users_expected_full_load.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/users/users_expected_incremental_load.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/users/users_input_full_load.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/users/users_input_full_load_modifies_protected_user.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/users/users_input_incremental_load.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/users/users_input_incremental_load_deletes_protected_user.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/data/provisioning/entities/users/users_input_incremental_load_modifies_protected_user.json +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/panther/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/panther/test_api_wrapper.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/panther/test_sdk_wrapper.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/provisioning/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/provisioning/entities/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/provisioning/entities/users/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/provisioning/entities/users/test_permissions.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/provisioning/entities/users/test_user_groups.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/provisioning/entities/users/test_users.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/provisioning/entities/workspaces/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/provisioning/entities/workspaces/test_provisioning.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/provisioning/entities/workspaces/test_workspace.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/provisioning/entities/workspaces/test_workspace_data_filters.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/provisioning/entities/workspaces/test_workspace_data_parser.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/provisioning/entities/workspaces/test_workspace_data_validator.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/provisioning/test_provisioning.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/test_ldm_extension/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/test_ldm_extension/test_input_processor.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/test_ldm_extension/test_input_validator.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/test_ldm_extension/test_ldm_extension_manager.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/test_ldm_extension/test_models/__init__.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/test_ldm_extension/test_models/test_analytical_object.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/test_ldm_extension/test_models/test_custom_data_object.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tests/utils/test_rate_limiter.py +0 -0
- {gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gooddata-pipelines
|
|
3
|
-
Version: 1.52.1.
|
|
3
|
+
Version: 1.52.1.dev3
|
|
4
4
|
Summary: GoodData Cloud lifecycle automation pipelines
|
|
5
5
|
Author-email: GoodData <support@gooddata.com>
|
|
6
6
|
License: MIT
|
|
@@ -8,7 +8,7 @@ License-File: LICENSE.txt
|
|
|
8
8
|
Requires-Python: >=3.10
|
|
9
9
|
Requires-Dist: boto3-stubs<2.0.0,>=1.39.3
|
|
10
10
|
Requires-Dist: boto3<2.0.0,>=1.39.3
|
|
11
|
-
Requires-Dist: gooddata-sdk~=1.52.1.
|
|
11
|
+
Requires-Dist: gooddata-sdk~=1.52.1.dev3
|
|
12
12
|
Requires-Dist: pydantic<3.0.0,>=2.11.3
|
|
13
13
|
Requires-Dist: requests<3.0.0,>=2.32.3
|
|
14
14
|
Requires-Dist: types-pyyaml<7.0.0,>=6.0.12.20250326
|
|
@@ -27,9 +27,10 @@ You can use the package to manage following resources in GDC:
|
|
|
27
27
|
- User/Group permissions
|
|
28
28
|
- User Data Filters
|
|
29
29
|
- Child workspaces (incl. Workspace Data Filter settings)
|
|
30
|
-
1.
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
1. Backup and restore of workspaces
|
|
31
|
+
- Create and backup snapshots of workspace metadata.
|
|
32
|
+
1. LDM Extension
|
|
33
|
+
- extend the Logical Data Model of a child workspace with custom datasets and fields
|
|
33
34
|
|
|
34
35
|
In case you are not interested in incorporating a library in your own program but would like to use a ready-made script, consider having a look at [GoodData Productivity Tools](https://github.com/gooddata/gooddata-productivity-tools).
|
|
35
36
|
|
|
@@ -10,9 +10,10 @@ You can use the package to manage following resources in GDC:
|
|
|
10
10
|
- User/Group permissions
|
|
11
11
|
- User Data Filters
|
|
12
12
|
- Child workspaces (incl. Workspace Data Filter settings)
|
|
13
|
-
1.
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
1. Backup and restore of workspaces
|
|
14
|
+
- Create and backup snapshots of workspace metadata.
|
|
15
|
+
1. LDM Extension
|
|
16
|
+
- extend the Logical Data Model of a child workspace with custom datasets and fields
|
|
16
17
|
|
|
17
18
|
In case you are not interested in incorporating a library in your own program but would like to use a ready-made script, consider having a look at [GoodData Productivity Tools](https://github.com/gooddata/gooddata-productivity-tools).
|
|
18
19
|
|
{gooddata_pipelines-1.52.1.dev2 → gooddata_pipelines-1.52.1.dev3}/gooddata_pipelines/__init__.py
RENAMED
|
@@ -10,6 +10,10 @@ from .backup_and_restore.models.storage import (
|
|
|
10
10
|
S3StorageConfig,
|
|
11
11
|
StorageType,
|
|
12
12
|
)
|
|
13
|
+
from .backup_and_restore.restore_manager import (
|
|
14
|
+
RestoreManager,
|
|
15
|
+
WorkspaceToRestore,
|
|
16
|
+
)
|
|
13
17
|
from .backup_and_restore.storage.local_storage import LocalStorage
|
|
14
18
|
from .backup_and_restore.storage.s3_storage import S3Storage
|
|
15
19
|
|
|
@@ -57,6 +61,8 @@ from .provisioning.generic.provision import provision
|
|
|
57
61
|
|
|
58
62
|
__all__ = [
|
|
59
63
|
"BackupManager",
|
|
64
|
+
"RestoreManager",
|
|
65
|
+
"WorkspaceToRestore",
|
|
60
66
|
"BackupRestoreConfig",
|
|
61
67
|
"StorageType",
|
|
62
68
|
"LocalStorage",
|
|
@@ -167,6 +167,17 @@ class ApiMethods:
|
|
|
167
167
|
endpoint = f"/layout/workspaces/{workspace_id}/userDataFilters"
|
|
168
168
|
return self._get(endpoint)
|
|
169
169
|
|
|
170
|
+
def put_user_data_filters(
|
|
171
|
+
self, workspace_id: str, user_data_filters: dict[str, Any]
|
|
172
|
+
) -> requests.Response:
|
|
173
|
+
"""Puts the user data filters into GoodData workspace."""
|
|
174
|
+
headers = {**self.headers, "Content-Type": "application/json"}
|
|
175
|
+
return self._put(
|
|
176
|
+
f"/layout/workspaces/{workspace_id}/userDataFilters",
|
|
177
|
+
user_data_filters,
|
|
178
|
+
headers,
|
|
179
|
+
)
|
|
180
|
+
|
|
170
181
|
def get_automations(self, workspace_id: str) -> requests.Response:
|
|
171
182
|
"""Gets the automations for a given workspace."""
|
|
172
183
|
endpoint = (
|
|
@@ -174,6 +185,22 @@ class ApiMethods:
|
|
|
174
185
|
)
|
|
175
186
|
return self._get(endpoint)
|
|
176
187
|
|
|
188
|
+
def post_automation(
|
|
189
|
+
self, workspace_id: str, automation: dict[str, Any]
|
|
190
|
+
) -> requests.Response:
|
|
191
|
+
"""Posts an automation for a given workspace."""
|
|
192
|
+
endpoint = f"/entities/workspaces/{workspace_id}/automations"
|
|
193
|
+
return self._post(endpoint, automation)
|
|
194
|
+
|
|
195
|
+
def delete_automation(
|
|
196
|
+
self, workspace_id: str, automation_id: str
|
|
197
|
+
) -> requests.Response:
|
|
198
|
+
"""Deletes an automation for a given workspace."""
|
|
199
|
+
endpoint = (
|
|
200
|
+
f"/entities/workspaces/{workspace_id}/automations/{automation_id}"
|
|
201
|
+
)
|
|
202
|
+
return self._delete(endpoint)
|
|
203
|
+
|
|
177
204
|
def get_all_metrics(self, workspace_id: str) -> requests.Response:
|
|
178
205
|
"""Get all metrics from the specified workspace.
|
|
179
206
|
|
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
# (C) 2025 GoodData Corporation
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
3
|
import os
|
|
5
4
|
import shutil
|
|
6
5
|
import tempfile
|
|
7
6
|
import time
|
|
8
7
|
import traceback
|
|
9
8
|
from pathlib import Path
|
|
10
|
-
from typing import Any
|
|
9
|
+
from typing import Any
|
|
11
10
|
|
|
12
11
|
import attrs
|
|
13
12
|
import requests
|
|
14
|
-
import yaml
|
|
15
|
-
from gooddata_sdk.utils import PROFILES_FILE_PATH, profile_content
|
|
16
13
|
|
|
17
|
-
from gooddata_pipelines.api.gooddata_api_wrapper import GoodDataApi
|
|
18
14
|
from gooddata_pipelines.backup_and_restore.backup_input_processor import (
|
|
19
15
|
BackupInputProcessor,
|
|
20
16
|
)
|
|
17
|
+
from gooddata_pipelines.backup_and_restore.base_manager import BaseManager
|
|
21
18
|
from gooddata_pipelines.backup_and_restore.constants import (
|
|
22
19
|
BackupSettings,
|
|
23
20
|
DirNames,
|
|
@@ -25,18 +22,10 @@ from gooddata_pipelines.backup_and_restore.constants import (
|
|
|
25
22
|
from gooddata_pipelines.backup_and_restore.models.input_type import InputType
|
|
26
23
|
from gooddata_pipelines.backup_and_restore.models.storage import (
|
|
27
24
|
BackupRestoreConfig,
|
|
28
|
-
StorageType,
|
|
29
25
|
)
|
|
30
26
|
from gooddata_pipelines.backup_and_restore.storage.base_storage import (
|
|
31
27
|
BackupStorage,
|
|
32
28
|
)
|
|
33
|
-
from gooddata_pipelines.backup_and_restore.storage.local_storage import (
|
|
34
|
-
LocalStorage,
|
|
35
|
-
)
|
|
36
|
-
from gooddata_pipelines.backup_and_restore.storage.s3_storage import (
|
|
37
|
-
S3Storage,
|
|
38
|
-
)
|
|
39
|
-
from gooddata_pipelines.logger import LogObserver
|
|
40
29
|
from gooddata_pipelines.utils.rate_limiter import RateLimiter
|
|
41
30
|
|
|
42
31
|
|
|
@@ -45,16 +34,12 @@ class BackupBatch:
|
|
|
45
34
|
list_of_ids: list[str]
|
|
46
35
|
|
|
47
36
|
|
|
48
|
-
class BackupManager:
|
|
37
|
+
class BackupManager(BaseManager):
|
|
49
38
|
storage: BackupStorage
|
|
50
39
|
|
|
51
40
|
def __init__(self, host: str, token: str, config: BackupRestoreConfig):
|
|
52
|
-
|
|
53
|
-
self.logger = LogObserver()
|
|
54
|
-
|
|
55
|
-
self.config = config
|
|
41
|
+
super().__init__(host, token, config)
|
|
56
42
|
|
|
57
|
-
self.storage = self._get_storage(self.config)
|
|
58
43
|
self.org_id = self._api.get_organization_id()
|
|
59
44
|
|
|
60
45
|
self.loader = BackupInputProcessor(self._api, self.config.api_page_size)
|
|
@@ -63,39 +48,6 @@ class BackupManager:
|
|
|
63
48
|
calls_per_second=self.config.api_calls_per_second,
|
|
64
49
|
)
|
|
65
50
|
|
|
66
|
-
@classmethod
|
|
67
|
-
def create(
|
|
68
|
-
cls: Type["BackupManager"],
|
|
69
|
-
config: BackupRestoreConfig,
|
|
70
|
-
host: str,
|
|
71
|
-
token: str,
|
|
72
|
-
) -> "BackupManager":
|
|
73
|
-
"""Creates a backup worker instance using the provided host and token."""
|
|
74
|
-
return cls(host=host, token=token, config=config)
|
|
75
|
-
|
|
76
|
-
@classmethod
|
|
77
|
-
def create_from_profile(
|
|
78
|
-
cls: Type["BackupManager"],
|
|
79
|
-
config: BackupRestoreConfig,
|
|
80
|
-
profile: str = "default",
|
|
81
|
-
profiles_path: Path = PROFILES_FILE_PATH,
|
|
82
|
-
) -> "BackupManager":
|
|
83
|
-
"""Creates a backup worker instance using a GoodData profile file."""
|
|
84
|
-
content = profile_content(profile, profiles_path)
|
|
85
|
-
return cls(**content, config=config)
|
|
86
|
-
|
|
87
|
-
@staticmethod
|
|
88
|
-
def _get_storage(conf: BackupRestoreConfig) -> BackupStorage:
|
|
89
|
-
"""Returns the storage class based on the storage type."""
|
|
90
|
-
if conf.storage_type == StorageType.S3:
|
|
91
|
-
return S3Storage(conf)
|
|
92
|
-
elif conf.storage_type == StorageType.LOCAL:
|
|
93
|
-
return LocalStorage(conf)
|
|
94
|
-
else:
|
|
95
|
-
raise RuntimeError(
|
|
96
|
-
f'Unsupported storage type "{conf.storage_type.value}".'
|
|
97
|
-
)
|
|
98
|
-
|
|
99
51
|
def get_user_data_filters(self, ws_id: str) -> dict:
|
|
100
52
|
"""Returns the user data filters for the specified workspace."""
|
|
101
53
|
with self._api_rate_limiter:
|
|
@@ -133,19 +85,13 @@ class BackupManager:
|
|
|
133
85
|
"user_data_filters",
|
|
134
86
|
filter["id"] + ".yaml",
|
|
135
87
|
)
|
|
136
|
-
self.
|
|
88
|
+
self.yaml_utils.dump(udf_file_path, filter)
|
|
137
89
|
|
|
138
90
|
@staticmethod
|
|
139
91
|
def _move_folder(source: Path, destination: Path) -> None:
|
|
140
92
|
"""Moves the source folder to the destination."""
|
|
141
93
|
shutil.move(source, destination)
|
|
142
94
|
|
|
143
|
-
@staticmethod
|
|
144
|
-
def _write_to_yaml(path: str, source: Any) -> None:
|
|
145
|
-
"""Writes the source to a YAML file."""
|
|
146
|
-
with open(path, "w") as outfile:
|
|
147
|
-
yaml.dump(source, outfile)
|
|
148
|
-
|
|
149
95
|
def _get_automations_from_api(self, workspace_id: str) -> Any:
|
|
150
96
|
"""Returns automations for the workspace as JSON."""
|
|
151
97
|
with self._api_rate_limiter:
|
|
@@ -182,8 +128,7 @@ class BackupManager:
|
|
|
182
128
|
|
|
183
129
|
# Store the automations in a JSON file
|
|
184
130
|
if len(automations["data"]) > 0:
|
|
185
|
-
|
|
186
|
-
json.dump(automations, f)
|
|
131
|
+
self.json_utils.dump(automations_file_path, automations)
|
|
187
132
|
|
|
188
133
|
def store_declarative_filter_views(
|
|
189
134
|
self, export_path: Path, workspace_id: str
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# (C) 2025 GoodData Corporation
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Type, TypeVar
|
|
6
|
+
|
|
7
|
+
from gooddata_sdk.utils import PROFILES_FILE_PATH, profile_content
|
|
8
|
+
|
|
9
|
+
from gooddata_pipelines.api.gooddata_api_wrapper import GoodDataApi
|
|
10
|
+
from gooddata_pipelines.backup_and_restore.models.storage import (
|
|
11
|
+
BackupRestoreConfig,
|
|
12
|
+
StorageType,
|
|
13
|
+
)
|
|
14
|
+
from gooddata_pipelines.backup_and_restore.storage.base_storage import (
|
|
15
|
+
BackupStorage,
|
|
16
|
+
)
|
|
17
|
+
from gooddata_pipelines.backup_and_restore.storage.local_storage import (
|
|
18
|
+
LocalStorage,
|
|
19
|
+
)
|
|
20
|
+
from gooddata_pipelines.backup_and_restore.storage.s3_storage import S3Storage
|
|
21
|
+
from gooddata_pipelines.logger import LogObserver
|
|
22
|
+
from gooddata_pipelines.utils.file_utils import JsonUtils, YamlUtils
|
|
23
|
+
|
|
24
|
+
ManagerT = TypeVar("ManagerT", bound="BaseManager")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class BaseManager(abc.ABC):
|
|
28
|
+
"""Base class to provide constructors for backup and restore managers."""
|
|
29
|
+
|
|
30
|
+
storage: BackupStorage
|
|
31
|
+
|
|
32
|
+
def __init__(self, host: str, token: str, config: BackupRestoreConfig):
|
|
33
|
+
self.config = config
|
|
34
|
+
|
|
35
|
+
self._api: GoodDataApi = GoodDataApi(host, token)
|
|
36
|
+
self.logger: LogObserver = LogObserver()
|
|
37
|
+
|
|
38
|
+
self.storage = self._get_storage(self.config)
|
|
39
|
+
|
|
40
|
+
self.yaml_utils = YamlUtils()
|
|
41
|
+
self.json_utils = JsonUtils()
|
|
42
|
+
|
|
43
|
+
def _get_storage(self, conf: BackupRestoreConfig) -> BackupStorage:
|
|
44
|
+
"""Returns the storage class based on the storage type."""
|
|
45
|
+
if conf.storage_type == StorageType.S3:
|
|
46
|
+
return S3Storage(conf)
|
|
47
|
+
elif conf.storage_type == StorageType.LOCAL:
|
|
48
|
+
return LocalStorage(conf)
|
|
49
|
+
else:
|
|
50
|
+
raise RuntimeError(
|
|
51
|
+
f'Unsupported storage type "{conf.storage_type.value}".'
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def create(
|
|
56
|
+
cls: Type[ManagerT],
|
|
57
|
+
config: BackupRestoreConfig,
|
|
58
|
+
host: str,
|
|
59
|
+
token: str,
|
|
60
|
+
) -> ManagerT:
|
|
61
|
+
"""Creates a backup worker instance using the provided host and token."""
|
|
62
|
+
return cls(host=host, token=token, config=config)
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def create_from_profile(
|
|
66
|
+
cls: Type[ManagerT],
|
|
67
|
+
config: BackupRestoreConfig,
|
|
68
|
+
profile: str = "default",
|
|
69
|
+
profiles_path: Path = PROFILES_FILE_PATH,
|
|
70
|
+
) -> ManagerT:
|
|
71
|
+
"""Creates a backup worker instance using a GoodData profile file."""
|
|
72
|
+
content = profile_content(profile, profiles_path)
|
|
73
|
+
return cls(host=content["host"], token=content["token"], config=config)
|
|
@@ -23,13 +23,14 @@ class DirNames:
|
|
|
23
23
|
|
|
24
24
|
@attrs.frozen
|
|
25
25
|
class ApiDefaults:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
PAGE_SIZE = 100
|
|
27
|
+
BATCH_SIZE = 100
|
|
28
|
+
CALLS_PER_SECOND = 1.0
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
@attrs.frozen
|
|
32
|
-
class BackupSettings
|
|
32
|
+
class BackupSettings:
|
|
33
|
+
API = ApiDefaults()
|
|
33
34
|
MAX_RETRIES = 3
|
|
34
35
|
RETRY_DELAY = 5 # seconds
|
|
35
36
|
TIMESTAMP_SDK_FOLDER = (
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# (C) 2025 GoodData Corporation
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
|
|
6
|
+
import yaml
|
|
7
|
+
from pydantic import BaseModel, Field, model_validator
|
|
8
|
+
|
|
9
|
+
from gooddata_pipelines.backup_and_restore.constants import BackupSettings
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class StorageType(Enum):
|
|
13
|
+
"""Type of storage."""
|
|
14
|
+
|
|
15
|
+
S3 = "s3"
|
|
16
|
+
LOCAL = "local"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class S3StorageConfig(BaseModel):
|
|
20
|
+
"""Configuration for S3 storage.
|
|
21
|
+
|
|
22
|
+
Can be created using the following constructor methods:
|
|
23
|
+
- `from_iam_role`
|
|
24
|
+
- `from_aws_credentials`
|
|
25
|
+
- `from_aws_profile`
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
backup_path: str
|
|
29
|
+
bucket: str
|
|
30
|
+
profile: str | None = None
|
|
31
|
+
aws_access_key_id: str | None = None
|
|
32
|
+
aws_secret_access_key: str | None = None
|
|
33
|
+
aws_default_region: str = "us-east-1"
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def from_iam_role(cls, backup_path: str, bucket: str) -> "S3StorageConfig":
|
|
37
|
+
"""Use default IAM role or environment credentials.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
backup_path: The path to the backup directory.
|
|
41
|
+
bucket: The name of the S3 bucket.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
S3StorageConfig: The S3 storage configuration.
|
|
45
|
+
"""
|
|
46
|
+
return cls(backup_path=backup_path, bucket=bucket)
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def from_aws_credentials(
|
|
50
|
+
cls,
|
|
51
|
+
backup_path: str,
|
|
52
|
+
bucket: str,
|
|
53
|
+
aws_access_key_id: str,
|
|
54
|
+
aws_secret_access_key: str,
|
|
55
|
+
aws_default_region: str,
|
|
56
|
+
) -> "S3StorageConfig":
|
|
57
|
+
"""Use explicit AWS access keys and region.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
backup_path: The path to the backup directory.
|
|
61
|
+
bucket: The name of the S3 bucket.
|
|
62
|
+
aws_access_key_id: The AWS access key ID.
|
|
63
|
+
aws_secret_access_key: The AWS secret access key.
|
|
64
|
+
aws_default_region: The AWS default region.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
S3StorageConfig: The S3 storage configuration.
|
|
68
|
+
"""
|
|
69
|
+
return cls(
|
|
70
|
+
backup_path=backup_path,
|
|
71
|
+
bucket=bucket,
|
|
72
|
+
aws_access_key_id=aws_access_key_id,
|
|
73
|
+
aws_secret_access_key=aws_secret_access_key,
|
|
74
|
+
aws_default_region=aws_default_region,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_aws_profile(
|
|
79
|
+
cls, backup_path: str, bucket: str, profile: str
|
|
80
|
+
) -> "S3StorageConfig":
|
|
81
|
+
"""Use a named AWS CLI profile.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
backup_path: The path to the backup directory.
|
|
85
|
+
bucket: The name of the S3 bucket.
|
|
86
|
+
profile: The name of the AWS profile.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
S3StorageConfig: The S3 storage configuration.
|
|
90
|
+
"""
|
|
91
|
+
return cls(backup_path=backup_path, bucket=bucket, profile=profile)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class LocalStorageConfig(BaseModel):
|
|
95
|
+
"""Placeholder for local storage config."""
|
|
96
|
+
|
|
97
|
+
backup_path: str = Field(default="local_backups")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class BackupRestoreConfig(BaseModel):
|
|
101
|
+
"""Configuration for backup and restore.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
storage_type: The type of storage to use. Defaults to `StorageType.LOCAL`.
|
|
105
|
+
storage: Storage configuration. Either `S3StorageConfig` or `LocalStorageConfig`. Defaults to `LocalStorageConfig()`.
|
|
106
|
+
api_page_size: The page size for fetching workspace relationships. Defaults to `BackupSettings.API.PAGE_SIZE`.
|
|
107
|
+
batch_size: The batch size for fetching workspace relationships. Defaults to `BackupSettings.API.BATCH_SIZE`.
|
|
108
|
+
api_calls_per_second: The maximum API calls per second (rate limiting). Defaults to `BackupSettings.API.CALLS_PER_SECOND`.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
storage_type: StorageType = Field(default=StorageType.LOCAL)
|
|
112
|
+
storage: S3StorageConfig | LocalStorageConfig = Field(
|
|
113
|
+
default_factory=LocalStorageConfig
|
|
114
|
+
)
|
|
115
|
+
api_page_size: Annotated[
|
|
116
|
+
int,
|
|
117
|
+
Field(
|
|
118
|
+
gt=0,
|
|
119
|
+
description="Page size must be greater than 0",
|
|
120
|
+
),
|
|
121
|
+
] = Field(default=BackupSettings.API.PAGE_SIZE)
|
|
122
|
+
batch_size: Annotated[
|
|
123
|
+
int,
|
|
124
|
+
Field(
|
|
125
|
+
gt=0,
|
|
126
|
+
description="Batch size must be greater than 0",
|
|
127
|
+
),
|
|
128
|
+
] = Field(default=BackupSettings.API.BATCH_SIZE)
|
|
129
|
+
api_calls_per_second: Annotated[
|
|
130
|
+
float,
|
|
131
|
+
Field(
|
|
132
|
+
gt=0,
|
|
133
|
+
description="Maximum API calls per second (rate limiting)",
|
|
134
|
+
),
|
|
135
|
+
] = Field(default=BackupSettings.API.CALLS_PER_SECOND)
|
|
136
|
+
|
|
137
|
+
@classmethod
|
|
138
|
+
def from_yaml(cls, conf_path: str) -> "BackupRestoreConfig":
|
|
139
|
+
with open(conf_path, "r") as stream:
|
|
140
|
+
conf: dict = yaml.safe_load(stream)
|
|
141
|
+
return cls(**conf)
|
|
142
|
+
|
|
143
|
+
@model_validator(mode="after")
|
|
144
|
+
def validate_storage(self) -> "BackupRestoreConfig":
|
|
145
|
+
"""Check that the storage gets correct configuration when using S3 storage"""
|
|
146
|
+
if self.storage_type == StorageType.S3:
|
|
147
|
+
if not isinstance(self.storage, S3StorageConfig):
|
|
148
|
+
raise ValueError(
|
|
149
|
+
"S3 storage must be configured with S3StorageConfig object"
|
|
150
|
+
)
|
|
151
|
+
elif self.storage_type == StorageType.LOCAL:
|
|
152
|
+
if not isinstance(self.storage, LocalStorageConfig):
|
|
153
|
+
raise ValueError(
|
|
154
|
+
"Local storage must be configured with LocalStorageConfig object"
|
|
155
|
+
)
|
|
156
|
+
return self
|