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.

Files changed (744) hide show
  1. cumulusci/__about__.py +1 -0
  2. cumulusci/__init__.py +22 -0
  3. cumulusci/__main__.py +3 -0
  4. cumulusci/cli/__init__.py +0 -0
  5. cumulusci/cli/cci.py +244 -0
  6. cumulusci/cli/error.py +125 -0
  7. cumulusci/cli/flow.py +185 -0
  8. cumulusci/cli/logger.py +72 -0
  9. cumulusci/cli/org.py +692 -0
  10. cumulusci/cli/plan.py +181 -0
  11. cumulusci/cli/project.py +391 -0
  12. cumulusci/cli/robot.py +116 -0
  13. cumulusci/cli/runtime.py +190 -0
  14. cumulusci/cli/service.py +521 -0
  15. cumulusci/cli/task.py +295 -0
  16. cumulusci/cli/tests/__init__.py +0 -0
  17. cumulusci/cli/tests/test_cci.py +545 -0
  18. cumulusci/cli/tests/test_error.py +170 -0
  19. cumulusci/cli/tests/test_flow.py +276 -0
  20. cumulusci/cli/tests/test_logger.py +25 -0
  21. cumulusci/cli/tests/test_org.py +1438 -0
  22. cumulusci/cli/tests/test_plan.py +245 -0
  23. cumulusci/cli/tests/test_project.py +235 -0
  24. cumulusci/cli/tests/test_robot.py +177 -0
  25. cumulusci/cli/tests/test_runtime.py +197 -0
  26. cumulusci/cli/tests/test_service.py +853 -0
  27. cumulusci/cli/tests/test_task.py +266 -0
  28. cumulusci/cli/tests/test_ui.py +310 -0
  29. cumulusci/cli/tests/test_utils.py +122 -0
  30. cumulusci/cli/tests/utils.py +52 -0
  31. cumulusci/cli/ui.py +234 -0
  32. cumulusci/cli/utils.py +150 -0
  33. cumulusci/conftest.py +181 -0
  34. cumulusci/core/__init__.py +0 -0
  35. cumulusci/core/config/BaseConfig.py +5 -0
  36. cumulusci/core/config/BaseTaskFlowConfig.py +5 -0
  37. cumulusci/core/config/OrgConfig.py +5 -0
  38. cumulusci/core/config/ScratchOrgConfig.py +5 -0
  39. cumulusci/core/config/__init__.py +125 -0
  40. cumulusci/core/config/base_config.py +111 -0
  41. cumulusci/core/config/base_task_flow_config.py +82 -0
  42. cumulusci/core/config/marketing_cloud_service_config.py +83 -0
  43. cumulusci/core/config/oauth2_service_config.py +17 -0
  44. cumulusci/core/config/org_config.py +604 -0
  45. cumulusci/core/config/project_config.py +782 -0
  46. cumulusci/core/config/scratch_org_config.py +251 -0
  47. cumulusci/core/config/sfdx_org_config.py +220 -0
  48. cumulusci/core/config/tests/_test_config_backwards_compatibility.py +33 -0
  49. cumulusci/core/config/tests/test_config.py +1895 -0
  50. cumulusci/core/config/tests/test_config_expensive.py +839 -0
  51. cumulusci/core/config/tests/test_config_util.py +91 -0
  52. cumulusci/core/config/universal_config.py +88 -0
  53. cumulusci/core/config/util.py +18 -0
  54. cumulusci/core/datasets.py +303 -0
  55. cumulusci/core/debug.py +33 -0
  56. cumulusci/core/dependencies/__init__.py +55 -0
  57. cumulusci/core/dependencies/base.py +561 -0
  58. cumulusci/core/dependencies/dependencies.py +273 -0
  59. cumulusci/core/dependencies/github.py +177 -0
  60. cumulusci/core/dependencies/github_resolvers.py +244 -0
  61. cumulusci/core/dependencies/resolvers.py +580 -0
  62. cumulusci/core/dependencies/tests/__init__.py +0 -0
  63. cumulusci/core/dependencies/tests/conftest.py +385 -0
  64. cumulusci/core/dependencies/tests/test_dependencies.py +950 -0
  65. cumulusci/core/dependencies/tests/test_github.py +83 -0
  66. cumulusci/core/dependencies/tests/test_resolvers.py +1027 -0
  67. cumulusci/core/dependencies/utils.py +13 -0
  68. cumulusci/core/enums.py +11 -0
  69. cumulusci/core/exceptions.py +311 -0
  70. cumulusci/core/flowrunner.py +888 -0
  71. cumulusci/core/github.py +665 -0
  72. cumulusci/core/keychain/__init__.py +24 -0
  73. cumulusci/core/keychain/base_project_keychain.py +441 -0
  74. cumulusci/core/keychain/encrypted_file_project_keychain.py +945 -0
  75. cumulusci/core/keychain/environment_project_keychain.py +7 -0
  76. cumulusci/core/keychain/serialization.py +152 -0
  77. cumulusci/core/keychain/subprocess_keychain.py +24 -0
  78. cumulusci/core/keychain/tests/conftest.py +50 -0
  79. cumulusci/core/keychain/tests/test_base_project_keychain.py +299 -0
  80. cumulusci/core/keychain/tests/test_encrypted_file_project_keychain.py +1228 -0
  81. cumulusci/core/metadeploy/__init__.py +0 -0
  82. cumulusci/core/metadeploy/api.py +88 -0
  83. cumulusci/core/metadeploy/plans.py +25 -0
  84. cumulusci/core/metadeploy/tests/test_api.py +276 -0
  85. cumulusci/core/runtime.py +115 -0
  86. cumulusci/core/sfdx.py +162 -0
  87. cumulusci/core/source/__init__.py +16 -0
  88. cumulusci/core/source/github.py +50 -0
  89. cumulusci/core/source/local_folder.py +35 -0
  90. cumulusci/core/source_transforms/__init__.py +0 -0
  91. cumulusci/core/source_transforms/tests/test_transforms.py +1091 -0
  92. cumulusci/core/source_transforms/transforms.py +532 -0
  93. cumulusci/core/tasks.py +404 -0
  94. cumulusci/core/template_utils.py +59 -0
  95. cumulusci/core/tests/__init__.py +0 -0
  96. cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_e2e.yaml +215 -0
  97. cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_extract_standard_objects.yaml +199 -0
  98. cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_read_explicit_extract_declaration.yaml +3 -0
  99. cumulusci/core/tests/fake_remote_repo/cumulusci.yml +32 -0
  100. cumulusci/core/tests/fake_remote_repo/tasks/directory/example_2.py +6 -0
  101. cumulusci/core/tests/fake_remote_repo/tasks/example.py +43 -0
  102. cumulusci/core/tests/fake_remote_repo_2/cumulusci.yml +11 -0
  103. cumulusci/core/tests/fake_remote_repo_2/tasks/example_3.py +6 -0
  104. cumulusci/core/tests/test_datasets_e2e.py +386 -0
  105. cumulusci/core/tests/test_exceptions.py +11 -0
  106. cumulusci/core/tests/test_flowrunner.py +836 -0
  107. cumulusci/core/tests/test_github.py +942 -0
  108. cumulusci/core/tests/test_sfdx.py +138 -0
  109. cumulusci/core/tests/test_source.py +678 -0
  110. cumulusci/core/tests/test_tasks.py +262 -0
  111. cumulusci/core/tests/test_utils.py +141 -0
  112. cumulusci/core/tests/test_utils_merge_config.py +276 -0
  113. cumulusci/core/tests/test_versions.py +76 -0
  114. cumulusci/core/tests/untrusted_repo_child/cumulusci.yml +7 -0
  115. cumulusci/core/tests/untrusted_repo_child/tasks/untrusted_child.py +6 -0
  116. cumulusci/core/tests/untrusted_repo_parent/cumulusci.yml +26 -0
  117. cumulusci/core/tests/untrusted_repo_parent/tasks/untrusted_parent.py +6 -0
  118. cumulusci/core/tests/utils.py +116 -0
  119. cumulusci/core/tests/yaml/global.yaml +0 -0
  120. cumulusci/core/utils.py +402 -0
  121. cumulusci/core/versions.py +149 -0
  122. cumulusci/cumulusci.yml +1621 -0
  123. cumulusci/files/admin_profile.xml +20 -0
  124. cumulusci/files/delete_excludes.txt +424 -0
  125. cumulusci/files/templates/project/README.md +12 -0
  126. cumulusci/files/templates/project/cumulusci.yml +63 -0
  127. cumulusci/files/templates/project/dot-gitignore +60 -0
  128. cumulusci/files/templates/project/mapping.yml +45 -0
  129. cumulusci/files/templates/project/scratch_def.json +25 -0
  130. cumulusci/oauth/__init__.py +0 -0
  131. cumulusci/oauth/client.py +400 -0
  132. cumulusci/oauth/exceptions.py +9 -0
  133. cumulusci/oauth/salesforce.py +95 -0
  134. cumulusci/oauth/tests/__init__.py +0 -0
  135. cumulusci/oauth/tests/cassettes/test_get_device_code.yaml +22 -0
  136. cumulusci/oauth/tests/cassettes/test_get_device_oauth_token.yaml +74 -0
  137. cumulusci/oauth/tests/test_client.py +308 -0
  138. cumulusci/oauth/tests/test_salesforce.py +46 -0
  139. cumulusci/plugins/__init__.py +3 -0
  140. cumulusci/plugins/plugin_base.py +93 -0
  141. cumulusci/plugins/plugin_loader.py +59 -0
  142. cumulusci/robotframework/CumulusCI.py +340 -0
  143. cumulusci/robotframework/CumulusCI.robot +7 -0
  144. cumulusci/robotframework/Performance.py +165 -0
  145. cumulusci/robotframework/Salesforce.py +936 -0
  146. cumulusci/robotframework/Salesforce.robot +192 -0
  147. cumulusci/robotframework/SalesforceAPI.py +416 -0
  148. cumulusci/robotframework/SalesforcePlaywright.py +220 -0
  149. cumulusci/robotframework/SalesforcePlaywright.robot +40 -0
  150. cumulusci/robotframework/__init__.py +2 -0
  151. cumulusci/robotframework/base_library.py +39 -0
  152. cumulusci/robotframework/faker_mixin.py +89 -0
  153. cumulusci/robotframework/form_handlers.py +222 -0
  154. cumulusci/robotframework/javascript/cci_init.js +34 -0
  155. cumulusci/robotframework/javascript/cumulusci.js +4 -0
  156. cumulusci/robotframework/locator_manager.py +197 -0
  157. cumulusci/robotframework/locators_56.py +88 -0
  158. cumulusci/robotframework/locators_57.py +5 -0
  159. cumulusci/robotframework/pageobjects/BasePageObjects.py +433 -0
  160. cumulusci/robotframework/pageobjects/ObjectManagerPageObject.py +246 -0
  161. cumulusci/robotframework/pageobjects/PageObjectLibrary.py +45 -0
  162. cumulusci/robotframework/pageobjects/PageObjects.py +351 -0
  163. cumulusci/robotframework/pageobjects/__init__.py +12 -0
  164. cumulusci/robotframework/pageobjects/baseobjects.py +120 -0
  165. cumulusci/robotframework/perftests/short/collection_perf.robot +105 -0
  166. cumulusci/robotframework/tests/CustomObjectTestPage.py +10 -0
  167. cumulusci/robotframework/tests/FooTestPage.py +8 -0
  168. cumulusci/robotframework/tests/cumulusci/base.robot +40 -0
  169. cumulusci/robotframework/tests/cumulusci/bulkdata.robot +38 -0
  170. cumulusci/robotframework/tests/cumulusci/communities.robot +57 -0
  171. cumulusci/robotframework/tests/cumulusci/datagen.robot +84 -0
  172. cumulusci/robotframework/tests/salesforce/TestLibraryA.py +24 -0
  173. cumulusci/robotframework/tests/salesforce/TestLibraryB.py +20 -0
  174. cumulusci/robotframework/tests/salesforce/TestListener.py +93 -0
  175. cumulusci/robotframework/tests/salesforce/api.robot +178 -0
  176. cumulusci/robotframework/tests/salesforce/browsers.robot +143 -0
  177. cumulusci/robotframework/tests/salesforce/classic.robot +51 -0
  178. cumulusci/robotframework/tests/salesforce/create_contact.robot +59 -0
  179. cumulusci/robotframework/tests/salesforce/faker.robot +68 -0
  180. cumulusci/robotframework/tests/salesforce/forms.robot +172 -0
  181. cumulusci/robotframework/tests/salesforce/label_locator.robot +244 -0
  182. cumulusci/robotframework/tests/salesforce/labels.html +33 -0
  183. cumulusci/robotframework/tests/salesforce/locators.robot +149 -0
  184. cumulusci/robotframework/tests/salesforce/pageobjects/base_pageobjects.robot +100 -0
  185. cumulusci/robotframework/tests/salesforce/pageobjects/example_page_object.py +25 -0
  186. cumulusci/robotframework/tests/salesforce/pageobjects/listing_page.robot +115 -0
  187. cumulusci/robotframework/tests/salesforce/pageobjects/objectmanager.robot +74 -0
  188. cumulusci/robotframework/tests/salesforce/pageobjects/pageobjects.robot +171 -0
  189. cumulusci/robotframework/tests/salesforce/performance.robot +109 -0
  190. cumulusci/robotframework/tests/salesforce/playwright/javascript_keywords.robot +33 -0
  191. cumulusci/robotframework/tests/salesforce/playwright/open_test_browser.robot +48 -0
  192. cumulusci/robotframework/tests/salesforce/playwright/playwright.robot +24 -0
  193. cumulusci/robotframework/tests/salesforce/playwright/ui.robot +32 -0
  194. cumulusci/robotframework/tests/salesforce/populate.robot +89 -0
  195. cumulusci/robotframework/tests/salesforce/test_testlistener.py +37 -0
  196. cumulusci/robotframework/tests/salesforce/ui.robot +361 -0
  197. cumulusci/robotframework/tests/test_cumulusci_library.py +304 -0
  198. cumulusci/robotframework/tests/test_locator_manager.py +158 -0
  199. cumulusci/robotframework/tests/test_pageobjects.py +291 -0
  200. cumulusci/robotframework/tests/test_performance.py +38 -0
  201. cumulusci/robotframework/tests/test_salesforce.py +79 -0
  202. cumulusci/robotframework/tests/test_salesforce_locators.py +73 -0
  203. cumulusci/robotframework/tests/test_template_util.py +53 -0
  204. cumulusci/robotframework/tests/test_utils.py +106 -0
  205. cumulusci/robotframework/utils.py +283 -0
  206. cumulusci/salesforce_api/__init__.py +0 -0
  207. cumulusci/salesforce_api/exceptions.py +23 -0
  208. cumulusci/salesforce_api/filterable_objects.py +96 -0
  209. cumulusci/salesforce_api/mc_soap_envelopes.py +89 -0
  210. cumulusci/salesforce_api/metadata.py +721 -0
  211. cumulusci/salesforce_api/org_schema.py +571 -0
  212. cumulusci/salesforce_api/org_schema_models.py +226 -0
  213. cumulusci/salesforce_api/package_install.py +265 -0
  214. cumulusci/salesforce_api/package_zip.py +301 -0
  215. cumulusci/salesforce_api/rest_deploy.py +148 -0
  216. cumulusci/salesforce_api/retrieve_profile_api.py +301 -0
  217. cumulusci/salesforce_api/soap_envelopes.py +177 -0
  218. cumulusci/salesforce_api/tests/__init__.py +0 -0
  219. cumulusci/salesforce_api/tests/metadata_test_strings.py +24 -0
  220. cumulusci/salesforce_api/tests/test_metadata.py +1015 -0
  221. cumulusci/salesforce_api/tests/test_package_install.py +219 -0
  222. cumulusci/salesforce_api/tests/test_package_zip.py +380 -0
  223. cumulusci/salesforce_api/tests/test_rest_deploy.py +264 -0
  224. cumulusci/salesforce_api/tests/test_retrieve_profile_api.py +337 -0
  225. cumulusci/salesforce_api/tests/test_utils.py +124 -0
  226. cumulusci/salesforce_api/utils.py +51 -0
  227. cumulusci/schema/cumulusci.jsonschema.json +782 -0
  228. cumulusci/tasks/__init__.py +0 -0
  229. cumulusci/tasks/apex/__init__.py +0 -0
  230. cumulusci/tasks/apex/anon.py +157 -0
  231. cumulusci/tasks/apex/batch.py +180 -0
  232. cumulusci/tasks/apex/testrunner.py +835 -0
  233. cumulusci/tasks/apex/tests/cassettes/ManualEditTestApexIntegrationTests.test_run_tests__integration_test.yaml +703 -0
  234. cumulusci/tasks/apex/tests/test_apex_tasks.py +1558 -0
  235. cumulusci/tasks/base_source_control_task.py +17 -0
  236. cumulusci/tasks/bulkdata/__init__.py +15 -0
  237. cumulusci/tasks/bulkdata/base_generate_data_task.py +96 -0
  238. cumulusci/tasks/bulkdata/dates.py +97 -0
  239. cumulusci/tasks/bulkdata/delete.py +156 -0
  240. cumulusci/tasks/bulkdata/extract.py +441 -0
  241. cumulusci/tasks/bulkdata/extract_dataset_utils/calculate_dependencies.py +117 -0
  242. cumulusci/tasks/bulkdata/extract_dataset_utils/extract_yml.py +123 -0
  243. cumulusci/tasks/bulkdata/extract_dataset_utils/hardcoded_default_declarations.py +49 -0
  244. cumulusci/tasks/bulkdata/extract_dataset_utils/synthesize_extract_declarations.py +283 -0
  245. cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_extract_yml.py +142 -0
  246. cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_synthesize_extract_declarations.py +575 -0
  247. cumulusci/tasks/bulkdata/factory_utils.py +134 -0
  248. cumulusci/tasks/bulkdata/generate.py +4 -0
  249. cumulusci/tasks/bulkdata/generate_and_load_data.py +232 -0
  250. cumulusci/tasks/bulkdata/generate_and_load_data_from_yaml.py +19 -0
  251. cumulusci/tasks/bulkdata/generate_from_yaml.py +183 -0
  252. cumulusci/tasks/bulkdata/generate_mapping.py +434 -0
  253. cumulusci/tasks/bulkdata/generate_mapping_utils/dependency_map.py +169 -0
  254. cumulusci/tasks/bulkdata/generate_mapping_utils/extract_mapping_file_generator.py +45 -0
  255. cumulusci/tasks/bulkdata/generate_mapping_utils/generate_mapping_from_declarations.py +121 -0
  256. cumulusci/tasks/bulkdata/generate_mapping_utils/load_mapping_file_generator.py +127 -0
  257. cumulusci/tasks/bulkdata/generate_mapping_utils/mapping_generator_post_processes.py +53 -0
  258. cumulusci/tasks/bulkdata/generate_mapping_utils/mapping_transforms.py +139 -0
  259. cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_generate_extract_mapping_from_declarations.py +135 -0
  260. cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_generate_load_mapping_from_declarations.py +330 -0
  261. cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_mapping_generator_post_processes.py +60 -0
  262. cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_mapping_transforms.py +188 -0
  263. cumulusci/tasks/bulkdata/load.py +1196 -0
  264. cumulusci/tasks/bulkdata/mapping_parser.py +811 -0
  265. cumulusci/tasks/bulkdata/query_transformers.py +264 -0
  266. cumulusci/tasks/bulkdata/select_utils.py +792 -0
  267. cumulusci/tasks/bulkdata/snowfakery.py +753 -0
  268. cumulusci/tasks/bulkdata/snowfakery_utils/queue_manager.py +478 -0
  269. cumulusci/tasks/bulkdata/snowfakery_utils/snowfakery_run_until.py +141 -0
  270. cumulusci/tasks/bulkdata/snowfakery_utils/snowfakery_working_directory.py +53 -0
  271. cumulusci/tasks/bulkdata/snowfakery_utils/subtask_configurator.py +64 -0
  272. cumulusci/tasks/bulkdata/step.py +1242 -0
  273. cumulusci/tasks/bulkdata/tests/__init__.py +0 -0
  274. cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_random_strategy.yaml +147 -0
  275. cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_annoy_strategy.yaml +123 -0
  276. cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_select_and_insert_strategy.yaml +313 -0
  277. cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_select_and_insert_strategy_bulk.yaml +550 -0
  278. cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_strategy.yaml +175 -0
  279. cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_standard_strategy.yaml +147 -0
  280. cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__multiple_needed.yaml +69 -0
  281. cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__none_needed.yaml +22 -0
  282. cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__one_needed.yaml +24 -0
  283. cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_snowfakery_query_salesforce.yaml +25 -0
  284. cumulusci/tasks/bulkdata/tests/cassettes/TestUpdatesIntegrationTests.test_updates_task.yaml +80 -0
  285. cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_simple_upsert__rest.yaml +270 -0
  286. cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert__rest.yaml +267 -0
  287. cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_external_id_field__rest.yaml +369 -0
  288. cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_external_id_field_rest__duplicate_error.yaml +204 -0
  289. cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_fields__bulk.yaml +675 -0
  290. cumulusci/tasks/bulkdata/tests/dummy_data_factory.py +36 -0
  291. cumulusci/tasks/bulkdata/tests/integration_test_utils.py +49 -0
  292. cumulusci/tasks/bulkdata/tests/mapping-oid.yml +87 -0
  293. cumulusci/tasks/bulkdata/tests/mapping_after.yml +38 -0
  294. cumulusci/tasks/bulkdata/tests/mapping_poly.yml +34 -0
  295. cumulusci/tasks/bulkdata/tests/mapping_poly_incomplete.yml +20 -0
  296. cumulusci/tasks/bulkdata/tests/mapping_poly_wrong.yml +21 -0
  297. cumulusci/tasks/bulkdata/tests/mapping_select.yml +20 -0
  298. cumulusci/tasks/bulkdata/tests/mapping_select_invalid_strategy.yml +20 -0
  299. cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__invalid_number.yml +21 -0
  300. cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__invalid_strategy.yml +21 -0
  301. cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__non_float.yml +21 -0
  302. cumulusci/tasks/bulkdata/tests/mapping_select_missing_priority_fields.yml +22 -0
  303. cumulusci/tasks/bulkdata/tests/mapping_select_no_priority_fields.yml +18 -0
  304. cumulusci/tasks/bulkdata/tests/mapping_simple.yml +27 -0
  305. cumulusci/tasks/bulkdata/tests/mapping_v1.yml +28 -0
  306. cumulusci/tasks/bulkdata/tests/mapping_v2.yml +21 -0
  307. cumulusci/tasks/bulkdata/tests/mapping_v3.yml +32 -0
  308. cumulusci/tasks/bulkdata/tests/mapping_vanilla_sf.yml +69 -0
  309. cumulusci/tasks/bulkdata/tests/mock_data_factory_without_mapping.py +12 -0
  310. cumulusci/tasks/bulkdata/tests/person_accounts.yml +23 -0
  311. cumulusci/tasks/bulkdata/tests/person_accounts_minimal.yml +15 -0
  312. cumulusci/tasks/bulkdata/tests/recordtypes.yml +8 -0
  313. cumulusci/tasks/bulkdata/tests/recordtypes_2.yml +6 -0
  314. cumulusci/tasks/bulkdata/tests/recordtypes_with_ispersontype.yml +8 -0
  315. cumulusci/tasks/bulkdata/tests/snowfakery/child/child2.yml +3 -0
  316. cumulusci/tasks/bulkdata/tests/snowfakery/child.yml +4 -0
  317. cumulusci/tasks/bulkdata/tests/snowfakery/gen_npsp_standard_objects.recipe.yml +89 -0
  318. cumulusci/tasks/bulkdata/tests/snowfakery/include_parent.yml +3 -0
  319. cumulusci/tasks/bulkdata/tests/snowfakery/npsp_standard_objects_macros.yml +34 -0
  320. cumulusci/tasks/bulkdata/tests/snowfakery/options.recipe.yml +6 -0
  321. cumulusci/tasks/bulkdata/tests/snowfakery/query_snowfakery.recipe.yml +16 -0
  322. cumulusci/tasks/bulkdata/tests/snowfakery/sf_standard_object_macros.yml +83 -0
  323. cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery.load.yml +2 -0
  324. cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery.recipe.yml +13 -0
  325. cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_2.load.yml +5 -0
  326. cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels.load.yml +13 -0
  327. cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels.recipe.yml +12 -0
  328. cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels_2.load.yml +13 -0
  329. cumulusci/tasks/bulkdata/tests/snowfakery/unique_values.recipe.yml +4 -0
  330. cumulusci/tasks/bulkdata/tests/snowfakery/upsert.recipe.yml +23 -0
  331. cumulusci/tasks/bulkdata/tests/snowfakery/upsert_2.recipe.yml +29 -0
  332. cumulusci/tasks/bulkdata/tests/snowfakery/upsert_before.yml +10 -0
  333. cumulusci/tasks/bulkdata/tests/test_base_generate_data_tasks.py +61 -0
  334. cumulusci/tasks/bulkdata/tests/test_dates.py +99 -0
  335. cumulusci/tasks/bulkdata/tests/test_delete.py +404 -0
  336. cumulusci/tasks/bulkdata/tests/test_extract.py +1311 -0
  337. cumulusci/tasks/bulkdata/tests/test_factory_utils.py +55 -0
  338. cumulusci/tasks/bulkdata/tests/test_generate_and_load.py +252 -0
  339. cumulusci/tasks/bulkdata/tests/test_generate_from_snowfakery_task.py +343 -0
  340. cumulusci/tasks/bulkdata/tests/test_generatemapping.py +1039 -0
  341. cumulusci/tasks/bulkdata/tests/test_load.py +3175 -0
  342. cumulusci/tasks/bulkdata/tests/test_mapping_parser.py +1658 -0
  343. cumulusci/tasks/bulkdata/tests/test_query_db__joins_self_lookups.yml +12 -0
  344. cumulusci/tasks/bulkdata/tests/test_query_db_joins_lookups.yml +26 -0
  345. cumulusci/tasks/bulkdata/tests/test_query_db_joins_lookups_select.yml +48 -0
  346. cumulusci/tasks/bulkdata/tests/test_select.py +171 -0
  347. cumulusci/tasks/bulkdata/tests/test_select_utils.py +1057 -0
  348. cumulusci/tasks/bulkdata/tests/test_snowfakery.py +1153 -0
  349. cumulusci/tasks/bulkdata/tests/test_step.py +3957 -0
  350. cumulusci/tasks/bulkdata/tests/test_updates.py +513 -0
  351. cumulusci/tasks/bulkdata/tests/test_upsert.py +1015 -0
  352. cumulusci/tasks/bulkdata/tests/test_utils.py +158 -0
  353. cumulusci/tasks/bulkdata/tests/testdata.db +0 -0
  354. cumulusci/tasks/bulkdata/tests/update_describe.py +50 -0
  355. cumulusci/tasks/bulkdata/tests/update_person_accounts.yml +23 -0
  356. cumulusci/tasks/bulkdata/tests/utils.py +114 -0
  357. cumulusci/tasks/bulkdata/update_data.py +260 -0
  358. cumulusci/tasks/bulkdata/upsert_utils.py +130 -0
  359. cumulusci/tasks/bulkdata/utils.py +249 -0
  360. cumulusci/tasks/command.py +178 -0
  361. cumulusci/tasks/connectedapp.py +186 -0
  362. cumulusci/tasks/create_package_version.py +778 -0
  363. cumulusci/tasks/datadictionary.py +745 -0
  364. cumulusci/tasks/dx_convert_from.py +26 -0
  365. cumulusci/tasks/github/__init__.py +17 -0
  366. cumulusci/tasks/github/base.py +16 -0
  367. cumulusci/tasks/github/commit_status.py +13 -0
  368. cumulusci/tasks/github/merge.py +11 -0
  369. cumulusci/tasks/github/publish.py +11 -0
  370. cumulusci/tasks/github/pull_request.py +11 -0
  371. cumulusci/tasks/github/release.py +11 -0
  372. cumulusci/tasks/github/release_report.py +11 -0
  373. cumulusci/tasks/github/tag.py +11 -0
  374. cumulusci/tasks/github/tests/__init__.py +0 -0
  375. cumulusci/tasks/github/tests/test_util.py +202 -0
  376. cumulusci/tasks/github/tests/test_vcs_migration.py +44 -0
  377. cumulusci/tasks/github/tests/util_github_api.py +666 -0
  378. cumulusci/tasks/github/util.py +252 -0
  379. cumulusci/tasks/marketing_cloud/__init__.py +0 -0
  380. cumulusci/tasks/marketing_cloud/api.py +188 -0
  381. cumulusci/tasks/marketing_cloud/base.py +38 -0
  382. cumulusci/tasks/marketing_cloud/deploy.py +345 -0
  383. cumulusci/tasks/marketing_cloud/get_user_info.py +40 -0
  384. cumulusci/tasks/marketing_cloud/mc_constants.py +1 -0
  385. cumulusci/tasks/marketing_cloud/tests/__init__.py +0 -0
  386. cumulusci/tasks/marketing_cloud/tests/conftest.py +46 -0
  387. cumulusci/tasks/marketing_cloud/tests/expected-payload.json +110 -0
  388. cumulusci/tasks/marketing_cloud/tests/test_api.py +97 -0
  389. cumulusci/tasks/marketing_cloud/tests/test_api_soap_envelopes.py +145 -0
  390. cumulusci/tasks/marketing_cloud/tests/test_base.py +14 -0
  391. cumulusci/tasks/marketing_cloud/tests/test_deploy.py +400 -0
  392. cumulusci/tasks/marketing_cloud/tests/test_get_user_info.py +141 -0
  393. cumulusci/tasks/marketing_cloud/tests/validation-response.json +39 -0
  394. cumulusci/tasks/metadata/__init__.py +0 -0
  395. cumulusci/tasks/metadata/ee_src.py +94 -0
  396. cumulusci/tasks/metadata/managed_src.py +100 -0
  397. cumulusci/tasks/metadata/metadata_map.yml +868 -0
  398. cumulusci/tasks/metadata/modify.py +99 -0
  399. cumulusci/tasks/metadata/package.py +684 -0
  400. cumulusci/tasks/metadata/tests/__init__.py +0 -0
  401. cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/.hidden/.keep +0 -0
  402. cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/destructiveChanges.xml +9 -0
  403. cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/package.xml +9 -0
  404. cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/package_install_uninstall.xml +11 -0
  405. cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/reports/namespace__TestFolder/TestReport.report +3 -0
  406. cumulusci/tasks/metadata/tests/sample_package.xml +9 -0
  407. cumulusci/tasks/metadata/tests/test_ee_src.py +112 -0
  408. cumulusci/tasks/metadata/tests/test_managed_src.py +111 -0
  409. cumulusci/tasks/metadata/tests/test_modify.py +123 -0
  410. cumulusci/tasks/metadata/tests/test_package.py +476 -0
  411. cumulusci/tasks/metadata_etl/__init__.py +29 -0
  412. cumulusci/tasks/metadata_etl/base.py +436 -0
  413. cumulusci/tasks/metadata_etl/duplicate_rules.py +24 -0
  414. cumulusci/tasks/metadata_etl/field_sets.py +70 -0
  415. cumulusci/tasks/metadata_etl/help_text.py +92 -0
  416. cumulusci/tasks/metadata_etl/layouts.py +550 -0
  417. cumulusci/tasks/metadata_etl/objects.py +68 -0
  418. cumulusci/tasks/metadata_etl/permissions.py +167 -0
  419. cumulusci/tasks/metadata_etl/picklists.py +221 -0
  420. cumulusci/tasks/metadata_etl/remote_site_settings.py +99 -0
  421. cumulusci/tasks/metadata_etl/sharing.py +138 -0
  422. cumulusci/tasks/metadata_etl/tests/test_base.py +512 -0
  423. cumulusci/tasks/metadata_etl/tests/test_duplicate_rules.py +22 -0
  424. cumulusci/tasks/metadata_etl/tests/test_field_sets.py +156 -0
  425. cumulusci/tasks/metadata_etl/tests/test_help_text.py +387 -0
  426. cumulusci/tasks/metadata_etl/tests/test_ip_ranges.py +85 -0
  427. cumulusci/tasks/metadata_etl/tests/test_layouts.py +858 -0
  428. cumulusci/tasks/metadata_etl/tests/test_objects.py +236 -0
  429. cumulusci/tasks/metadata_etl/tests/test_permissions.py +223 -0
  430. cumulusci/tasks/metadata_etl/tests/test_picklists.py +547 -0
  431. cumulusci/tasks/metadata_etl/tests/test_remote_site_settings.py +46 -0
  432. cumulusci/tasks/metadata_etl/tests/test_sharing.py +333 -0
  433. cumulusci/tasks/metadata_etl/tests/test_value_sets.py +298 -0
  434. cumulusci/tasks/metadata_etl/value_sets.py +106 -0
  435. cumulusci/tasks/metadeploy.py +393 -0
  436. cumulusci/tasks/metaxml.py +88 -0
  437. cumulusci/tasks/preflight/__init__.py +0 -0
  438. cumulusci/tasks/preflight/dataset_load.py +49 -0
  439. cumulusci/tasks/preflight/licenses.py +86 -0
  440. cumulusci/tasks/preflight/packages.py +14 -0
  441. cumulusci/tasks/preflight/permsets.py +23 -0
  442. cumulusci/tasks/preflight/recordtypes.py +16 -0
  443. cumulusci/tasks/preflight/retrieve_tasks.py +30 -0
  444. cumulusci/tasks/preflight/settings.py +77 -0
  445. cumulusci/tasks/preflight/sobjects.py +202 -0
  446. cumulusci/tasks/preflight/tests/test_dataset_load.py +85 -0
  447. cumulusci/tasks/preflight/tests/test_licenses.py +174 -0
  448. cumulusci/tasks/preflight/tests/test_packages.py +14 -0
  449. cumulusci/tasks/preflight/tests/test_permset_preflights.py +51 -0
  450. cumulusci/tasks/preflight/tests/test_recordtypes.py +30 -0
  451. cumulusci/tasks/preflight/tests/test_retrieve_tasks.py +62 -0
  452. cumulusci/tasks/preflight/tests/test_settings.py +130 -0
  453. cumulusci/tasks/preflight/tests/test_sobjects.py +231 -0
  454. cumulusci/tasks/push/README.md +59 -0
  455. cumulusci/tasks/push/__init__.py +0 -0
  456. cumulusci/tasks/push/push_api.py +659 -0
  457. cumulusci/tasks/push/pushfails.py +136 -0
  458. cumulusci/tasks/push/tasks.py +476 -0
  459. cumulusci/tasks/push/tests/conftest.py +263 -0
  460. cumulusci/tasks/push/tests/test_push_api.py +951 -0
  461. cumulusci/tasks/push/tests/test_push_tasks.py +659 -0
  462. cumulusci/tasks/release_notes/README.md +63 -0
  463. cumulusci/tasks/release_notes/__init__.py +0 -0
  464. cumulusci/tasks/release_notes/exceptions.py +5 -0
  465. cumulusci/tasks/release_notes/generator.py +137 -0
  466. cumulusci/tasks/release_notes/parser.py +232 -0
  467. cumulusci/tasks/release_notes/provider.py +44 -0
  468. cumulusci/tasks/release_notes/task.py +300 -0
  469. cumulusci/tasks/release_notes/tests/__init__.py +0 -0
  470. cumulusci/tasks/release_notes/tests/change_notes/full/example1.md +17 -0
  471. cumulusci/tasks/release_notes/tests/change_notes/multi/1.txt +1 -0
  472. cumulusci/tasks/release_notes/tests/change_notes/multi/2.txt +1 -0
  473. cumulusci/tasks/release_notes/tests/change_notes/multi/3.txt +1 -0
  474. cumulusci/tasks/release_notes/tests/change_notes/single/1.txt +1 -0
  475. cumulusci/tasks/release_notes/tests/test_generator.py +582 -0
  476. cumulusci/tasks/release_notes/tests/test_parser.py +867 -0
  477. cumulusci/tasks/release_notes/tests/test_provider.py +512 -0
  478. cumulusci/tasks/release_notes/tests/test_task.py +461 -0
  479. cumulusci/tasks/release_notes/tests/utils.py +153 -0
  480. cumulusci/tasks/robotframework/__init__.py +3 -0
  481. cumulusci/tasks/robotframework/debugger/DebugListener.py +100 -0
  482. cumulusci/tasks/robotframework/debugger/__init__.py +10 -0
  483. cumulusci/tasks/robotframework/debugger/model.py +87 -0
  484. cumulusci/tasks/robotframework/debugger/ui.py +259 -0
  485. cumulusci/tasks/robotframework/libdoc.py +269 -0
  486. cumulusci/tasks/robotframework/robotframework.py +392 -0
  487. cumulusci/tasks/robotframework/stylesheet.css +130 -0
  488. cumulusci/tasks/robotframework/template.html +109 -0
  489. cumulusci/tasks/robotframework/tests/TestLibrary.py +18 -0
  490. cumulusci/tasks/robotframework/tests/TestPageObjects.py +31 -0
  491. cumulusci/tasks/robotframework/tests/TestResource.robot +8 -0
  492. cumulusci/tasks/robotframework/tests/failing_tests.robot +16 -0
  493. cumulusci/tasks/robotframework/tests/performance.robot +23 -0
  494. cumulusci/tasks/robotframework/tests/test_browser_proxies.py +137 -0
  495. cumulusci/tasks/robotframework/tests/test_debugger.py +360 -0
  496. cumulusci/tasks/robotframework/tests/test_robot_parallel.py +141 -0
  497. cumulusci/tasks/robotframework/tests/test_robotframework.py +860 -0
  498. cumulusci/tasks/salesforce/BaseRetrieveMetadata.py +58 -0
  499. cumulusci/tasks/salesforce/BaseSalesforceApiTask.py +45 -0
  500. cumulusci/tasks/salesforce/BaseSalesforceMetadataApiTask.py +18 -0
  501. cumulusci/tasks/salesforce/BaseSalesforceTask.py +4 -0
  502. cumulusci/tasks/salesforce/BaseUninstallMetadata.py +41 -0
  503. cumulusci/tasks/salesforce/CreateCommunity.py +124 -0
  504. cumulusci/tasks/salesforce/CreatePackage.py +29 -0
  505. cumulusci/tasks/salesforce/Deploy.py +240 -0
  506. cumulusci/tasks/salesforce/DeployBundles.py +88 -0
  507. cumulusci/tasks/salesforce/DescribeMetadataTypes.py +26 -0
  508. cumulusci/tasks/salesforce/EnsureRecordTypes.py +202 -0
  509. cumulusci/tasks/salesforce/GetInstalledPackages.py +8 -0
  510. cumulusci/tasks/salesforce/ListCommunities.py +40 -0
  511. cumulusci/tasks/salesforce/ListCommunityTemplates.py +19 -0
  512. cumulusci/tasks/salesforce/PublishCommunity.py +62 -0
  513. cumulusci/tasks/salesforce/RetrievePackaged.py +41 -0
  514. cumulusci/tasks/salesforce/RetrieveReportsAndDashboards.py +82 -0
  515. cumulusci/tasks/salesforce/RetrieveUnpackaged.py +36 -0
  516. cumulusci/tasks/salesforce/SOQLQuery.py +39 -0
  517. cumulusci/tasks/salesforce/UninstallLocal.py +15 -0
  518. cumulusci/tasks/salesforce/UninstallLocalBundles.py +28 -0
  519. cumulusci/tasks/salesforce/UninstallLocalNamespacedBundles.py +58 -0
  520. cumulusci/tasks/salesforce/UninstallPackage.py +32 -0
  521. cumulusci/tasks/salesforce/UninstallPackaged.py +56 -0
  522. cumulusci/tasks/salesforce/UpdateAdminProfile.py +8 -0
  523. cumulusci/tasks/salesforce/__init__.py +79 -0
  524. cumulusci/tasks/salesforce/activate_flow.py +74 -0
  525. cumulusci/tasks/salesforce/check_components.py +324 -0
  526. cumulusci/tasks/salesforce/composite.py +142 -0
  527. cumulusci/tasks/salesforce/create_permission_sets.py +35 -0
  528. cumulusci/tasks/salesforce/custom_settings.py +134 -0
  529. cumulusci/tasks/salesforce/custom_settings_wait.py +132 -0
  530. cumulusci/tasks/salesforce/enable_prediction.py +107 -0
  531. cumulusci/tasks/salesforce/insert_record.py +40 -0
  532. cumulusci/tasks/salesforce/install_package_version.py +242 -0
  533. cumulusci/tasks/salesforce/license_preflights.py +8 -0
  534. cumulusci/tasks/salesforce/network_member_group.py +178 -0
  535. cumulusci/tasks/salesforce/nonsourcetracking.py +228 -0
  536. cumulusci/tasks/salesforce/org_settings.py +193 -0
  537. cumulusci/tasks/salesforce/package_upload.py +328 -0
  538. cumulusci/tasks/salesforce/profiles.py +74 -0
  539. cumulusci/tasks/salesforce/promote_package_version.py +376 -0
  540. cumulusci/tasks/salesforce/retrieve_profile.py +195 -0
  541. cumulusci/tasks/salesforce/salesforce_files.py +244 -0
  542. cumulusci/tasks/salesforce/sourcetracking.py +507 -0
  543. cumulusci/tasks/salesforce/tests/__init__.py +3 -0
  544. cumulusci/tasks/salesforce/tests/test_CreateCommunity.py +278 -0
  545. cumulusci/tasks/salesforce/tests/test_CreatePackage.py +22 -0
  546. cumulusci/tasks/salesforce/tests/test_Deploy.py +470 -0
  547. cumulusci/tasks/salesforce/tests/test_DeployBundles.py +76 -0
  548. cumulusci/tasks/salesforce/tests/test_EnsureRecordTypes.py +345 -0
  549. cumulusci/tasks/salesforce/tests/test_ListCommunities.py +84 -0
  550. cumulusci/tasks/salesforce/tests/test_ListCommunityTemplates.py +49 -0
  551. cumulusci/tasks/salesforce/tests/test_PackageUpload.py +547 -0
  552. cumulusci/tasks/salesforce/tests/test_ProfileGrantAllAccess.py +699 -0
  553. cumulusci/tasks/salesforce/tests/test_PublishCommunity.py +181 -0
  554. cumulusci/tasks/salesforce/tests/test_RetrievePackaged.py +24 -0
  555. cumulusci/tasks/salesforce/tests/test_RetrieveReportsAndDashboards.py +56 -0
  556. cumulusci/tasks/salesforce/tests/test_RetrieveUnpackaged.py +21 -0
  557. cumulusci/tasks/salesforce/tests/test_SOQLQuery.py +30 -0
  558. cumulusci/tasks/salesforce/tests/test_UninstallLocal.py +15 -0
  559. cumulusci/tasks/salesforce/tests/test_UninstallLocalBundles.py +19 -0
  560. cumulusci/tasks/salesforce/tests/test_UninstallLocalNamespacedBundles.py +22 -0
  561. cumulusci/tasks/salesforce/tests/test_UninstallPackage.py +19 -0
  562. cumulusci/tasks/salesforce/tests/test_UninstallPackaged.py +66 -0
  563. cumulusci/tasks/salesforce/tests/test_UninstallPackagedIncremental.py +127 -0
  564. cumulusci/tasks/salesforce/tests/test_activate_flow.py +132 -0
  565. cumulusci/tasks/salesforce/tests/test_base_tasks.py +110 -0
  566. cumulusci/tasks/salesforce/tests/test_check_components.py +445 -0
  567. cumulusci/tasks/salesforce/tests/test_composite.py +250 -0
  568. cumulusci/tasks/salesforce/tests/test_create_permission_sets.py +41 -0
  569. cumulusci/tasks/salesforce/tests/test_custom_settings.py +227 -0
  570. cumulusci/tasks/salesforce/tests/test_custom_settings_wait.py +174 -0
  571. cumulusci/tasks/salesforce/tests/test_describemetadatatypes.py +18 -0
  572. cumulusci/tasks/salesforce/tests/test_enable_prediction.py +240 -0
  573. cumulusci/tasks/salesforce/tests/test_insert_record.py +110 -0
  574. cumulusci/tasks/salesforce/tests/test_install_package_version.py +464 -0
  575. cumulusci/tasks/salesforce/tests/test_network_member_group.py +444 -0
  576. cumulusci/tasks/salesforce/tests/test_nonsourcetracking.py +235 -0
  577. cumulusci/tasks/salesforce/tests/test_org_settings.py +407 -0
  578. cumulusci/tasks/salesforce/tests/test_profiles.py +202 -0
  579. cumulusci/tasks/salesforce/tests/test_retrieve_profile.py +287 -0
  580. cumulusci/tasks/salesforce/tests/test_salesforce_files.py +228 -0
  581. cumulusci/tasks/salesforce/tests/test_sourcetracking.py +350 -0
  582. cumulusci/tasks/salesforce/tests/test_trigger_handlers.py +300 -0
  583. cumulusci/tasks/salesforce/tests/test_update_dependencies.py +509 -0
  584. cumulusci/tasks/salesforce/tests/util.py +79 -0
  585. cumulusci/tasks/salesforce/trigger_handlers.py +119 -0
  586. cumulusci/tasks/salesforce/uninstall_packaged_incremental.py +136 -0
  587. cumulusci/tasks/salesforce/update_dependencies.py +290 -0
  588. cumulusci/tasks/salesforce/update_profile.py +339 -0
  589. cumulusci/tasks/salesforce/users/permsets.py +227 -0
  590. cumulusci/tasks/salesforce/users/photos.py +162 -0
  591. cumulusci/tasks/salesforce/users/tests/photo.mock.txt +1 -0
  592. cumulusci/tasks/salesforce/users/tests/test_permsets.py +950 -0
  593. cumulusci/tasks/salesforce/users/tests/test_photos.py +373 -0
  594. cumulusci/tasks/sample_data/capture_sample_data.py +77 -0
  595. cumulusci/tasks/sample_data/load_sample_data.py +85 -0
  596. cumulusci/tasks/sample_data/test_capture_sample_data.py +117 -0
  597. cumulusci/tasks/sample_data/test_load_sample_data.py +121 -0
  598. cumulusci/tasks/sfdx.py +83 -0
  599. cumulusci/tasks/tests/__init__.py +1 -0
  600. cumulusci/tasks/tests/conftest.py +30 -0
  601. cumulusci/tasks/tests/test_command.py +129 -0
  602. cumulusci/tasks/tests/test_connectedapp.py +236 -0
  603. cumulusci/tasks/tests/test_create_package_version.py +847 -0
  604. cumulusci/tasks/tests/test_datadictionary.py +1575 -0
  605. cumulusci/tasks/tests/test_dx_convert_from.py +60 -0
  606. cumulusci/tasks/tests/test_metadeploy.py +624 -0
  607. cumulusci/tasks/tests/test_metaxml.py +99 -0
  608. cumulusci/tasks/tests/test_promote_package_version.py +488 -0
  609. cumulusci/tasks/tests/test_pushfails.py +96 -0
  610. cumulusci/tasks/tests/test_salesforce.py +72 -0
  611. cumulusci/tasks/tests/test_sfdx.py +105 -0
  612. cumulusci/tasks/tests/test_util.py +207 -0
  613. cumulusci/tasks/util.py +261 -0
  614. cumulusci/tasks/vcs/__init__.py +19 -0
  615. cumulusci/tasks/vcs/commit_status.py +58 -0
  616. cumulusci/tasks/vcs/create_commit_status.py +37 -0
  617. cumulusci/tasks/vcs/download_extract.py +199 -0
  618. cumulusci/tasks/vcs/merge.py +298 -0
  619. cumulusci/tasks/vcs/publish.py +207 -0
  620. cumulusci/tasks/vcs/pull_request.py +9 -0
  621. cumulusci/tasks/vcs/release.py +134 -0
  622. cumulusci/tasks/vcs/release_report.py +105 -0
  623. cumulusci/tasks/vcs/tag.py +31 -0
  624. cumulusci/tasks/vcs/tests/github/test_commit_status.py +196 -0
  625. cumulusci/tasks/vcs/tests/github/test_download_extract.py +896 -0
  626. cumulusci/tasks/vcs/tests/github/test_merge.py +1118 -0
  627. cumulusci/tasks/vcs/tests/github/test_publish.py +823 -0
  628. cumulusci/tasks/vcs/tests/github/test_pull_request.py +29 -0
  629. cumulusci/tasks/vcs/tests/github/test_release.py +390 -0
  630. cumulusci/tasks/vcs/tests/github/test_release_report.py +109 -0
  631. cumulusci/tasks/vcs/tests/github/test_tag.py +90 -0
  632. cumulusci/tasks/vlocity/exceptions.py +2 -0
  633. cumulusci/tasks/vlocity/tests/test_vlocity.py +283 -0
  634. cumulusci/tasks/vlocity/vlocity.py +342 -0
  635. cumulusci/tests/__init__.py +1 -0
  636. cumulusci/tests/cassettes/GET_sobjects_Account_PersonAccount_describe.yaml +18 -0
  637. cumulusci/tests/cassettes/TestIntegrationInfrastructure.test_integration_tests.yaml +19 -0
  638. cumulusci/tests/pytest_plugins/pytest_sf_orgconnect.py +307 -0
  639. cumulusci/tests/pytest_plugins/pytest_sf_vcr.py +275 -0
  640. cumulusci/tests/pytest_plugins/pytest_sf_vcr_serializer.py +160 -0
  641. cumulusci/tests/pytest_plugins/pytest_typeguard.py +5 -0
  642. cumulusci/tests/pytest_plugins/test_vcr_string_compressor.py +49 -0
  643. cumulusci/tests/pytest_plugins/vcr_string_compressor.py +97 -0
  644. cumulusci/tests/shared_cassettes/GET_sobjects_Account_describe.yaml +18 -0
  645. cumulusci/tests/shared_cassettes/GET_sobjects_Case_describe.yaml +18 -0
  646. cumulusci/tests/shared_cassettes/GET_sobjects_Contact_describe.yaml +4838 -0
  647. cumulusci/tests/shared_cassettes/GET_sobjects_Custom__c_describe.yaml +242 -0
  648. cumulusci/tests/shared_cassettes/GET_sobjects_Event_describe.yaml +19 -0
  649. cumulusci/tests/shared_cassettes/GET_sobjects_Global_describe.yaml +1338 -0
  650. cumulusci/tests/shared_cassettes/GET_sobjects_Lead_describe.yaml +18 -0
  651. cumulusci/tests/shared_cassettes/GET_sobjects_OpportunityContactRole_describe.yaml +34 -0
  652. cumulusci/tests/shared_cassettes/GET_sobjects_Opportunity_describe.yaml +1261 -0
  653. cumulusci/tests/shared_cassettes/GET_sobjects_Organization.yaml +49 -0
  654. cumulusci/tests/shared_cassettes/vcr_string_templates/batchInfoList_xml.tpl +15 -0
  655. cumulusci/tests/shared_cassettes/vcr_string_templates/batchInfo_xml.tpl +13 -0
  656. cumulusci/tests/shared_cassettes/vcr_string_templates/jobInfo_insert_xml.tpl +24 -0
  657. cumulusci/tests/shared_cassettes/vcr_string_templates/jobInfo_upsert_xml.tpl +25 -0
  658. cumulusci/tests/test_entry_points.py +20 -0
  659. cumulusci/tests/test_integration_infrastructure.py +131 -0
  660. cumulusci/tests/test_main.py +9 -0
  661. cumulusci/tests/test_schema.py +32 -0
  662. cumulusci/tests/test_utils.py +657 -0
  663. cumulusci/tests/test_vcr_serializer.py +134 -0
  664. cumulusci/tests/uncompressed_cassette.yaml +83 -0
  665. cumulusci/tests/util.py +344 -0
  666. cumulusci/utils/__init__.py +731 -0
  667. cumulusci/utils/classutils.py +9 -0
  668. cumulusci/utils/collections.py +32 -0
  669. cumulusci/utils/deprecation.py +11 -0
  670. cumulusci/utils/encryption.py +31 -0
  671. cumulusci/utils/fileutils.py +295 -0
  672. cumulusci/utils/git.py +142 -0
  673. cumulusci/utils/http/multi_request.py +214 -0
  674. cumulusci/utils/http/requests_utils.py +103 -0
  675. cumulusci/utils/http/tests/cassettes/ManualEditTestCompositeParallelSalesforce.test_http_headers.yaml +32 -0
  676. cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_composite_parallel_salesforce.yaml +65 -0
  677. cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_errors.yaml +24 -0
  678. cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_reference_ids.yaml +49 -0
  679. cumulusci/utils/http/tests/test_multi_request.py +255 -0
  680. cumulusci/utils/iterators.py +21 -0
  681. cumulusci/utils/logging.py +128 -0
  682. cumulusci/utils/metaprogramming.py +10 -0
  683. cumulusci/utils/options.py +138 -0
  684. cumulusci/utils/parallel/queries_in_parallel/run_queries_in_parallel.py +29 -0
  685. cumulusci/utils/parallel/queries_in_parallel/tests/test_run_queries_in_parallel.py +50 -0
  686. cumulusci/utils/parallel/task_worker_queues/parallel_worker.py +238 -0
  687. cumulusci/utils/parallel/task_worker_queues/parallel_worker_queue.py +243 -0
  688. cumulusci/utils/parallel/task_worker_queues/tests/test_parallel_worker.py +353 -0
  689. cumulusci/utils/salesforce/count_sobjects.py +46 -0
  690. cumulusci/utils/salesforce/soql.py +17 -0
  691. cumulusci/utils/salesforce/tests/cassettes/ManualEdit_TestCountSObjects.test_count_sobjects__network_errors.yaml +23 -0
  692. cumulusci/utils/salesforce/tests/cassettes/TestCountSObjects.test_count_sobjects__errors.yaml +33 -0
  693. cumulusci/utils/salesforce/tests/cassettes/TestCountSObjects.test_count_sobjects_simple.yaml +29 -0
  694. cumulusci/utils/salesforce/tests/test_count_sobjects.py +29 -0
  695. cumulusci/utils/salesforce/tests/test_soql.py +30 -0
  696. cumulusci/utils/tests/cassettes/ManualEditTestDescribeOrg.test_minimal_schema.yaml +36 -0
  697. cumulusci/utils/tests/cassettes/ManualEdit_test_describe_to_sql.yaml +191 -0
  698. cumulusci/utils/tests/test_fileutils.py +284 -0
  699. cumulusci/utils/tests/test_git.py +85 -0
  700. cumulusci/utils/tests/test_logging.py +70 -0
  701. cumulusci/utils/tests/test_option_parsing.py +188 -0
  702. cumulusci/utils/tests/test_org_schema.py +691 -0
  703. cumulusci/utils/tests/test_org_schema_models.py +79 -0
  704. cumulusci/utils/tests/test_waiting.py +25 -0
  705. cumulusci/utils/version_strings.py +391 -0
  706. cumulusci/utils/waiting.py +42 -0
  707. cumulusci/utils/xml/__init__.py +91 -0
  708. cumulusci/utils/xml/metadata_tree.py +299 -0
  709. cumulusci/utils/xml/robot_xml.py +114 -0
  710. cumulusci/utils/xml/salesforce_encoding.py +100 -0
  711. cumulusci/utils/xml/test/test_metadata_tree.py +251 -0
  712. cumulusci/utils/xml/test/test_salesforce_encoding.py +173 -0
  713. cumulusci/utils/yaml/cumulusci_yml.py +401 -0
  714. cumulusci/utils/yaml/model_parser.py +156 -0
  715. cumulusci/utils/yaml/safer_loader.py +74 -0
  716. cumulusci/utils/yaml/tests/bad_cci.yml +5 -0
  717. cumulusci/utils/yaml/tests/cassettes/TestCumulusciYml.test_validate_url__with_errors.yaml +20 -0
  718. cumulusci/utils/yaml/tests/test_cumulusci_yml.py +286 -0
  719. cumulusci/utils/yaml/tests/test_model_parser.py +175 -0
  720. cumulusci/utils/yaml/tests/test_safer_loader.py +88 -0
  721. cumulusci/utils/ziputils.py +61 -0
  722. cumulusci/vcs/base.py +143 -0
  723. cumulusci/vcs/bootstrap.py +272 -0
  724. cumulusci/vcs/github/__init__.py +24 -0
  725. cumulusci/vcs/github/adapter.py +689 -0
  726. cumulusci/vcs/github/release_notes/generator.py +219 -0
  727. cumulusci/vcs/github/release_notes/parser.py +151 -0
  728. cumulusci/vcs/github/release_notes/provider.py +143 -0
  729. cumulusci/vcs/github/service.py +569 -0
  730. cumulusci/vcs/github/tests/test_adapter.py +138 -0
  731. cumulusci/vcs/github/tests/test_service.py +408 -0
  732. cumulusci/vcs/models.py +586 -0
  733. cumulusci/vcs/tests/conftest.py +41 -0
  734. cumulusci/vcs/tests/dummy_service.py +241 -0
  735. cumulusci/vcs/tests/test_vcs_base.py +687 -0
  736. cumulusci/vcs/tests/test_vcs_bootstrap.py +727 -0
  737. cumulusci/vcs/utils/__init__.py +31 -0
  738. cumulusci/vcs/vcs_source.py +287 -0
  739. cumulusci_plus-5.0.0.dist-info/METADATA +145 -0
  740. cumulusci_plus-5.0.0.dist-info/RECORD +744 -0
  741. cumulusci_plus-5.0.0.dist-info/WHEEL +4 -0
  742. cumulusci_plus-5.0.0.dist-info/entry_points.txt +3 -0
  743. cumulusci_plus-5.0.0.dist-info/licenses/AUTHORS.rst +41 -0
  744. 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) :])