cumulusci-plus 5.0.0__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 cumulusci-plus might be problematic. Click here for more details.
- cumulusci/__about__.py +1 -0
- cumulusci/__init__.py +22 -0
- cumulusci/__main__.py +3 -0
- cumulusci/cli/__init__.py +0 -0
- cumulusci/cli/cci.py +244 -0
- cumulusci/cli/error.py +125 -0
- cumulusci/cli/flow.py +185 -0
- cumulusci/cli/logger.py +72 -0
- cumulusci/cli/org.py +692 -0
- cumulusci/cli/plan.py +181 -0
- cumulusci/cli/project.py +391 -0
- cumulusci/cli/robot.py +116 -0
- cumulusci/cli/runtime.py +190 -0
- cumulusci/cli/service.py +521 -0
- cumulusci/cli/task.py +295 -0
- cumulusci/cli/tests/__init__.py +0 -0
- cumulusci/cli/tests/test_cci.py +545 -0
- cumulusci/cli/tests/test_error.py +170 -0
- cumulusci/cli/tests/test_flow.py +276 -0
- cumulusci/cli/tests/test_logger.py +25 -0
- cumulusci/cli/tests/test_org.py +1438 -0
- cumulusci/cli/tests/test_plan.py +245 -0
- cumulusci/cli/tests/test_project.py +235 -0
- cumulusci/cli/tests/test_robot.py +177 -0
- cumulusci/cli/tests/test_runtime.py +197 -0
- cumulusci/cli/tests/test_service.py +853 -0
- cumulusci/cli/tests/test_task.py +266 -0
- cumulusci/cli/tests/test_ui.py +310 -0
- cumulusci/cli/tests/test_utils.py +122 -0
- cumulusci/cli/tests/utils.py +52 -0
- cumulusci/cli/ui.py +234 -0
- cumulusci/cli/utils.py +150 -0
- cumulusci/conftest.py +181 -0
- cumulusci/core/__init__.py +0 -0
- cumulusci/core/config/BaseConfig.py +5 -0
- cumulusci/core/config/BaseTaskFlowConfig.py +5 -0
- cumulusci/core/config/OrgConfig.py +5 -0
- cumulusci/core/config/ScratchOrgConfig.py +5 -0
- cumulusci/core/config/__init__.py +125 -0
- cumulusci/core/config/base_config.py +111 -0
- cumulusci/core/config/base_task_flow_config.py +82 -0
- cumulusci/core/config/marketing_cloud_service_config.py +83 -0
- cumulusci/core/config/oauth2_service_config.py +17 -0
- cumulusci/core/config/org_config.py +604 -0
- cumulusci/core/config/project_config.py +782 -0
- cumulusci/core/config/scratch_org_config.py +251 -0
- cumulusci/core/config/sfdx_org_config.py +220 -0
- cumulusci/core/config/tests/_test_config_backwards_compatibility.py +33 -0
- cumulusci/core/config/tests/test_config.py +1895 -0
- cumulusci/core/config/tests/test_config_expensive.py +839 -0
- cumulusci/core/config/tests/test_config_util.py +91 -0
- cumulusci/core/config/universal_config.py +88 -0
- cumulusci/core/config/util.py +18 -0
- cumulusci/core/datasets.py +303 -0
- cumulusci/core/debug.py +33 -0
- cumulusci/core/dependencies/__init__.py +55 -0
- cumulusci/core/dependencies/base.py +561 -0
- cumulusci/core/dependencies/dependencies.py +273 -0
- cumulusci/core/dependencies/github.py +177 -0
- cumulusci/core/dependencies/github_resolvers.py +244 -0
- cumulusci/core/dependencies/resolvers.py +580 -0
- cumulusci/core/dependencies/tests/__init__.py +0 -0
- cumulusci/core/dependencies/tests/conftest.py +385 -0
- cumulusci/core/dependencies/tests/test_dependencies.py +950 -0
- cumulusci/core/dependencies/tests/test_github.py +83 -0
- cumulusci/core/dependencies/tests/test_resolvers.py +1027 -0
- cumulusci/core/dependencies/utils.py +13 -0
- cumulusci/core/enums.py +11 -0
- cumulusci/core/exceptions.py +311 -0
- cumulusci/core/flowrunner.py +888 -0
- cumulusci/core/github.py +665 -0
- cumulusci/core/keychain/__init__.py +24 -0
- cumulusci/core/keychain/base_project_keychain.py +441 -0
- cumulusci/core/keychain/encrypted_file_project_keychain.py +945 -0
- cumulusci/core/keychain/environment_project_keychain.py +7 -0
- cumulusci/core/keychain/serialization.py +152 -0
- cumulusci/core/keychain/subprocess_keychain.py +24 -0
- cumulusci/core/keychain/tests/conftest.py +50 -0
- cumulusci/core/keychain/tests/test_base_project_keychain.py +299 -0
- cumulusci/core/keychain/tests/test_encrypted_file_project_keychain.py +1228 -0
- cumulusci/core/metadeploy/__init__.py +0 -0
- cumulusci/core/metadeploy/api.py +88 -0
- cumulusci/core/metadeploy/plans.py +25 -0
- cumulusci/core/metadeploy/tests/test_api.py +276 -0
- cumulusci/core/runtime.py +115 -0
- cumulusci/core/sfdx.py +162 -0
- cumulusci/core/source/__init__.py +16 -0
- cumulusci/core/source/github.py +50 -0
- cumulusci/core/source/local_folder.py +35 -0
- cumulusci/core/source_transforms/__init__.py +0 -0
- cumulusci/core/source_transforms/tests/test_transforms.py +1091 -0
- cumulusci/core/source_transforms/transforms.py +532 -0
- cumulusci/core/tasks.py +404 -0
- cumulusci/core/template_utils.py +59 -0
- cumulusci/core/tests/__init__.py +0 -0
- cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_e2e.yaml +215 -0
- cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_extract_standard_objects.yaml +199 -0
- cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_read_explicit_extract_declaration.yaml +3 -0
- cumulusci/core/tests/fake_remote_repo/cumulusci.yml +32 -0
- cumulusci/core/tests/fake_remote_repo/tasks/directory/example_2.py +6 -0
- cumulusci/core/tests/fake_remote_repo/tasks/example.py +43 -0
- cumulusci/core/tests/fake_remote_repo_2/cumulusci.yml +11 -0
- cumulusci/core/tests/fake_remote_repo_2/tasks/example_3.py +6 -0
- cumulusci/core/tests/test_datasets_e2e.py +386 -0
- cumulusci/core/tests/test_exceptions.py +11 -0
- cumulusci/core/tests/test_flowrunner.py +836 -0
- cumulusci/core/tests/test_github.py +942 -0
- cumulusci/core/tests/test_sfdx.py +138 -0
- cumulusci/core/tests/test_source.py +678 -0
- cumulusci/core/tests/test_tasks.py +262 -0
- cumulusci/core/tests/test_utils.py +141 -0
- cumulusci/core/tests/test_utils_merge_config.py +276 -0
- cumulusci/core/tests/test_versions.py +76 -0
- cumulusci/core/tests/untrusted_repo_child/cumulusci.yml +7 -0
- cumulusci/core/tests/untrusted_repo_child/tasks/untrusted_child.py +6 -0
- cumulusci/core/tests/untrusted_repo_parent/cumulusci.yml +26 -0
- cumulusci/core/tests/untrusted_repo_parent/tasks/untrusted_parent.py +6 -0
- cumulusci/core/tests/utils.py +116 -0
- cumulusci/core/tests/yaml/global.yaml +0 -0
- cumulusci/core/utils.py +402 -0
- cumulusci/core/versions.py +149 -0
- cumulusci/cumulusci.yml +1621 -0
- cumulusci/files/admin_profile.xml +20 -0
- cumulusci/files/delete_excludes.txt +424 -0
- cumulusci/files/templates/project/README.md +12 -0
- cumulusci/files/templates/project/cumulusci.yml +63 -0
- cumulusci/files/templates/project/dot-gitignore +60 -0
- cumulusci/files/templates/project/mapping.yml +45 -0
- cumulusci/files/templates/project/scratch_def.json +25 -0
- cumulusci/oauth/__init__.py +0 -0
- cumulusci/oauth/client.py +400 -0
- cumulusci/oauth/exceptions.py +9 -0
- cumulusci/oauth/salesforce.py +95 -0
- cumulusci/oauth/tests/__init__.py +0 -0
- cumulusci/oauth/tests/cassettes/test_get_device_code.yaml +22 -0
- cumulusci/oauth/tests/cassettes/test_get_device_oauth_token.yaml +74 -0
- cumulusci/oauth/tests/test_client.py +308 -0
- cumulusci/oauth/tests/test_salesforce.py +46 -0
- cumulusci/plugins/__init__.py +3 -0
- cumulusci/plugins/plugin_base.py +93 -0
- cumulusci/plugins/plugin_loader.py +59 -0
- cumulusci/robotframework/CumulusCI.py +340 -0
- cumulusci/robotframework/CumulusCI.robot +7 -0
- cumulusci/robotframework/Performance.py +165 -0
- cumulusci/robotframework/Salesforce.py +936 -0
- cumulusci/robotframework/Salesforce.robot +192 -0
- cumulusci/robotframework/SalesforceAPI.py +416 -0
- cumulusci/robotframework/SalesforcePlaywright.py +220 -0
- cumulusci/robotframework/SalesforcePlaywright.robot +40 -0
- cumulusci/robotframework/__init__.py +2 -0
- cumulusci/robotframework/base_library.py +39 -0
- cumulusci/robotframework/faker_mixin.py +89 -0
- cumulusci/robotframework/form_handlers.py +222 -0
- cumulusci/robotframework/javascript/cci_init.js +34 -0
- cumulusci/robotframework/javascript/cumulusci.js +4 -0
- cumulusci/robotframework/locator_manager.py +197 -0
- cumulusci/robotframework/locators_56.py +88 -0
- cumulusci/robotframework/locators_57.py +5 -0
- cumulusci/robotframework/pageobjects/BasePageObjects.py +433 -0
- cumulusci/robotframework/pageobjects/ObjectManagerPageObject.py +246 -0
- cumulusci/robotframework/pageobjects/PageObjectLibrary.py +45 -0
- cumulusci/robotframework/pageobjects/PageObjects.py +351 -0
- cumulusci/robotframework/pageobjects/__init__.py +12 -0
- cumulusci/robotframework/pageobjects/baseobjects.py +120 -0
- cumulusci/robotframework/perftests/short/collection_perf.robot +105 -0
- cumulusci/robotframework/tests/CustomObjectTestPage.py +10 -0
- cumulusci/robotframework/tests/FooTestPage.py +8 -0
- cumulusci/robotframework/tests/cumulusci/base.robot +40 -0
- cumulusci/robotframework/tests/cumulusci/bulkdata.robot +38 -0
- cumulusci/robotframework/tests/cumulusci/communities.robot +57 -0
- cumulusci/robotframework/tests/cumulusci/datagen.robot +84 -0
- cumulusci/robotframework/tests/salesforce/TestLibraryA.py +24 -0
- cumulusci/robotframework/tests/salesforce/TestLibraryB.py +20 -0
- cumulusci/robotframework/tests/salesforce/TestListener.py +93 -0
- cumulusci/robotframework/tests/salesforce/api.robot +178 -0
- cumulusci/robotframework/tests/salesforce/browsers.robot +143 -0
- cumulusci/robotframework/tests/salesforce/classic.robot +51 -0
- cumulusci/robotframework/tests/salesforce/create_contact.robot +59 -0
- cumulusci/robotframework/tests/salesforce/faker.robot +68 -0
- cumulusci/robotframework/tests/salesforce/forms.robot +172 -0
- cumulusci/robotframework/tests/salesforce/label_locator.robot +244 -0
- cumulusci/robotframework/tests/salesforce/labels.html +33 -0
- cumulusci/robotframework/tests/salesforce/locators.robot +149 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/base_pageobjects.robot +100 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/example_page_object.py +25 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/listing_page.robot +115 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/objectmanager.robot +74 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/pageobjects.robot +171 -0
- cumulusci/robotframework/tests/salesforce/performance.robot +109 -0
- cumulusci/robotframework/tests/salesforce/playwright/javascript_keywords.robot +33 -0
- cumulusci/robotframework/tests/salesforce/playwright/open_test_browser.robot +48 -0
- cumulusci/robotframework/tests/salesforce/playwright/playwright.robot +24 -0
- cumulusci/robotframework/tests/salesforce/playwright/ui.robot +32 -0
- cumulusci/robotframework/tests/salesforce/populate.robot +89 -0
- cumulusci/robotframework/tests/salesforce/test_testlistener.py +37 -0
- cumulusci/robotframework/tests/salesforce/ui.robot +361 -0
- cumulusci/robotframework/tests/test_cumulusci_library.py +304 -0
- cumulusci/robotframework/tests/test_locator_manager.py +158 -0
- cumulusci/robotframework/tests/test_pageobjects.py +291 -0
- cumulusci/robotframework/tests/test_performance.py +38 -0
- cumulusci/robotframework/tests/test_salesforce.py +79 -0
- cumulusci/robotframework/tests/test_salesforce_locators.py +73 -0
- cumulusci/robotframework/tests/test_template_util.py +53 -0
- cumulusci/robotframework/tests/test_utils.py +106 -0
- cumulusci/robotframework/utils.py +283 -0
- cumulusci/salesforce_api/__init__.py +0 -0
- cumulusci/salesforce_api/exceptions.py +23 -0
- cumulusci/salesforce_api/filterable_objects.py +96 -0
- cumulusci/salesforce_api/mc_soap_envelopes.py +89 -0
- cumulusci/salesforce_api/metadata.py +721 -0
- cumulusci/salesforce_api/org_schema.py +571 -0
- cumulusci/salesforce_api/org_schema_models.py +226 -0
- cumulusci/salesforce_api/package_install.py +265 -0
- cumulusci/salesforce_api/package_zip.py +301 -0
- cumulusci/salesforce_api/rest_deploy.py +148 -0
- cumulusci/salesforce_api/retrieve_profile_api.py +301 -0
- cumulusci/salesforce_api/soap_envelopes.py +177 -0
- cumulusci/salesforce_api/tests/__init__.py +0 -0
- cumulusci/salesforce_api/tests/metadata_test_strings.py +24 -0
- cumulusci/salesforce_api/tests/test_metadata.py +1015 -0
- cumulusci/salesforce_api/tests/test_package_install.py +219 -0
- cumulusci/salesforce_api/tests/test_package_zip.py +380 -0
- cumulusci/salesforce_api/tests/test_rest_deploy.py +264 -0
- cumulusci/salesforce_api/tests/test_retrieve_profile_api.py +337 -0
- cumulusci/salesforce_api/tests/test_utils.py +124 -0
- cumulusci/salesforce_api/utils.py +51 -0
- cumulusci/schema/cumulusci.jsonschema.json +782 -0
- cumulusci/tasks/__init__.py +0 -0
- cumulusci/tasks/apex/__init__.py +0 -0
- cumulusci/tasks/apex/anon.py +157 -0
- cumulusci/tasks/apex/batch.py +180 -0
- cumulusci/tasks/apex/testrunner.py +835 -0
- cumulusci/tasks/apex/tests/cassettes/ManualEditTestApexIntegrationTests.test_run_tests__integration_test.yaml +703 -0
- cumulusci/tasks/apex/tests/test_apex_tasks.py +1558 -0
- cumulusci/tasks/base_source_control_task.py +17 -0
- cumulusci/tasks/bulkdata/__init__.py +15 -0
- cumulusci/tasks/bulkdata/base_generate_data_task.py +96 -0
- cumulusci/tasks/bulkdata/dates.py +97 -0
- cumulusci/tasks/bulkdata/delete.py +156 -0
- cumulusci/tasks/bulkdata/extract.py +441 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/calculate_dependencies.py +117 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/extract_yml.py +123 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/hardcoded_default_declarations.py +49 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/synthesize_extract_declarations.py +283 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_extract_yml.py +142 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_synthesize_extract_declarations.py +575 -0
- cumulusci/tasks/bulkdata/factory_utils.py +134 -0
- cumulusci/tasks/bulkdata/generate.py +4 -0
- cumulusci/tasks/bulkdata/generate_and_load_data.py +232 -0
- cumulusci/tasks/bulkdata/generate_and_load_data_from_yaml.py +19 -0
- cumulusci/tasks/bulkdata/generate_from_yaml.py +183 -0
- cumulusci/tasks/bulkdata/generate_mapping.py +434 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/dependency_map.py +169 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/extract_mapping_file_generator.py +45 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/generate_mapping_from_declarations.py +121 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/load_mapping_file_generator.py +127 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/mapping_generator_post_processes.py +53 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/mapping_transforms.py +139 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_generate_extract_mapping_from_declarations.py +135 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_generate_load_mapping_from_declarations.py +330 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_mapping_generator_post_processes.py +60 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_mapping_transforms.py +188 -0
- cumulusci/tasks/bulkdata/load.py +1196 -0
- cumulusci/tasks/bulkdata/mapping_parser.py +811 -0
- cumulusci/tasks/bulkdata/query_transformers.py +264 -0
- cumulusci/tasks/bulkdata/select_utils.py +792 -0
- cumulusci/tasks/bulkdata/snowfakery.py +753 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/queue_manager.py +478 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/snowfakery_run_until.py +141 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/snowfakery_working_directory.py +53 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/subtask_configurator.py +64 -0
- cumulusci/tasks/bulkdata/step.py +1242 -0
- cumulusci/tasks/bulkdata/tests/__init__.py +0 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_random_strategy.yaml +147 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_annoy_strategy.yaml +123 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_select_and_insert_strategy.yaml +313 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_select_and_insert_strategy_bulk.yaml +550 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_strategy.yaml +175 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_standard_strategy.yaml +147 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__multiple_needed.yaml +69 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__none_needed.yaml +22 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__one_needed.yaml +24 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_snowfakery_query_salesforce.yaml +25 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpdatesIntegrationTests.test_updates_task.yaml +80 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_simple_upsert__rest.yaml +270 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert__rest.yaml +267 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_external_id_field__rest.yaml +369 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_external_id_field_rest__duplicate_error.yaml +204 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_fields__bulk.yaml +675 -0
- cumulusci/tasks/bulkdata/tests/dummy_data_factory.py +36 -0
- cumulusci/tasks/bulkdata/tests/integration_test_utils.py +49 -0
- cumulusci/tasks/bulkdata/tests/mapping-oid.yml +87 -0
- cumulusci/tasks/bulkdata/tests/mapping_after.yml +38 -0
- cumulusci/tasks/bulkdata/tests/mapping_poly.yml +34 -0
- cumulusci/tasks/bulkdata/tests/mapping_poly_incomplete.yml +20 -0
- cumulusci/tasks/bulkdata/tests/mapping_poly_wrong.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select.yml +20 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_strategy.yml +20 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__invalid_number.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__invalid_strategy.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__non_float.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_missing_priority_fields.yml +22 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_no_priority_fields.yml +18 -0
- cumulusci/tasks/bulkdata/tests/mapping_simple.yml +27 -0
- cumulusci/tasks/bulkdata/tests/mapping_v1.yml +28 -0
- cumulusci/tasks/bulkdata/tests/mapping_v2.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_v3.yml +32 -0
- cumulusci/tasks/bulkdata/tests/mapping_vanilla_sf.yml +69 -0
- cumulusci/tasks/bulkdata/tests/mock_data_factory_without_mapping.py +12 -0
- cumulusci/tasks/bulkdata/tests/person_accounts.yml +23 -0
- cumulusci/tasks/bulkdata/tests/person_accounts_minimal.yml +15 -0
- cumulusci/tasks/bulkdata/tests/recordtypes.yml +8 -0
- cumulusci/tasks/bulkdata/tests/recordtypes_2.yml +6 -0
- cumulusci/tasks/bulkdata/tests/recordtypes_with_ispersontype.yml +8 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/child/child2.yml +3 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/child.yml +4 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/gen_npsp_standard_objects.recipe.yml +89 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/include_parent.yml +3 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/npsp_standard_objects_macros.yml +34 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/options.recipe.yml +6 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/query_snowfakery.recipe.yml +16 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/sf_standard_object_macros.yml +83 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery.load.yml +2 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery.recipe.yml +13 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_2.load.yml +5 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels.load.yml +13 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels.recipe.yml +12 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels_2.load.yml +13 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/unique_values.recipe.yml +4 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/upsert.recipe.yml +23 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/upsert_2.recipe.yml +29 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/upsert_before.yml +10 -0
- cumulusci/tasks/bulkdata/tests/test_base_generate_data_tasks.py +61 -0
- cumulusci/tasks/bulkdata/tests/test_dates.py +99 -0
- cumulusci/tasks/bulkdata/tests/test_delete.py +404 -0
- cumulusci/tasks/bulkdata/tests/test_extract.py +1311 -0
- cumulusci/tasks/bulkdata/tests/test_factory_utils.py +55 -0
- cumulusci/tasks/bulkdata/tests/test_generate_and_load.py +252 -0
- cumulusci/tasks/bulkdata/tests/test_generate_from_snowfakery_task.py +343 -0
- cumulusci/tasks/bulkdata/tests/test_generatemapping.py +1039 -0
- cumulusci/tasks/bulkdata/tests/test_load.py +3175 -0
- cumulusci/tasks/bulkdata/tests/test_mapping_parser.py +1658 -0
- cumulusci/tasks/bulkdata/tests/test_query_db__joins_self_lookups.yml +12 -0
- cumulusci/tasks/bulkdata/tests/test_query_db_joins_lookups.yml +26 -0
- cumulusci/tasks/bulkdata/tests/test_query_db_joins_lookups_select.yml +48 -0
- cumulusci/tasks/bulkdata/tests/test_select.py +171 -0
- cumulusci/tasks/bulkdata/tests/test_select_utils.py +1057 -0
- cumulusci/tasks/bulkdata/tests/test_snowfakery.py +1153 -0
- cumulusci/tasks/bulkdata/tests/test_step.py +3957 -0
- cumulusci/tasks/bulkdata/tests/test_updates.py +513 -0
- cumulusci/tasks/bulkdata/tests/test_upsert.py +1015 -0
- cumulusci/tasks/bulkdata/tests/test_utils.py +158 -0
- cumulusci/tasks/bulkdata/tests/testdata.db +0 -0
- cumulusci/tasks/bulkdata/tests/update_describe.py +50 -0
- cumulusci/tasks/bulkdata/tests/update_person_accounts.yml +23 -0
- cumulusci/tasks/bulkdata/tests/utils.py +114 -0
- cumulusci/tasks/bulkdata/update_data.py +260 -0
- cumulusci/tasks/bulkdata/upsert_utils.py +130 -0
- cumulusci/tasks/bulkdata/utils.py +249 -0
- cumulusci/tasks/command.py +178 -0
- cumulusci/tasks/connectedapp.py +186 -0
- cumulusci/tasks/create_package_version.py +778 -0
- cumulusci/tasks/datadictionary.py +745 -0
- cumulusci/tasks/dx_convert_from.py +26 -0
- cumulusci/tasks/github/__init__.py +17 -0
- cumulusci/tasks/github/base.py +16 -0
- cumulusci/tasks/github/commit_status.py +13 -0
- cumulusci/tasks/github/merge.py +11 -0
- cumulusci/tasks/github/publish.py +11 -0
- cumulusci/tasks/github/pull_request.py +11 -0
- cumulusci/tasks/github/release.py +11 -0
- cumulusci/tasks/github/release_report.py +11 -0
- cumulusci/tasks/github/tag.py +11 -0
- cumulusci/tasks/github/tests/__init__.py +0 -0
- cumulusci/tasks/github/tests/test_util.py +202 -0
- cumulusci/tasks/github/tests/test_vcs_migration.py +44 -0
- cumulusci/tasks/github/tests/util_github_api.py +666 -0
- cumulusci/tasks/github/util.py +252 -0
- cumulusci/tasks/marketing_cloud/__init__.py +0 -0
- cumulusci/tasks/marketing_cloud/api.py +188 -0
- cumulusci/tasks/marketing_cloud/base.py +38 -0
- cumulusci/tasks/marketing_cloud/deploy.py +345 -0
- cumulusci/tasks/marketing_cloud/get_user_info.py +40 -0
- cumulusci/tasks/marketing_cloud/mc_constants.py +1 -0
- cumulusci/tasks/marketing_cloud/tests/__init__.py +0 -0
- cumulusci/tasks/marketing_cloud/tests/conftest.py +46 -0
- cumulusci/tasks/marketing_cloud/tests/expected-payload.json +110 -0
- cumulusci/tasks/marketing_cloud/tests/test_api.py +97 -0
- cumulusci/tasks/marketing_cloud/tests/test_api_soap_envelopes.py +145 -0
- cumulusci/tasks/marketing_cloud/tests/test_base.py +14 -0
- cumulusci/tasks/marketing_cloud/tests/test_deploy.py +400 -0
- cumulusci/tasks/marketing_cloud/tests/test_get_user_info.py +141 -0
- cumulusci/tasks/marketing_cloud/tests/validation-response.json +39 -0
- cumulusci/tasks/metadata/__init__.py +0 -0
- cumulusci/tasks/metadata/ee_src.py +94 -0
- cumulusci/tasks/metadata/managed_src.py +100 -0
- cumulusci/tasks/metadata/metadata_map.yml +868 -0
- cumulusci/tasks/metadata/modify.py +99 -0
- cumulusci/tasks/metadata/package.py +684 -0
- cumulusci/tasks/metadata/tests/__init__.py +0 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/.hidden/.keep +0 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/destructiveChanges.xml +9 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/package.xml +9 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/package_install_uninstall.xml +11 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/reports/namespace__TestFolder/TestReport.report +3 -0
- cumulusci/tasks/metadata/tests/sample_package.xml +9 -0
- cumulusci/tasks/metadata/tests/test_ee_src.py +112 -0
- cumulusci/tasks/metadata/tests/test_managed_src.py +111 -0
- cumulusci/tasks/metadata/tests/test_modify.py +123 -0
- cumulusci/tasks/metadata/tests/test_package.py +476 -0
- cumulusci/tasks/metadata_etl/__init__.py +29 -0
- cumulusci/tasks/metadata_etl/base.py +436 -0
- cumulusci/tasks/metadata_etl/duplicate_rules.py +24 -0
- cumulusci/tasks/metadata_etl/field_sets.py +70 -0
- cumulusci/tasks/metadata_etl/help_text.py +92 -0
- cumulusci/tasks/metadata_etl/layouts.py +550 -0
- cumulusci/tasks/metadata_etl/objects.py +68 -0
- cumulusci/tasks/metadata_etl/permissions.py +167 -0
- cumulusci/tasks/metadata_etl/picklists.py +221 -0
- cumulusci/tasks/metadata_etl/remote_site_settings.py +99 -0
- cumulusci/tasks/metadata_etl/sharing.py +138 -0
- cumulusci/tasks/metadata_etl/tests/test_base.py +512 -0
- cumulusci/tasks/metadata_etl/tests/test_duplicate_rules.py +22 -0
- cumulusci/tasks/metadata_etl/tests/test_field_sets.py +156 -0
- cumulusci/tasks/metadata_etl/tests/test_help_text.py +387 -0
- cumulusci/tasks/metadata_etl/tests/test_ip_ranges.py +85 -0
- cumulusci/tasks/metadata_etl/tests/test_layouts.py +858 -0
- cumulusci/tasks/metadata_etl/tests/test_objects.py +236 -0
- cumulusci/tasks/metadata_etl/tests/test_permissions.py +223 -0
- cumulusci/tasks/metadata_etl/tests/test_picklists.py +547 -0
- cumulusci/tasks/metadata_etl/tests/test_remote_site_settings.py +46 -0
- cumulusci/tasks/metadata_etl/tests/test_sharing.py +333 -0
- cumulusci/tasks/metadata_etl/tests/test_value_sets.py +298 -0
- cumulusci/tasks/metadata_etl/value_sets.py +106 -0
- cumulusci/tasks/metadeploy.py +393 -0
- cumulusci/tasks/metaxml.py +88 -0
- cumulusci/tasks/preflight/__init__.py +0 -0
- cumulusci/tasks/preflight/dataset_load.py +49 -0
- cumulusci/tasks/preflight/licenses.py +86 -0
- cumulusci/tasks/preflight/packages.py +14 -0
- cumulusci/tasks/preflight/permsets.py +23 -0
- cumulusci/tasks/preflight/recordtypes.py +16 -0
- cumulusci/tasks/preflight/retrieve_tasks.py +30 -0
- cumulusci/tasks/preflight/settings.py +77 -0
- cumulusci/tasks/preflight/sobjects.py +202 -0
- cumulusci/tasks/preflight/tests/test_dataset_load.py +85 -0
- cumulusci/tasks/preflight/tests/test_licenses.py +174 -0
- cumulusci/tasks/preflight/tests/test_packages.py +14 -0
- cumulusci/tasks/preflight/tests/test_permset_preflights.py +51 -0
- cumulusci/tasks/preflight/tests/test_recordtypes.py +30 -0
- cumulusci/tasks/preflight/tests/test_retrieve_tasks.py +62 -0
- cumulusci/tasks/preflight/tests/test_settings.py +130 -0
- cumulusci/tasks/preflight/tests/test_sobjects.py +231 -0
- cumulusci/tasks/push/README.md +59 -0
- cumulusci/tasks/push/__init__.py +0 -0
- cumulusci/tasks/push/push_api.py +659 -0
- cumulusci/tasks/push/pushfails.py +136 -0
- cumulusci/tasks/push/tasks.py +476 -0
- cumulusci/tasks/push/tests/conftest.py +263 -0
- cumulusci/tasks/push/tests/test_push_api.py +951 -0
- cumulusci/tasks/push/tests/test_push_tasks.py +659 -0
- cumulusci/tasks/release_notes/README.md +63 -0
- cumulusci/tasks/release_notes/__init__.py +0 -0
- cumulusci/tasks/release_notes/exceptions.py +5 -0
- cumulusci/tasks/release_notes/generator.py +137 -0
- cumulusci/tasks/release_notes/parser.py +232 -0
- cumulusci/tasks/release_notes/provider.py +44 -0
- cumulusci/tasks/release_notes/task.py +300 -0
- cumulusci/tasks/release_notes/tests/__init__.py +0 -0
- cumulusci/tasks/release_notes/tests/change_notes/full/example1.md +17 -0
- cumulusci/tasks/release_notes/tests/change_notes/multi/1.txt +1 -0
- cumulusci/tasks/release_notes/tests/change_notes/multi/2.txt +1 -0
- cumulusci/tasks/release_notes/tests/change_notes/multi/3.txt +1 -0
- cumulusci/tasks/release_notes/tests/change_notes/single/1.txt +1 -0
- cumulusci/tasks/release_notes/tests/test_generator.py +582 -0
- cumulusci/tasks/release_notes/tests/test_parser.py +867 -0
- cumulusci/tasks/release_notes/tests/test_provider.py +512 -0
- cumulusci/tasks/release_notes/tests/test_task.py +461 -0
- cumulusci/tasks/release_notes/tests/utils.py +153 -0
- cumulusci/tasks/robotframework/__init__.py +3 -0
- cumulusci/tasks/robotframework/debugger/DebugListener.py +100 -0
- cumulusci/tasks/robotframework/debugger/__init__.py +10 -0
- cumulusci/tasks/robotframework/debugger/model.py +87 -0
- cumulusci/tasks/robotframework/debugger/ui.py +259 -0
- cumulusci/tasks/robotframework/libdoc.py +269 -0
- cumulusci/tasks/robotframework/robotframework.py +392 -0
- cumulusci/tasks/robotframework/stylesheet.css +130 -0
- cumulusci/tasks/robotframework/template.html +109 -0
- cumulusci/tasks/robotframework/tests/TestLibrary.py +18 -0
- cumulusci/tasks/robotframework/tests/TestPageObjects.py +31 -0
- cumulusci/tasks/robotframework/tests/TestResource.robot +8 -0
- cumulusci/tasks/robotframework/tests/failing_tests.robot +16 -0
- cumulusci/tasks/robotframework/tests/performance.robot +23 -0
- cumulusci/tasks/robotframework/tests/test_browser_proxies.py +137 -0
- cumulusci/tasks/robotframework/tests/test_debugger.py +360 -0
- cumulusci/tasks/robotframework/tests/test_robot_parallel.py +141 -0
- cumulusci/tasks/robotframework/tests/test_robotframework.py +860 -0
- cumulusci/tasks/salesforce/BaseRetrieveMetadata.py +58 -0
- cumulusci/tasks/salesforce/BaseSalesforceApiTask.py +45 -0
- cumulusci/tasks/salesforce/BaseSalesforceMetadataApiTask.py +18 -0
- cumulusci/tasks/salesforce/BaseSalesforceTask.py +4 -0
- cumulusci/tasks/salesforce/BaseUninstallMetadata.py +41 -0
- cumulusci/tasks/salesforce/CreateCommunity.py +124 -0
- cumulusci/tasks/salesforce/CreatePackage.py +29 -0
- cumulusci/tasks/salesforce/Deploy.py +240 -0
- cumulusci/tasks/salesforce/DeployBundles.py +88 -0
- cumulusci/tasks/salesforce/DescribeMetadataTypes.py +26 -0
- cumulusci/tasks/salesforce/EnsureRecordTypes.py +202 -0
- cumulusci/tasks/salesforce/GetInstalledPackages.py +8 -0
- cumulusci/tasks/salesforce/ListCommunities.py +40 -0
- cumulusci/tasks/salesforce/ListCommunityTemplates.py +19 -0
- cumulusci/tasks/salesforce/PublishCommunity.py +62 -0
- cumulusci/tasks/salesforce/RetrievePackaged.py +41 -0
- cumulusci/tasks/salesforce/RetrieveReportsAndDashboards.py +82 -0
- cumulusci/tasks/salesforce/RetrieveUnpackaged.py +36 -0
- cumulusci/tasks/salesforce/SOQLQuery.py +39 -0
- cumulusci/tasks/salesforce/UninstallLocal.py +15 -0
- cumulusci/tasks/salesforce/UninstallLocalBundles.py +28 -0
- cumulusci/tasks/salesforce/UninstallLocalNamespacedBundles.py +58 -0
- cumulusci/tasks/salesforce/UninstallPackage.py +32 -0
- cumulusci/tasks/salesforce/UninstallPackaged.py +56 -0
- cumulusci/tasks/salesforce/UpdateAdminProfile.py +8 -0
- cumulusci/tasks/salesforce/__init__.py +79 -0
- cumulusci/tasks/salesforce/activate_flow.py +74 -0
- cumulusci/tasks/salesforce/check_components.py +324 -0
- cumulusci/tasks/salesforce/composite.py +142 -0
- cumulusci/tasks/salesforce/create_permission_sets.py +35 -0
- cumulusci/tasks/salesforce/custom_settings.py +134 -0
- cumulusci/tasks/salesforce/custom_settings_wait.py +132 -0
- cumulusci/tasks/salesforce/enable_prediction.py +107 -0
- cumulusci/tasks/salesforce/insert_record.py +40 -0
- cumulusci/tasks/salesforce/install_package_version.py +242 -0
- cumulusci/tasks/salesforce/license_preflights.py +8 -0
- cumulusci/tasks/salesforce/network_member_group.py +178 -0
- cumulusci/tasks/salesforce/nonsourcetracking.py +228 -0
- cumulusci/tasks/salesforce/org_settings.py +193 -0
- cumulusci/tasks/salesforce/package_upload.py +328 -0
- cumulusci/tasks/salesforce/profiles.py +74 -0
- cumulusci/tasks/salesforce/promote_package_version.py +376 -0
- cumulusci/tasks/salesforce/retrieve_profile.py +195 -0
- cumulusci/tasks/salesforce/salesforce_files.py +244 -0
- cumulusci/tasks/salesforce/sourcetracking.py +507 -0
- cumulusci/tasks/salesforce/tests/__init__.py +3 -0
- cumulusci/tasks/salesforce/tests/test_CreateCommunity.py +278 -0
- cumulusci/tasks/salesforce/tests/test_CreatePackage.py +22 -0
- cumulusci/tasks/salesforce/tests/test_Deploy.py +470 -0
- cumulusci/tasks/salesforce/tests/test_DeployBundles.py +76 -0
- cumulusci/tasks/salesforce/tests/test_EnsureRecordTypes.py +345 -0
- cumulusci/tasks/salesforce/tests/test_ListCommunities.py +84 -0
- cumulusci/tasks/salesforce/tests/test_ListCommunityTemplates.py +49 -0
- cumulusci/tasks/salesforce/tests/test_PackageUpload.py +547 -0
- cumulusci/tasks/salesforce/tests/test_ProfileGrantAllAccess.py +699 -0
- cumulusci/tasks/salesforce/tests/test_PublishCommunity.py +181 -0
- cumulusci/tasks/salesforce/tests/test_RetrievePackaged.py +24 -0
- cumulusci/tasks/salesforce/tests/test_RetrieveReportsAndDashboards.py +56 -0
- cumulusci/tasks/salesforce/tests/test_RetrieveUnpackaged.py +21 -0
- cumulusci/tasks/salesforce/tests/test_SOQLQuery.py +30 -0
- cumulusci/tasks/salesforce/tests/test_UninstallLocal.py +15 -0
- cumulusci/tasks/salesforce/tests/test_UninstallLocalBundles.py +19 -0
- cumulusci/tasks/salesforce/tests/test_UninstallLocalNamespacedBundles.py +22 -0
- cumulusci/tasks/salesforce/tests/test_UninstallPackage.py +19 -0
- cumulusci/tasks/salesforce/tests/test_UninstallPackaged.py +66 -0
- cumulusci/tasks/salesforce/tests/test_UninstallPackagedIncremental.py +127 -0
- cumulusci/tasks/salesforce/tests/test_activate_flow.py +132 -0
- cumulusci/tasks/salesforce/tests/test_base_tasks.py +110 -0
- cumulusci/tasks/salesforce/tests/test_check_components.py +445 -0
- cumulusci/tasks/salesforce/tests/test_composite.py +250 -0
- cumulusci/tasks/salesforce/tests/test_create_permission_sets.py +41 -0
- cumulusci/tasks/salesforce/tests/test_custom_settings.py +227 -0
- cumulusci/tasks/salesforce/tests/test_custom_settings_wait.py +174 -0
- cumulusci/tasks/salesforce/tests/test_describemetadatatypes.py +18 -0
- cumulusci/tasks/salesforce/tests/test_enable_prediction.py +240 -0
- cumulusci/tasks/salesforce/tests/test_insert_record.py +110 -0
- cumulusci/tasks/salesforce/tests/test_install_package_version.py +464 -0
- cumulusci/tasks/salesforce/tests/test_network_member_group.py +444 -0
- cumulusci/tasks/salesforce/tests/test_nonsourcetracking.py +235 -0
- cumulusci/tasks/salesforce/tests/test_org_settings.py +407 -0
- cumulusci/tasks/salesforce/tests/test_profiles.py +202 -0
- cumulusci/tasks/salesforce/tests/test_retrieve_profile.py +287 -0
- cumulusci/tasks/salesforce/tests/test_salesforce_files.py +228 -0
- cumulusci/tasks/salesforce/tests/test_sourcetracking.py +350 -0
- cumulusci/tasks/salesforce/tests/test_trigger_handlers.py +300 -0
- cumulusci/tasks/salesforce/tests/test_update_dependencies.py +509 -0
- cumulusci/tasks/salesforce/tests/util.py +79 -0
- cumulusci/tasks/salesforce/trigger_handlers.py +119 -0
- cumulusci/tasks/salesforce/uninstall_packaged_incremental.py +136 -0
- cumulusci/tasks/salesforce/update_dependencies.py +290 -0
- cumulusci/tasks/salesforce/update_profile.py +339 -0
- cumulusci/tasks/salesforce/users/permsets.py +227 -0
- cumulusci/tasks/salesforce/users/photos.py +162 -0
- cumulusci/tasks/salesforce/users/tests/photo.mock.txt +1 -0
- cumulusci/tasks/salesforce/users/tests/test_permsets.py +950 -0
- cumulusci/tasks/salesforce/users/tests/test_photos.py +373 -0
- cumulusci/tasks/sample_data/capture_sample_data.py +77 -0
- cumulusci/tasks/sample_data/load_sample_data.py +85 -0
- cumulusci/tasks/sample_data/test_capture_sample_data.py +117 -0
- cumulusci/tasks/sample_data/test_load_sample_data.py +121 -0
- cumulusci/tasks/sfdx.py +83 -0
- cumulusci/tasks/tests/__init__.py +1 -0
- cumulusci/tasks/tests/conftest.py +30 -0
- cumulusci/tasks/tests/test_command.py +129 -0
- cumulusci/tasks/tests/test_connectedapp.py +236 -0
- cumulusci/tasks/tests/test_create_package_version.py +847 -0
- cumulusci/tasks/tests/test_datadictionary.py +1575 -0
- cumulusci/tasks/tests/test_dx_convert_from.py +60 -0
- cumulusci/tasks/tests/test_metadeploy.py +624 -0
- cumulusci/tasks/tests/test_metaxml.py +99 -0
- cumulusci/tasks/tests/test_promote_package_version.py +488 -0
- cumulusci/tasks/tests/test_pushfails.py +96 -0
- cumulusci/tasks/tests/test_salesforce.py +72 -0
- cumulusci/tasks/tests/test_sfdx.py +105 -0
- cumulusci/tasks/tests/test_util.py +207 -0
- cumulusci/tasks/util.py +261 -0
- cumulusci/tasks/vcs/__init__.py +19 -0
- cumulusci/tasks/vcs/commit_status.py +58 -0
- cumulusci/tasks/vcs/create_commit_status.py +37 -0
- cumulusci/tasks/vcs/download_extract.py +199 -0
- cumulusci/tasks/vcs/merge.py +298 -0
- cumulusci/tasks/vcs/publish.py +207 -0
- cumulusci/tasks/vcs/pull_request.py +9 -0
- cumulusci/tasks/vcs/release.py +134 -0
- cumulusci/tasks/vcs/release_report.py +105 -0
- cumulusci/tasks/vcs/tag.py +31 -0
- cumulusci/tasks/vcs/tests/github/test_commit_status.py +196 -0
- cumulusci/tasks/vcs/tests/github/test_download_extract.py +896 -0
- cumulusci/tasks/vcs/tests/github/test_merge.py +1118 -0
- cumulusci/tasks/vcs/tests/github/test_publish.py +823 -0
- cumulusci/tasks/vcs/tests/github/test_pull_request.py +29 -0
- cumulusci/tasks/vcs/tests/github/test_release.py +390 -0
- cumulusci/tasks/vcs/tests/github/test_release_report.py +109 -0
- cumulusci/tasks/vcs/tests/github/test_tag.py +90 -0
- cumulusci/tasks/vlocity/exceptions.py +2 -0
- cumulusci/tasks/vlocity/tests/test_vlocity.py +283 -0
- cumulusci/tasks/vlocity/vlocity.py +342 -0
- cumulusci/tests/__init__.py +1 -0
- cumulusci/tests/cassettes/GET_sobjects_Account_PersonAccount_describe.yaml +18 -0
- cumulusci/tests/cassettes/TestIntegrationInfrastructure.test_integration_tests.yaml +19 -0
- cumulusci/tests/pytest_plugins/pytest_sf_orgconnect.py +307 -0
- cumulusci/tests/pytest_plugins/pytest_sf_vcr.py +275 -0
- cumulusci/tests/pytest_plugins/pytest_sf_vcr_serializer.py +160 -0
- cumulusci/tests/pytest_plugins/pytest_typeguard.py +5 -0
- cumulusci/tests/pytest_plugins/test_vcr_string_compressor.py +49 -0
- cumulusci/tests/pytest_plugins/vcr_string_compressor.py +97 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Account_describe.yaml +18 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Case_describe.yaml +18 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Contact_describe.yaml +4838 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Custom__c_describe.yaml +242 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Event_describe.yaml +19 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Global_describe.yaml +1338 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Lead_describe.yaml +18 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_OpportunityContactRole_describe.yaml +34 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Opportunity_describe.yaml +1261 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Organization.yaml +49 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/batchInfoList_xml.tpl +15 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/batchInfo_xml.tpl +13 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/jobInfo_insert_xml.tpl +24 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/jobInfo_upsert_xml.tpl +25 -0
- cumulusci/tests/test_entry_points.py +20 -0
- cumulusci/tests/test_integration_infrastructure.py +131 -0
- cumulusci/tests/test_main.py +9 -0
- cumulusci/tests/test_schema.py +32 -0
- cumulusci/tests/test_utils.py +657 -0
- cumulusci/tests/test_vcr_serializer.py +134 -0
- cumulusci/tests/uncompressed_cassette.yaml +83 -0
- cumulusci/tests/util.py +344 -0
- cumulusci/utils/__init__.py +731 -0
- cumulusci/utils/classutils.py +9 -0
- cumulusci/utils/collections.py +32 -0
- cumulusci/utils/deprecation.py +11 -0
- cumulusci/utils/encryption.py +31 -0
- cumulusci/utils/fileutils.py +295 -0
- cumulusci/utils/git.py +142 -0
- cumulusci/utils/http/multi_request.py +214 -0
- cumulusci/utils/http/requests_utils.py +103 -0
- cumulusci/utils/http/tests/cassettes/ManualEditTestCompositeParallelSalesforce.test_http_headers.yaml +32 -0
- cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_composite_parallel_salesforce.yaml +65 -0
- cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_errors.yaml +24 -0
- cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_reference_ids.yaml +49 -0
- cumulusci/utils/http/tests/test_multi_request.py +255 -0
- cumulusci/utils/iterators.py +21 -0
- cumulusci/utils/logging.py +128 -0
- cumulusci/utils/metaprogramming.py +10 -0
- cumulusci/utils/options.py +138 -0
- cumulusci/utils/parallel/queries_in_parallel/run_queries_in_parallel.py +29 -0
- cumulusci/utils/parallel/queries_in_parallel/tests/test_run_queries_in_parallel.py +50 -0
- cumulusci/utils/parallel/task_worker_queues/parallel_worker.py +238 -0
- cumulusci/utils/parallel/task_worker_queues/parallel_worker_queue.py +243 -0
- cumulusci/utils/parallel/task_worker_queues/tests/test_parallel_worker.py +353 -0
- cumulusci/utils/salesforce/count_sobjects.py +46 -0
- cumulusci/utils/salesforce/soql.py +17 -0
- cumulusci/utils/salesforce/tests/cassettes/ManualEdit_TestCountSObjects.test_count_sobjects__network_errors.yaml +23 -0
- cumulusci/utils/salesforce/tests/cassettes/TestCountSObjects.test_count_sobjects__errors.yaml +33 -0
- cumulusci/utils/salesforce/tests/cassettes/TestCountSObjects.test_count_sobjects_simple.yaml +29 -0
- cumulusci/utils/salesforce/tests/test_count_sobjects.py +29 -0
- cumulusci/utils/salesforce/tests/test_soql.py +30 -0
- cumulusci/utils/tests/cassettes/ManualEditTestDescribeOrg.test_minimal_schema.yaml +36 -0
- cumulusci/utils/tests/cassettes/ManualEdit_test_describe_to_sql.yaml +191 -0
- cumulusci/utils/tests/test_fileutils.py +284 -0
- cumulusci/utils/tests/test_git.py +85 -0
- cumulusci/utils/tests/test_logging.py +70 -0
- cumulusci/utils/tests/test_option_parsing.py +188 -0
- cumulusci/utils/tests/test_org_schema.py +691 -0
- cumulusci/utils/tests/test_org_schema_models.py +79 -0
- cumulusci/utils/tests/test_waiting.py +25 -0
- cumulusci/utils/version_strings.py +391 -0
- cumulusci/utils/waiting.py +42 -0
- cumulusci/utils/xml/__init__.py +91 -0
- cumulusci/utils/xml/metadata_tree.py +299 -0
- cumulusci/utils/xml/robot_xml.py +114 -0
- cumulusci/utils/xml/salesforce_encoding.py +100 -0
- cumulusci/utils/xml/test/test_metadata_tree.py +251 -0
- cumulusci/utils/xml/test/test_salesforce_encoding.py +173 -0
- cumulusci/utils/yaml/cumulusci_yml.py +401 -0
- cumulusci/utils/yaml/model_parser.py +156 -0
- cumulusci/utils/yaml/safer_loader.py +74 -0
- cumulusci/utils/yaml/tests/bad_cci.yml +5 -0
- cumulusci/utils/yaml/tests/cassettes/TestCumulusciYml.test_validate_url__with_errors.yaml +20 -0
- cumulusci/utils/yaml/tests/test_cumulusci_yml.py +286 -0
- cumulusci/utils/yaml/tests/test_model_parser.py +175 -0
- cumulusci/utils/yaml/tests/test_safer_loader.py +88 -0
- cumulusci/utils/ziputils.py +61 -0
- cumulusci/vcs/base.py +143 -0
- cumulusci/vcs/bootstrap.py +272 -0
- cumulusci/vcs/github/__init__.py +24 -0
- cumulusci/vcs/github/adapter.py +689 -0
- cumulusci/vcs/github/release_notes/generator.py +219 -0
- cumulusci/vcs/github/release_notes/parser.py +151 -0
- cumulusci/vcs/github/release_notes/provider.py +143 -0
- cumulusci/vcs/github/service.py +569 -0
- cumulusci/vcs/github/tests/test_adapter.py +138 -0
- cumulusci/vcs/github/tests/test_service.py +408 -0
- cumulusci/vcs/models.py +586 -0
- cumulusci/vcs/tests/conftest.py +41 -0
- cumulusci/vcs/tests/dummy_service.py +241 -0
- cumulusci/vcs/tests/test_vcs_base.py +687 -0
- cumulusci/vcs/tests/test_vcs_bootstrap.py +727 -0
- cumulusci/vcs/utils/__init__.py +31 -0
- cumulusci/vcs/vcs_source.py +287 -0
- cumulusci_plus-5.0.0.dist-info/METADATA +145 -0
- cumulusci_plus-5.0.0.dist-info/RECORD +744 -0
- cumulusci_plus-5.0.0.dist-info/WHEEL +4 -0
- cumulusci_plus-5.0.0.dist-info/entry_points.txt +3 -0
- cumulusci_plus-5.0.0.dist-info/licenses/AUTHORS.rst +41 -0
- cumulusci_plus-5.0.0.dist-info/licenses/LICENSE +30 -0
|
@@ -0,0 +1,745 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
from pathlib import PurePosixPath
|
|
4
|
+
from typing import List, Optional, Union
|
|
5
|
+
from unittest.mock import Mock
|
|
6
|
+
from zipfile import ZipFile
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
from cumulusci.core.config.project_config import BaseProjectConfig
|
|
11
|
+
from cumulusci.core.dependencies import parse_dependencies
|
|
12
|
+
from cumulusci.core.dependencies.base import Dependency, DynamicDependency
|
|
13
|
+
from cumulusci.core.dependencies.resolvers import get_static_dependencies
|
|
14
|
+
from cumulusci.core.exceptions import TaskOptionsError
|
|
15
|
+
from cumulusci.core.utils import process_bool_arg
|
|
16
|
+
from cumulusci.tasks.base_source_control_task import BaseSourceControlTask
|
|
17
|
+
from cumulusci.utils import download_extract_vcs_from_repo
|
|
18
|
+
from cumulusci.utils.version_strings import LooseVersion
|
|
19
|
+
from cumulusci.utils.xml import metadata_tree
|
|
20
|
+
from cumulusci.vcs.bootstrap import get_remote_project_config, get_repo_from_url
|
|
21
|
+
from cumulusci.vcs.models import AbstractRepo
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Package(BaseModel):
|
|
25
|
+
repo: Optional[Union[AbstractRepo, Mock]]
|
|
26
|
+
package_name: str
|
|
27
|
+
namespace: str
|
|
28
|
+
prefix_release: str
|
|
29
|
+
|
|
30
|
+
def __hash__(self) -> int:
|
|
31
|
+
return self.repo.__hash__()
|
|
32
|
+
|
|
33
|
+
class Config:
|
|
34
|
+
arbitrary_types_allowed = True
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class PackageVersion(BaseModel):
|
|
38
|
+
package: Package
|
|
39
|
+
version: LooseVersion
|
|
40
|
+
|
|
41
|
+
class Config:
|
|
42
|
+
arbitrary_types_allowed = True
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SObjectDetail(BaseModel):
|
|
46
|
+
version: PackageVersion
|
|
47
|
+
api_name: str
|
|
48
|
+
label: str
|
|
49
|
+
description: str
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class FieldDetail(BaseModel):
|
|
53
|
+
version: PackageVersion
|
|
54
|
+
sobject: str
|
|
55
|
+
api_name: str
|
|
56
|
+
label: str
|
|
57
|
+
type: str
|
|
58
|
+
help_text: str
|
|
59
|
+
description: str
|
|
60
|
+
valid_values: str
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# "Version number" used to represent a prerelease.
|
|
64
|
+
PRERELEASE_SIGIL = LooseVersion("100000001.0")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class GenerateDataDictionary(BaseSourceControlTask):
|
|
68
|
+
task_docs = """
|
|
69
|
+
Generate a data dictionary for the project by walking all GitHub releases.
|
|
70
|
+
The data dictionary is output as two CSV files.
|
|
71
|
+
One, in `object_path`, includes
|
|
72
|
+
|
|
73
|
+
- Object Label
|
|
74
|
+
- Object API Name
|
|
75
|
+
- Object Description
|
|
76
|
+
- Version Introduced
|
|
77
|
+
|
|
78
|
+
with one row per packaged object.
|
|
79
|
+
|
|
80
|
+
The other, in `field_path`, includes
|
|
81
|
+
|
|
82
|
+
- Object Label
|
|
83
|
+
- Object API Name
|
|
84
|
+
- Field Label
|
|
85
|
+
- Field API Name
|
|
86
|
+
- Field Type
|
|
87
|
+
- Valid Picklist Values
|
|
88
|
+
- Help Text
|
|
89
|
+
- Field Description
|
|
90
|
+
- Version Introduced
|
|
91
|
+
- Version Picklist Values Last Changed
|
|
92
|
+
- Version Help Text Last Changed
|
|
93
|
+
|
|
94
|
+
Both MDAPI and SFDX format releases are supported.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
task_options = {
|
|
98
|
+
"object_path": {
|
|
99
|
+
"description": "Path to a CSV file to contain an sObject-level data dictionary."
|
|
100
|
+
},
|
|
101
|
+
"field_path": {
|
|
102
|
+
"description": "Path to a CSV file to contain an field-level data dictionary."
|
|
103
|
+
},
|
|
104
|
+
"include_dependencies": {
|
|
105
|
+
"description": "Process all of the GitHub dependencies of this project and "
|
|
106
|
+
"include their schema in the data dictionary.",
|
|
107
|
+
"default": True,
|
|
108
|
+
},
|
|
109
|
+
"additional_dependencies": {
|
|
110
|
+
"description": "Include schema from additional GitHub repositories that "
|
|
111
|
+
"are not explicit dependencies of this project to build a unified data dictionary. "
|
|
112
|
+
"Specify as a list of dicts as in project__dependencies in cumulusci.yml. Note: only "
|
|
113
|
+
"repository dependencies are supported."
|
|
114
|
+
},
|
|
115
|
+
"include_prerelease": {
|
|
116
|
+
"description": "Treat the current branch as containing prerelease schema, "
|
|
117
|
+
"and included it as Prerelease in the data dictionary. NOTE: this option "
|
|
118
|
+
"cannot be used with `additional_dependencies` or `include_dependencies`."
|
|
119
|
+
},
|
|
120
|
+
"include_protected_schema": {
|
|
121
|
+
"description": "Include Custom Objects, Custom Settings, and Custom Metadata "
|
|
122
|
+
"Types that are marked as Protected. Defaults to False."
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
def _init_options(self, kwargs):
|
|
127
|
+
super()._init_options(kwargs)
|
|
128
|
+
|
|
129
|
+
if self.options.get("object_path") is None:
|
|
130
|
+
self.options[
|
|
131
|
+
"object_path"
|
|
132
|
+
] = f"{self.project_config.project__name} Objects.csv"
|
|
133
|
+
|
|
134
|
+
if self.options.get("field_path") is None:
|
|
135
|
+
self.options[
|
|
136
|
+
"field_path"
|
|
137
|
+
] = f"{self.project_config.project__name} Fields.csv"
|
|
138
|
+
|
|
139
|
+
include_dependencies = self.options.get("include_dependencies")
|
|
140
|
+
self.options["include_dependencies"] = process_bool_arg(
|
|
141
|
+
True if include_dependencies is None else include_dependencies
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if "additional_dependencies" in self.options:
|
|
145
|
+
additional_deps = parse_dependencies(
|
|
146
|
+
self.options["additional_dependencies"]
|
|
147
|
+
)
|
|
148
|
+
if not all(isinstance(d, DynamicDependency) for d in additional_deps):
|
|
149
|
+
raise TaskOptionsError(
|
|
150
|
+
"Only VCS with dynamic dependencies are currently supported."
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
self.options["include_prerelease"] = process_bool_arg(
|
|
154
|
+
self.options.get("include_prerelease") or False
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if self.options["include_prerelease"]:
|
|
158
|
+
if self.options.get("additional_dependencies"):
|
|
159
|
+
raise TaskOptionsError(
|
|
160
|
+
"The additional_dependencies option cannot be used with include_prerelease."
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if self.options["include_dependencies"]:
|
|
164
|
+
self.logger.info(
|
|
165
|
+
"Setting include_prerelease prohibits include_dependencies; setting include_dependencies to False"
|
|
166
|
+
)
|
|
167
|
+
self.options["include_dependencies"] = False
|
|
168
|
+
|
|
169
|
+
self.options["include_protected_schema"] = process_bool_arg(
|
|
170
|
+
self.options.get("include_protected_schema") or False
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
def _get_repo_dependencies(
|
|
174
|
+
self, dependencies: List[DynamicDependency]
|
|
175
|
+
) -> List[Package]:
|
|
176
|
+
"""Return a list of Package objects representing all of the GitHub repositories
|
|
177
|
+
in this project's dependency tree. Ignore all non-GitHub dependencies."""
|
|
178
|
+
scm_deps = set()
|
|
179
|
+
packages = []
|
|
180
|
+
|
|
181
|
+
def log_scm(some_dep: Dependency):
|
|
182
|
+
if isinstance(some_dep, DynamicDependency):
|
|
183
|
+
scm_deps.add(some_dep)
|
|
184
|
+
|
|
185
|
+
return True
|
|
186
|
+
|
|
187
|
+
_ = get_static_dependencies(
|
|
188
|
+
self.project_config,
|
|
189
|
+
dependencies,
|
|
190
|
+
resolution_strategy="production",
|
|
191
|
+
filter_function=log_scm,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
for dependency in scm_deps:
|
|
195
|
+
repo: AbstractRepo = get_repo_from_url(self.project_config, dependency.url)
|
|
196
|
+
repo.logger = self.logger
|
|
197
|
+
|
|
198
|
+
config: BaseProjectConfig = get_remote_project_config(repo, dependency.ref)
|
|
199
|
+
package_name, namespace = BaseProjectConfig.get_package_data(config)
|
|
200
|
+
packages.append(
|
|
201
|
+
Package(
|
|
202
|
+
repo=repo,
|
|
203
|
+
package_name=package_name,
|
|
204
|
+
namespace=f"{namespace}__" if namespace else "",
|
|
205
|
+
prefix_release=config.project__git__prefix_release or "release/",
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
return packages
|
|
210
|
+
|
|
211
|
+
def _run_task(self):
|
|
212
|
+
self.logger.info("Starting data dictionary generation")
|
|
213
|
+
|
|
214
|
+
self._init_schema()
|
|
215
|
+
|
|
216
|
+
namespace = self.project_config.project__package__namespace
|
|
217
|
+
if namespace:
|
|
218
|
+
namespace = f"{namespace}__"
|
|
219
|
+
repos = [
|
|
220
|
+
Package(
|
|
221
|
+
repo=self.get_repo(),
|
|
222
|
+
package_name=self.project_config.project__package__name,
|
|
223
|
+
namespace=namespace,
|
|
224
|
+
prefix_release=self.project_config.project__git__prefix_release,
|
|
225
|
+
)
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
# Find all of our dependencies, if we're processing dependencies.
|
|
229
|
+
dependencies = []
|
|
230
|
+
if (
|
|
231
|
+
self.options["include_dependencies"]
|
|
232
|
+
and self.project_config.project__dependencies
|
|
233
|
+
):
|
|
234
|
+
parsed_deps = parse_dependencies(self.project_config.project__dependencies)
|
|
235
|
+
dependencies.extend(
|
|
236
|
+
d for d in parsed_deps if isinstance(d, DynamicDependency)
|
|
237
|
+
)
|
|
238
|
+
if "additional_dependencies" in self.options:
|
|
239
|
+
# init_options() required these to all be DynamicDependencies
|
|
240
|
+
dependencies.extend(
|
|
241
|
+
parse_dependencies(self.options["additional_dependencies"])
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
if dependencies:
|
|
245
|
+
repos.extend(self._get_repo_dependencies(dependencies))
|
|
246
|
+
|
|
247
|
+
for package in repos:
|
|
248
|
+
self._walk_releases(package)
|
|
249
|
+
|
|
250
|
+
self._write_results()
|
|
251
|
+
|
|
252
|
+
def _init_schema(self):
|
|
253
|
+
"""Initialize the structure used for schema storage."""
|
|
254
|
+
self.sobjects = defaultdict(list)
|
|
255
|
+
self.fields = defaultdict(list)
|
|
256
|
+
self.package_versions = defaultdict(list)
|
|
257
|
+
self.omit_sobjects = set()
|
|
258
|
+
|
|
259
|
+
def _walk_releases(self, package: Package):
|
|
260
|
+
"""Traverse all of the releases in this project's repository and process
|
|
261
|
+
each one matching our tag (not draft/prerelease) to generate the data dictionary."""
|
|
262
|
+
for release in package.repo.releases():
|
|
263
|
+
# Skip this release if any are true:
|
|
264
|
+
# It is a draft release
|
|
265
|
+
# It is prerelease (managed beta)
|
|
266
|
+
# This release's tag does not have the expected prefix,
|
|
267
|
+
# meaning we don't know its version number
|
|
268
|
+
if (
|
|
269
|
+
release.draft
|
|
270
|
+
or release.prerelease
|
|
271
|
+
or not release.tag_name.startswith(package.prefix_release)
|
|
272
|
+
):
|
|
273
|
+
continue
|
|
274
|
+
|
|
275
|
+
zip_file = download_extract_vcs_from_repo(
|
|
276
|
+
package.repo, ref=release.tag_name
|
|
277
|
+
)
|
|
278
|
+
version = PackageVersion(
|
|
279
|
+
package=package,
|
|
280
|
+
version=self._version_from_tag_name(
|
|
281
|
+
release.tag_name, package.prefix_release
|
|
282
|
+
),
|
|
283
|
+
)
|
|
284
|
+
self.package_versions[package].append(version.version)
|
|
285
|
+
self.logger.info(
|
|
286
|
+
f"Analyzing {package.package_name} version {version.version}"
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
self._process_zipfile(zip_file, version)
|
|
290
|
+
|
|
291
|
+
# If we are asked to process a prerelease, do so.
|
|
292
|
+
if self.options["include_prerelease"]:
|
|
293
|
+
# package.repo is guaranteed to be our repo (via _init_options())
|
|
294
|
+
zip_file = download_extract_vcs_from_repo(
|
|
295
|
+
package.repo, ref=self.project_config.repo_branch
|
|
296
|
+
)
|
|
297
|
+
version = PackageVersion(
|
|
298
|
+
package=package,
|
|
299
|
+
version=PRERELEASE_SIGIL,
|
|
300
|
+
)
|
|
301
|
+
self.package_versions[package].append(version.version)
|
|
302
|
+
self.logger.info(
|
|
303
|
+
f"Analyzing {package.package_name} prerelease from {self.project_config.repo_branch}"
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
self._process_zipfile(zip_file, version)
|
|
307
|
+
|
|
308
|
+
def _process_zipfile(self, zip_file: ZipFile, version: PackageVersion):
|
|
309
|
+
if "src/objects/" in zip_file.namelist():
|
|
310
|
+
# MDAPI format
|
|
311
|
+
self._process_mdapi_release(zip_file, version)
|
|
312
|
+
elif "sfdx-project.json" in zip_file.namelist():
|
|
313
|
+
# SFDX format
|
|
314
|
+
# Note: we check MDAPI first, because many
|
|
315
|
+
# CumulusCI projects contain an sfdx-project.json
|
|
316
|
+
# along with MDAPI source.
|
|
317
|
+
self._process_sfdx_release(zip_file, version)
|
|
318
|
+
|
|
319
|
+
def _process_mdapi_release(self, zip_file: ZipFile, version: PackageVersion):
|
|
320
|
+
"""Process an MDAPI ZIP file for objects and fields"""
|
|
321
|
+
for f in zip_file.namelist():
|
|
322
|
+
path = PurePosixPath(f)
|
|
323
|
+
if path.parent == PurePosixPath("src/objects") and path.suffix == ".object":
|
|
324
|
+
sobject_name = path.stem
|
|
325
|
+
if sobject_name.count("__") == 1:
|
|
326
|
+
sobject_name = f"{version.package.namespace}{sobject_name}"
|
|
327
|
+
|
|
328
|
+
self._process_object_element(
|
|
329
|
+
sobject_name, metadata_tree.fromstring(zip_file.read(f)), version
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
def _should_process_object(
|
|
333
|
+
self,
|
|
334
|
+
namespace: str,
|
|
335
|
+
sobject_name: str,
|
|
336
|
+
element: Optional[metadata_tree.MetadataElement],
|
|
337
|
+
):
|
|
338
|
+
"""Determine if we should track this object in the object dictionary.
|
|
339
|
+
Fields may be included regardless.
|
|
340
|
+
|
|
341
|
+
We don't process custom objects owned by other namespaces.
|
|
342
|
+
It's fine for Package A to package fields on an object owned by Package B.
|
|
343
|
+
We'll record the fields on their owning package (B) and the object on its (A)."""
|
|
344
|
+
|
|
345
|
+
return (
|
|
346
|
+
(
|
|
347
|
+
sobject_name.endswith("__c")
|
|
348
|
+
or sobject_name.endswith("__mdt")
|
|
349
|
+
or sobject_name.endswith("__e")
|
|
350
|
+
or sobject_name.count("__") == 0
|
|
351
|
+
)
|
|
352
|
+
and sobject_name.startswith(namespace)
|
|
353
|
+
and (
|
|
354
|
+
not self._is_protected_object(element)
|
|
355
|
+
or self.options["include_protected_schema"]
|
|
356
|
+
)
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
def _should_process_object_fields(
|
|
360
|
+
self, sobject_name: str, element: Optional[metadata_tree.MetadataElement]
|
|
361
|
+
) -> bool:
|
|
362
|
+
"""Determine if we should track fields from this object in the field dictionary.
|
|
363
|
+
The object itself may or may not be included.
|
|
364
|
+
|
|
365
|
+
We will track any field on a custom object, Platform Event,
|
|
366
|
+
Custom Setting, Custom Metadata Type, or standard entity.
|
|
367
|
+
We will not track any object's fields that are protected
|
|
368
|
+
in this package version."""
|
|
369
|
+
return (
|
|
370
|
+
sobject_name.endswith("__c")
|
|
371
|
+
or sobject_name.endswith("__mdt")
|
|
372
|
+
or sobject_name.endswith("__e")
|
|
373
|
+
or sobject_name.count("__") == 0
|
|
374
|
+
) and (
|
|
375
|
+
not self._is_protected_object(element)
|
|
376
|
+
or self.options["include_protected_schema"]
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
def _is_protected_object(
|
|
380
|
+
self, element: Optional[metadata_tree.MetadataElement]
|
|
381
|
+
) -> bool:
|
|
382
|
+
if not element:
|
|
383
|
+
return False
|
|
384
|
+
|
|
385
|
+
# customSettingsVisibility is used in older API versions.
|
|
386
|
+
visibility_elem = element.find("customSettingsVisibility")
|
|
387
|
+
if visibility_elem:
|
|
388
|
+
return visibility_elem.text != "Public"
|
|
389
|
+
|
|
390
|
+
visibility_elem = element.find("visibility")
|
|
391
|
+
if visibility_elem:
|
|
392
|
+
return visibility_elem.text != "Public"
|
|
393
|
+
|
|
394
|
+
return False
|
|
395
|
+
|
|
396
|
+
def _process_sfdx_release(self, zip_file: ZipFile, version: PackageVersion):
|
|
397
|
+
"""Process an SFDX ZIP file for objects and fields"""
|
|
398
|
+
for f in zip_file.namelist():
|
|
399
|
+
path = PurePosixPath(f)
|
|
400
|
+
# Be flexible about processing directories in SFDX context.
|
|
401
|
+
# This may not be optimal if the repo contains multiple
|
|
402
|
+
# 2GP package subdirectories.
|
|
403
|
+
if "objects/" in f and not f.startswith("unpackaged/"):
|
|
404
|
+
if path.suffixes == [".object-meta", ".xml"]:
|
|
405
|
+
sobject_name = path.name[: -len(".object-meta.xml")]
|
|
406
|
+
if sobject_name.count("__") == 1:
|
|
407
|
+
sobject_name = f"{version.package.namespace}{sobject_name}"
|
|
408
|
+
|
|
409
|
+
element = metadata_tree.fromstring(zip_file.read(f))
|
|
410
|
+
|
|
411
|
+
if self._should_process_object(
|
|
412
|
+
version.package.namespace, sobject_name, element
|
|
413
|
+
):
|
|
414
|
+
self._process_object_element(sobject_name, element, version)
|
|
415
|
+
else:
|
|
416
|
+
# If this is an object type from which we shouldn't process any fields,
|
|
417
|
+
# track it in omit_sobjects so we can drop any fields later if we don't have
|
|
418
|
+
# the right information at time of processing.
|
|
419
|
+
|
|
420
|
+
# Note that the owning object may be in a dependency package, so we won't find it below.
|
|
421
|
+
if not self._should_process_object_fields(
|
|
422
|
+
sobject_name, element
|
|
423
|
+
):
|
|
424
|
+
self.omit_sobjects.add(sobject_name)
|
|
425
|
+
elif path.suffixes == [".field-meta", ".xml"]:
|
|
426
|
+
# To get the sObject name, we need to remove the `/fields/SomeField.field-meta.xml`
|
|
427
|
+
# and take the last path component
|
|
428
|
+
|
|
429
|
+
# Find the sObject metadata file
|
|
430
|
+
sobject_name = f"{path.parent.parent.stem}"
|
|
431
|
+
sobject_file = str(
|
|
432
|
+
path.parent.parent / f"{sobject_name}.object-meta.xml"
|
|
433
|
+
)
|
|
434
|
+
if sobject_name.count("__") == 1:
|
|
435
|
+
sobject_name = f"{version.package.namespace}{sobject_name}"
|
|
436
|
+
|
|
437
|
+
# If the object-meta file is locatable, load it so we can check
|
|
438
|
+
# if this is a Custom Setting.
|
|
439
|
+
if sobject_file in zip_file.namelist():
|
|
440
|
+
object_entity = metadata_tree.fromstring(
|
|
441
|
+
zip_file.read(sobject_file)
|
|
442
|
+
)
|
|
443
|
+
else:
|
|
444
|
+
object_entity = None
|
|
445
|
+
|
|
446
|
+
if self._should_process_object_fields(sobject_name, object_entity):
|
|
447
|
+
self._process_field_element(
|
|
448
|
+
sobject_name,
|
|
449
|
+
metadata_tree.fromstring(zip_file.read(f)),
|
|
450
|
+
version,
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
def _process_object_element(
|
|
454
|
+
self,
|
|
455
|
+
sobject_name: str,
|
|
456
|
+
element: metadata_tree.MetadataElement,
|
|
457
|
+
version: PackageVersion,
|
|
458
|
+
):
|
|
459
|
+
"""Process a <CustomObject> metadata entity, whether SFDX or MDAPI"""
|
|
460
|
+
description_elem = getattr(element, "description", None)
|
|
461
|
+
|
|
462
|
+
# SFDX release can check this before calling us, but MDAPI can't.
|
|
463
|
+
if self._should_process_object(
|
|
464
|
+
version.package.namespace, sobject_name, element
|
|
465
|
+
):
|
|
466
|
+
if description_elem and description_elem.text:
|
|
467
|
+
description = description_elem.text
|
|
468
|
+
else:
|
|
469
|
+
description = ""
|
|
470
|
+
|
|
471
|
+
self.sobjects[sobject_name].append(
|
|
472
|
+
SObjectDetail(
|
|
473
|
+
version=version,
|
|
474
|
+
api_name=sobject_name,
|
|
475
|
+
label=element.label.text,
|
|
476
|
+
description=description,
|
|
477
|
+
)
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
# For MDAPI-format elements. No-op on SFDX.
|
|
481
|
+
if self._should_process_object_fields(sobject_name, element):
|
|
482
|
+
for field in element.findall("fields"):
|
|
483
|
+
self._process_field_element(sobject_name, field, version)
|
|
484
|
+
else:
|
|
485
|
+
# If this is an object type from which we shouldn't process any fields,
|
|
486
|
+
# track it in omit_sobjects so we can drop any fields later if we don't have
|
|
487
|
+
# the right information at time of processing.
|
|
488
|
+
|
|
489
|
+
# Note that the owning object may be in a dependency package, so we won't find it otherwise.
|
|
490
|
+
self.omit_sobjects.add(sobject_name)
|
|
491
|
+
|
|
492
|
+
def _process_field_element(
|
|
493
|
+
self,
|
|
494
|
+
sobject: str,
|
|
495
|
+
field: metadata_tree.MetadataElement,
|
|
496
|
+
version: PackageVersion,
|
|
497
|
+
):
|
|
498
|
+
"""Process a field entity, which can be either a <fields> element
|
|
499
|
+
in MDAPI format or a <CustomField> in SFDX"""
|
|
500
|
+
# `element` may be either a `fields` element (in MDAPI)
|
|
501
|
+
# or a `CustomField` (SFDX)
|
|
502
|
+
# If this is a custom field, register its presence in this version
|
|
503
|
+
if "__" in field.fullName.text:
|
|
504
|
+
field_name = f"{version.package.namespace}{field.fullName.text}"
|
|
505
|
+
# get field help text value
|
|
506
|
+
help_text_elem = field.find("inlineHelpText")
|
|
507
|
+
# get field description text value
|
|
508
|
+
description_text_elem = field.find("description")
|
|
509
|
+
|
|
510
|
+
field_type = field.type.text
|
|
511
|
+
valid_values = ""
|
|
512
|
+
|
|
513
|
+
length = ""
|
|
514
|
+
|
|
515
|
+
if not field.find("formula"):
|
|
516
|
+
if field_type in ["Text", "LongTextArea"]:
|
|
517
|
+
length = f" ({field.length.text})"
|
|
518
|
+
elif field_type == "Number":
|
|
519
|
+
length = f" ({int(field.precision.text) - int(field.scale.text)}.{field.scale.text})"
|
|
520
|
+
|
|
521
|
+
if field_type in ("Picklist", "MultiselectPicklist"):
|
|
522
|
+
# There's two different ways of storing picklist values
|
|
523
|
+
# (exclusive of Global Value Sets).
|
|
524
|
+
# <picklist> is used prior to API 38.0:
|
|
525
|
+
# https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_picklist.htm
|
|
526
|
+
# <valueSet> is used thereafter:
|
|
527
|
+
# https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_field_types.htm#meta_type_valueset
|
|
528
|
+
if field.find("valueSet") is not None:
|
|
529
|
+
# Determine if this field uses a Global Value Set.
|
|
530
|
+
value_set = field.valueSet
|
|
531
|
+
if value_set.find("valueSetName") is not None:
|
|
532
|
+
value_set_name = value_set.find("valueSetName").text
|
|
533
|
+
valid_values = f"Global Value Set {value_set_name}"
|
|
534
|
+
else:
|
|
535
|
+
valueSetDefinition = value_set.valueSetDefinition
|
|
536
|
+
labels = [
|
|
537
|
+
value.find("label").text
|
|
538
|
+
if value.find("label")
|
|
539
|
+
else value.fullName.text
|
|
540
|
+
for value in valueSetDefinition.findall("value")
|
|
541
|
+
]
|
|
542
|
+
|
|
543
|
+
valid_values = "; ".join(labels)
|
|
544
|
+
elif field.find("picklist") is not None:
|
|
545
|
+
picklist = field.picklist
|
|
546
|
+
names = [
|
|
547
|
+
value.fullName.text
|
|
548
|
+
for value in picklist.findall("picklistValues")
|
|
549
|
+
]
|
|
550
|
+
|
|
551
|
+
valid_values = "; ".join(names)
|
|
552
|
+
elif field_type == "Lookup":
|
|
553
|
+
target_sobject = field.referenceTo.text
|
|
554
|
+
if target_sobject.count("__") == 1:
|
|
555
|
+
target_sobject = f"{version.package.namespace}{target_sobject}"
|
|
556
|
+
field_type = f"Lookup to {target_sobject}"
|
|
557
|
+
# Note: polymorphic custom fields are not allowed.
|
|
558
|
+
elif field_type == "MasterDetail":
|
|
559
|
+
target_sobject = field.referenceTo.text
|
|
560
|
+
if target_sobject.count("__") == 1:
|
|
561
|
+
target_sobject = f"{version.package.namespace}{target_sobject}"
|
|
562
|
+
field_type = f"Master-Detail Relationship to {target_sobject}"
|
|
563
|
+
# Note: polymorphic custom fields are not allowed.
|
|
564
|
+
|
|
565
|
+
if help_text_elem and help_text_elem.text:
|
|
566
|
+
help_text = help_text_elem.text
|
|
567
|
+
else:
|
|
568
|
+
help_text = ""
|
|
569
|
+
|
|
570
|
+
if description_text_elem and description_text_elem.text:
|
|
571
|
+
description = description_text_elem.text
|
|
572
|
+
else:
|
|
573
|
+
description = ""
|
|
574
|
+
|
|
575
|
+
fd = FieldDetail(
|
|
576
|
+
version=version,
|
|
577
|
+
sobject=sobject,
|
|
578
|
+
api_name=field_name,
|
|
579
|
+
label=field.label.text,
|
|
580
|
+
type=f"{field_type}{length}",
|
|
581
|
+
help_text=help_text,
|
|
582
|
+
description=description,
|
|
583
|
+
valid_values=valid_values,
|
|
584
|
+
)
|
|
585
|
+
fully_qualified_name = f"{sobject}.{fd.api_name}"
|
|
586
|
+
self.fields[fully_qualified_name].append(fd)
|
|
587
|
+
|
|
588
|
+
@staticmethod
|
|
589
|
+
def _get_version_name(package_version: PackageVersion) -> str:
|
|
590
|
+
if package_version.version == PRERELEASE_SIGIL:
|
|
591
|
+
return "Prerelease"
|
|
592
|
+
return str(package_version.version)
|
|
593
|
+
|
|
594
|
+
def _write_object_results(self, file_handle):
|
|
595
|
+
"""Write to the given handle an output CSV containing the data dictionary for sObjects."""
|
|
596
|
+
writer = csv.writer(file_handle, quoting=csv.QUOTE_ALL)
|
|
597
|
+
|
|
598
|
+
writer.writerow(
|
|
599
|
+
[
|
|
600
|
+
"Object Label",
|
|
601
|
+
"Object API Name",
|
|
602
|
+
"Object Description",
|
|
603
|
+
"Version Introduced",
|
|
604
|
+
"Version Deleted",
|
|
605
|
+
]
|
|
606
|
+
)
|
|
607
|
+
|
|
608
|
+
for sobject_name, versions in self.sobjects.items():
|
|
609
|
+
# object_version.version.version yields the LooseVersion
|
|
610
|
+
# of the package version of this object version.
|
|
611
|
+
versions = sorted(
|
|
612
|
+
versions,
|
|
613
|
+
key=lambda object_version: object_version.version.version,
|
|
614
|
+
reverse=True,
|
|
615
|
+
)
|
|
616
|
+
first_version = versions[-1]
|
|
617
|
+
last_version = versions[0]
|
|
618
|
+
|
|
619
|
+
# Locate the version, if any, where this object was deleted.
|
|
620
|
+
package_versions = sorted(
|
|
621
|
+
self.package_versions[last_version.version.package]
|
|
622
|
+
)
|
|
623
|
+
if last_version.version.version != package_versions[-1]:
|
|
624
|
+
deleted_version = package_versions[
|
|
625
|
+
package_versions.index(last_version.version.version) + 1
|
|
626
|
+
]
|
|
627
|
+
else:
|
|
628
|
+
deleted_version = None
|
|
629
|
+
|
|
630
|
+
writer.writerow(
|
|
631
|
+
[
|
|
632
|
+
last_version.label,
|
|
633
|
+
sobject_name,
|
|
634
|
+
last_version.description,
|
|
635
|
+
f"{first_version.version.package.package_name} {self._get_version_name(first_version.version)}",
|
|
636
|
+
""
|
|
637
|
+
if deleted_version is None
|
|
638
|
+
else f"{first_version.version.package.package_name} {deleted_version}",
|
|
639
|
+
]
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
def _write_field_results(self, file_handle):
|
|
643
|
+
"""Write to the given handle an output CSV containing the data dictionary for fields."""
|
|
644
|
+
writer = csv.writer(file_handle, quoting=csv.QUOTE_ALL)
|
|
645
|
+
|
|
646
|
+
writer.writerow(
|
|
647
|
+
[
|
|
648
|
+
"Object Label",
|
|
649
|
+
"Object API Name",
|
|
650
|
+
"Field Label",
|
|
651
|
+
"Field API Name",
|
|
652
|
+
"Type",
|
|
653
|
+
"Picklist Values",
|
|
654
|
+
"Help Text",
|
|
655
|
+
"Field Description",
|
|
656
|
+
"Version Introduced",
|
|
657
|
+
"Version Picklist Values Last Changed",
|
|
658
|
+
"Version Help Text Last Changed",
|
|
659
|
+
"Version Deleted",
|
|
660
|
+
]
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
for _, field_versions in self.fields.items():
|
|
664
|
+
# field_version.version.version yields the LooseVersion of the package version for this field version.
|
|
665
|
+
versions = sorted(
|
|
666
|
+
field_versions,
|
|
667
|
+
key=lambda field_version: field_version.version.version,
|
|
668
|
+
reverse=True,
|
|
669
|
+
)
|
|
670
|
+
first_version = versions[-1]
|
|
671
|
+
last_version = versions[0]
|
|
672
|
+
|
|
673
|
+
if last_version.sobject in self.omit_sobjects:
|
|
674
|
+
continue
|
|
675
|
+
|
|
676
|
+
# Locate the last versions where the valid values and the help text changed.
|
|
677
|
+
valid_values_version = None
|
|
678
|
+
for (index, version) in enumerate(versions[1:]):
|
|
679
|
+
if version.valid_values != last_version.valid_values:
|
|
680
|
+
valid_values_version = versions[index]
|
|
681
|
+
break
|
|
682
|
+
|
|
683
|
+
help_text_version = None
|
|
684
|
+
for (index, version) in enumerate(versions[1:]):
|
|
685
|
+
if version.help_text != last_version.help_text:
|
|
686
|
+
help_text_version = versions[index]
|
|
687
|
+
break
|
|
688
|
+
|
|
689
|
+
# Locate the version, if any, where this field was deleted.
|
|
690
|
+
package_versions = sorted(
|
|
691
|
+
self.package_versions[last_version.version.package]
|
|
692
|
+
)
|
|
693
|
+
if last_version.version.version != package_versions[-1]:
|
|
694
|
+
deleted_version = package_versions[
|
|
695
|
+
package_versions.index(last_version.version.version) + 1
|
|
696
|
+
]
|
|
697
|
+
else:
|
|
698
|
+
deleted_version = None
|
|
699
|
+
|
|
700
|
+
# Find the sObject name, if possible, for this field.
|
|
701
|
+
if last_version.sobject in self.sobjects:
|
|
702
|
+
sobject_label = sorted(
|
|
703
|
+
self.sobjects[last_version.sobject],
|
|
704
|
+
key=lambda ver: ver.version.version,
|
|
705
|
+
reverse=True,
|
|
706
|
+
)[0].label
|
|
707
|
+
else:
|
|
708
|
+
sobject_label = last_version.sobject
|
|
709
|
+
|
|
710
|
+
writer.writerow(
|
|
711
|
+
[
|
|
712
|
+
sobject_label,
|
|
713
|
+
last_version.sobject,
|
|
714
|
+
last_version.label,
|
|
715
|
+
last_version.api_name,
|
|
716
|
+
last_version.type,
|
|
717
|
+
last_version.valid_values,
|
|
718
|
+
last_version.help_text,
|
|
719
|
+
last_version.description,
|
|
720
|
+
f"{first_version.version.package.package_name} {self._get_version_name(first_version.version)}",
|
|
721
|
+
f"{first_version.version.package.package_name} {self._get_version_name(valid_values_version.version)}"
|
|
722
|
+
if valid_values_version
|
|
723
|
+
else "",
|
|
724
|
+
f"{first_version.version.package.package_name} {self._get_version_name(help_text_version.version)}"
|
|
725
|
+
if help_text_version
|
|
726
|
+
else "",
|
|
727
|
+
""
|
|
728
|
+
if deleted_version is None
|
|
729
|
+
else f"{first_version.version.package.package_name} {deleted_version}",
|
|
730
|
+
]
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
def _write_results(self):
|
|
734
|
+
"""Write the stored schema details to our destination CSVs"""
|
|
735
|
+
with open(self.options["object_path"], "w") as object_file:
|
|
736
|
+
self._write_object_results(object_file)
|
|
737
|
+
|
|
738
|
+
with open(self.options["field_path"], "w") as field_file:
|
|
739
|
+
self._write_field_results(field_file)
|
|
740
|
+
|
|
741
|
+
def _version_from_tag_name(
|
|
742
|
+
self, tag_name: str, prefix_release: str
|
|
743
|
+
) -> LooseVersion:
|
|
744
|
+
"""Parse a release's tag and return a LooseVersion"""
|
|
745
|
+
return LooseVersion(tag_name[len(prefix_release) :])
|