cumulusci-plus 5.0.21__py3-none-any.whl → 5.0.43__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.
- cumulusci/__about__.py +1 -1
- cumulusci/cli/logger.py +2 -2
- cumulusci/cli/service.py +20 -0
- cumulusci/cli/task.py +19 -3
- cumulusci/cli/tests/test_error.py +3 -1
- cumulusci/cli/tests/test_flow.py +279 -2
- cumulusci/cli/tests/test_org.py +5 -0
- cumulusci/cli/tests/test_service.py +15 -12
- cumulusci/cli/tests/test_task.py +122 -2
- cumulusci/cli/tests/utils.py +1 -4
- cumulusci/core/config/__init__.py +1 -0
- cumulusci/core/config/base_task_flow_config.py +26 -1
- cumulusci/core/config/org_config.py +2 -1
- cumulusci/core/config/project_config.py +14 -20
- cumulusci/core/config/scratch_org_config.py +12 -0
- cumulusci/core/config/tests/test_config.py +1 -0
- cumulusci/core/config/tests/test_config_expensive.py +9 -3
- cumulusci/core/config/universal_config.py +3 -4
- cumulusci/core/dependencies/base.py +5 -1
- cumulusci/core/dependencies/dependencies.py +1 -1
- cumulusci/core/dependencies/github.py +1 -2
- cumulusci/core/dependencies/resolvers.py +1 -1
- cumulusci/core/dependencies/tests/test_dependencies.py +1 -1
- cumulusci/core/dependencies/tests/test_resolvers.py +1 -1
- cumulusci/core/flowrunner.py +90 -6
- cumulusci/core/github.py +1 -1
- cumulusci/core/sfdx.py +3 -1
- cumulusci/core/source_transforms/tests/test_transforms.py +1 -1
- cumulusci/core/source_transforms/transforms.py +1 -1
- cumulusci/core/tasks.py +13 -2
- cumulusci/core/tests/test_flowrunner.py +100 -0
- cumulusci/core/tests/test_tasks.py +65 -0
- cumulusci/core/utils.py +3 -1
- cumulusci/core/versions.py +1 -1
- cumulusci/cumulusci.yml +73 -1
- cumulusci/oauth/client.py +1 -1
- cumulusci/plugins/plugin_base.py +5 -3
- cumulusci/robotframework/pageobjects/ObjectManagerPageObject.py +1 -1
- cumulusci/salesforce_api/rest_deploy.py +1 -1
- cumulusci/schema/cumulusci.jsonschema.json +69 -0
- cumulusci/tasks/apex/anon.py +1 -1
- cumulusci/tasks/apex/testrunner.py +421 -144
- cumulusci/tasks/apex/tests/test_apex_tasks.py +917 -1
- cumulusci/tasks/bulkdata/extract.py +0 -1
- cumulusci/tasks/bulkdata/extract_dataset_utils/extract_yml.py +1 -1
- cumulusci/tasks/bulkdata/extract_dataset_utils/synthesize_extract_declarations.py +1 -1
- cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_extract_yml.py +1 -1
- cumulusci/tasks/bulkdata/generate_and_load_data.py +136 -12
- cumulusci/tasks/bulkdata/mapping_parser.py +139 -44
- cumulusci/tasks/bulkdata/select_utils.py +1 -1
- cumulusci/tasks/bulkdata/snowfakery.py +100 -25
- cumulusci/tasks/bulkdata/tests/test_generate_and_load.py +159 -0
- cumulusci/tasks/bulkdata/tests/test_load.py +0 -2
- cumulusci/tasks/bulkdata/tests/test_mapping_parser.py +763 -1
- cumulusci/tasks/bulkdata/tests/test_select_utils.py +46 -0
- cumulusci/tasks/bulkdata/tests/test_snowfakery.py +133 -0
- cumulusci/tasks/create_package_version.py +190 -16
- cumulusci/tasks/datadictionary.py +1 -1
- cumulusci/tasks/metadata_etl/__init__.py +2 -0
- cumulusci/tasks/metadata_etl/applications.py +256 -0
- cumulusci/tasks/metadata_etl/base.py +7 -3
- cumulusci/tasks/metadata_etl/layouts.py +1 -1
- cumulusci/tasks/metadata_etl/permissions.py +1 -1
- cumulusci/tasks/metadata_etl/remote_site_settings.py +2 -2
- cumulusci/tasks/metadata_etl/tests/test_applications.py +710 -0
- cumulusci/tasks/push/README.md +15 -17
- cumulusci/tasks/release_notes/README.md +13 -13
- cumulusci/tasks/release_notes/generator.py +13 -8
- cumulusci/tasks/robotframework/tests/test_robotframework.py +6 -1
- cumulusci/tasks/salesforce/Deploy.py +53 -2
- cumulusci/tasks/salesforce/SfPackageCommands.py +363 -0
- cumulusci/tasks/salesforce/__init__.py +1 -0
- cumulusci/tasks/salesforce/assign_ps_psg.py +448 -0
- cumulusci/tasks/salesforce/composite.py +1 -1
- cumulusci/tasks/salesforce/custom_settings_wait.py +1 -1
- cumulusci/tasks/salesforce/enable_prediction.py +5 -1
- cumulusci/tasks/salesforce/getPackageVersion.py +89 -0
- cumulusci/tasks/salesforce/insert_record.py +18 -19
- cumulusci/tasks/salesforce/sourcetracking.py +1 -1
- cumulusci/tasks/salesforce/tests/test_Deploy.py +316 -1
- cumulusci/tasks/salesforce/tests/test_SfPackageCommands.py +554 -0
- cumulusci/tasks/salesforce/tests/test_assign_ps_psg.py +1055 -0
- cumulusci/tasks/salesforce/tests/test_enable_prediction.py +4 -2
- cumulusci/tasks/salesforce/tests/test_getPackageVersion.py +651 -0
- cumulusci/tasks/salesforce/tests/test_update_dependencies.py +1 -1
- cumulusci/tasks/salesforce/tests/test_update_external_auth_identity_provider.py +927 -0
- cumulusci/tasks/salesforce/tests/test_update_external_credential.py +1427 -0
- cumulusci/tasks/salesforce/tests/test_update_named_credential.py +1042 -0
- cumulusci/tasks/salesforce/tests/test_update_record.py +512 -0
- cumulusci/tasks/salesforce/update_dependencies.py +2 -2
- cumulusci/tasks/salesforce/update_external_auth_identity_provider.py +551 -0
- cumulusci/tasks/salesforce/update_external_credential.py +647 -0
- cumulusci/tasks/salesforce/update_named_credential.py +441 -0
- cumulusci/tasks/salesforce/update_profile.py +17 -13
- cumulusci/tasks/salesforce/update_record.py +217 -0
- cumulusci/tasks/salesforce/users/permsets.py +62 -5
- cumulusci/tasks/salesforce/users/tests/test_permsets.py +237 -11
- cumulusci/tasks/sfdmu/__init__.py +0 -0
- cumulusci/tasks/sfdmu/sfdmu.py +376 -0
- cumulusci/tasks/sfdmu/tests/__init__.py +1 -0
- cumulusci/tasks/sfdmu/tests/test_runner.py +212 -0
- cumulusci/tasks/sfdmu/tests/test_sfdmu.py +1012 -0
- cumulusci/tasks/tests/test_create_package_version.py +716 -1
- cumulusci/tasks/tests/test_util.py +42 -0
- cumulusci/tasks/util.py +37 -1
- cumulusci/tasks/utility/copyContents.py +402 -0
- cumulusci/tasks/utility/credentialManager.py +302 -0
- cumulusci/tasks/utility/directoryRecreator.py +30 -0
- cumulusci/tasks/utility/env_management.py +1 -1
- cumulusci/tasks/utility/secretsToEnv.py +135 -0
- cumulusci/tasks/utility/tests/test_copyContents.py +1719 -0
- cumulusci/tasks/utility/tests/test_credentialManager.py +1150 -0
- cumulusci/tasks/utility/tests/test_directoryRecreator.py +439 -0
- cumulusci/tasks/utility/tests/test_secretsToEnv.py +1118 -0
- cumulusci/tests/test_integration_infrastructure.py +3 -1
- cumulusci/tests/test_utils.py +70 -6
- cumulusci/utils/__init__.py +54 -9
- cumulusci/utils/classutils.py +5 -2
- cumulusci/utils/http/tests/cassettes/ManualEditTestCompositeParallelSalesforce.test_http_headers.yaml +31 -30
- cumulusci/utils/options.py +23 -1
- cumulusci/utils/parallel/task_worker_queues/parallel_worker.py +1 -1
- cumulusci/utils/yaml/cumulusci_yml.py +8 -3
- cumulusci/utils/yaml/model_parser.py +2 -2
- cumulusci/utils/yaml/tests/test_cumulusci_yml.py +1 -1
- cumulusci/utils/yaml/tests/test_model_parser.py +3 -3
- cumulusci/vcs/base.py +23 -15
- cumulusci/vcs/bootstrap.py +5 -4
- cumulusci/vcs/utils/list_modified_files.py +189 -0
- cumulusci/vcs/utils/tests/test_list_modified_files.py +588 -0
- {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/METADATA +11 -10
- {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/RECORD +135 -104
- {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/WHEEL +1 -1
- {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/entry_points.txt +0 -0
- {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/licenses/AUTHORS.rst +0 -0
- {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from pydantic.v1 import root_validator
|
|
5
|
+
|
|
6
|
+
from cumulusci.core.exceptions import SalesforceException
|
|
7
|
+
from cumulusci.tasks.salesforce import BaseSalesforceApiTask
|
|
8
|
+
from cumulusci.utils.options import CCIOptions, Field, MappingOption
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class UpdateRecord(BaseSalesforceApiTask):
|
|
12
|
+
task_docs = """
|
|
13
|
+
Update one or more Salesforce records.
|
|
14
|
+
For example, update by record ID:
|
|
15
|
+
cci task run update_record --org dev --object Account --record_id 001xx000003DGbXXXX --values Name:UpdatedName,Status__c:Active
|
|
16
|
+
Or update by query criteria:
|
|
17
|
+
cci task run update_record --org dev --object Account --where Name:TestAccount,Status__c:Draft --values Name:UpdatedName,Status__c:Active
|
|
18
|
+
Or use environment variables with transform_values:
|
|
19
|
+
cci task run update_record --org dev --object Account --record_id 001xx000003DGbXXXX --transform_values Name:ACCOUNT_NAME_VAR,Status__c:ACCOUNT_STATUS_VAR
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
class Options(CCIOptions):
|
|
23
|
+
object: str = Field(..., description="An sObject type to update")
|
|
24
|
+
values: Optional[MappingOption] = Field(
|
|
25
|
+
None,
|
|
26
|
+
description="Field names and values to update in the format 'aa:bb,cc:dd', or a YAML dict in cumulusci.yml.",
|
|
27
|
+
)
|
|
28
|
+
transform_values: Optional[MappingOption] = Field(
|
|
29
|
+
None,
|
|
30
|
+
description="Field names and environment variable keys in the format 'field:ENV_KEY,field2:ENV_KEY2'. Values will be extracted from environment variables.",
|
|
31
|
+
)
|
|
32
|
+
record_id: Optional[str] = Field(
|
|
33
|
+
None,
|
|
34
|
+
description="The ID of a specific record to update. If specified, the 'where' option is ignored.",
|
|
35
|
+
)
|
|
36
|
+
where: Optional[str] = Field(
|
|
37
|
+
None,
|
|
38
|
+
description="Query criteria to identify records in the format 'field:value,field2:value2'. Multiple records may be updated.",
|
|
39
|
+
)
|
|
40
|
+
tooling: bool = Field(
|
|
41
|
+
False, description="If True, use the Tooling API instead of REST API."
|
|
42
|
+
)
|
|
43
|
+
fail_on_error: bool = Field(
|
|
44
|
+
True,
|
|
45
|
+
description="If True (default), fail the task if any record update fails. If False, log errors but continue.",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
@root_validator
|
|
49
|
+
def validate_options(cls, values):
|
|
50
|
+
"""Validate required option combinations"""
|
|
51
|
+
# Validate that either record_id or where is provided
|
|
52
|
+
if not values.get("record_id") and not values.get("where"):
|
|
53
|
+
raise SalesforceException(
|
|
54
|
+
"Either 'record_id' or 'where' option must be specified"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Validate that at least values or transform_values is provided
|
|
58
|
+
if not values.get("values") and not values.get("transform_values"):
|
|
59
|
+
raise SalesforceException(
|
|
60
|
+
"Either 'values' or 'transform_values' option must be specified"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return values
|
|
64
|
+
|
|
65
|
+
parsed_options: Options
|
|
66
|
+
|
|
67
|
+
def _init_task(self):
|
|
68
|
+
super()._init_task()
|
|
69
|
+
self.api = self.sf if not self.parsed_options.tooling else self.tooling
|
|
70
|
+
|
|
71
|
+
# Build the final values dict by merging values and transform_values
|
|
72
|
+
self.final_values = {}
|
|
73
|
+
|
|
74
|
+
# Start with regular values if provided
|
|
75
|
+
if self.parsed_options.values:
|
|
76
|
+
self.final_values.update(self.parsed_options.values)
|
|
77
|
+
|
|
78
|
+
# Process transform_values and extract from environment
|
|
79
|
+
if self.parsed_options.transform_values:
|
|
80
|
+
for field, env_key in self.parsed_options.transform_values.items():
|
|
81
|
+
env_value = os.environ.get(env_key, env_key)
|
|
82
|
+
self.final_values[field] = env_value
|
|
83
|
+
self.logger.info(
|
|
84
|
+
f"Transform value for field '{field}': {env_key} -> {env_value}"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def _run_task(self):
|
|
88
|
+
if self.parsed_options.record_id:
|
|
89
|
+
# Direct update by record ID
|
|
90
|
+
self._update_by_id(self.parsed_options.record_id)
|
|
91
|
+
else:
|
|
92
|
+
# Query and update multiple records
|
|
93
|
+
self._update_by_query()
|
|
94
|
+
|
|
95
|
+
def _update_by_id(self, record_id):
|
|
96
|
+
"""Update a single record by ID"""
|
|
97
|
+
object_handler = getattr(self.api, self.parsed_options.object)
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
rc = object_handler.update(record_id, self.final_values)
|
|
101
|
+
if rc == 204 or (isinstance(rc, dict) and rc.get("success")):
|
|
102
|
+
self.logger.info(
|
|
103
|
+
f"{self.parsed_options.object} record updated successfully: {record_id}"
|
|
104
|
+
)
|
|
105
|
+
else:
|
|
106
|
+
error_msg = (
|
|
107
|
+
f"Could not update {self.parsed_options.object} record {record_id}"
|
|
108
|
+
)
|
|
109
|
+
if isinstance(rc, dict) and "errors" in rc:
|
|
110
|
+
error_msg += f": {rc['errors']}"
|
|
111
|
+
if self.parsed_options.fail_on_error:
|
|
112
|
+
raise SalesforceException(error_msg)
|
|
113
|
+
else:
|
|
114
|
+
self.logger.error(error_msg)
|
|
115
|
+
except Exception as e:
|
|
116
|
+
if self.parsed_options.fail_on_error:
|
|
117
|
+
raise SalesforceException(
|
|
118
|
+
f"Error updating {self.parsed_options.object} record {record_id}: {str(e)}"
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
self.logger.error(
|
|
122
|
+
f"Error updating {self.parsed_options.object} record {record_id}: {str(e)}"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def _update_by_query(self):
|
|
126
|
+
"""Query records and update all matching records"""
|
|
127
|
+
# Parse where clause into query criteria - MappingOption already parses it
|
|
128
|
+
from cumulusci.core.utils import parse_list_of_pairs_dict_arg
|
|
129
|
+
|
|
130
|
+
where_criteria = parse_list_of_pairs_dict_arg(self.parsed_options.where)
|
|
131
|
+
|
|
132
|
+
# Build WHERE clause
|
|
133
|
+
where_parts = [
|
|
134
|
+
f"{field} = '{value}'" for field, value in where_criteria.items()
|
|
135
|
+
]
|
|
136
|
+
where_clause = " AND ".join(where_parts)
|
|
137
|
+
|
|
138
|
+
# Build and execute query
|
|
139
|
+
query = f"SELECT Id FROM {self.parsed_options.object} WHERE {where_clause}"
|
|
140
|
+
self.logger.info(f"Querying records: {query}")
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
result = self.api.query(query)
|
|
144
|
+
except Exception as e:
|
|
145
|
+
raise SalesforceException(f"Error executing query: {str(e)}")
|
|
146
|
+
|
|
147
|
+
records = result.get("records", [])
|
|
148
|
+
total_count = len(records)
|
|
149
|
+
|
|
150
|
+
if total_count == 0:
|
|
151
|
+
self.logger.warning(
|
|
152
|
+
f"No {self.parsed_options.object} records found matching criteria: {self.parsed_options.where}"
|
|
153
|
+
)
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
self.logger.info(
|
|
157
|
+
f"Found {total_count} {self.parsed_options.object} record(s) to update"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Use different update strategy based on record count
|
|
161
|
+
if total_count == 1:
|
|
162
|
+
# Single record: use direct update
|
|
163
|
+
self._update_by_id(records[0]["Id"])
|
|
164
|
+
else:
|
|
165
|
+
# Multiple records: use bulk update
|
|
166
|
+
self._update_records_bulk(records)
|
|
167
|
+
|
|
168
|
+
def _update_records_bulk(self, records):
|
|
169
|
+
"""Update multiple records using Bulk API"""
|
|
170
|
+
# Prepare data for bulk update
|
|
171
|
+
update_data = []
|
|
172
|
+
for record in records:
|
|
173
|
+
record_data = {"Id": record["Id"]}
|
|
174
|
+
record_data.update(self.final_values)
|
|
175
|
+
update_data.append(record_data)
|
|
176
|
+
|
|
177
|
+
self.logger.info(
|
|
178
|
+
f"Performing bulk update of {len(update_data)} {self.parsed_options.object} records"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
# Use Bulk API for update
|
|
183
|
+
results = self.bulk.update(self.parsed_options.object, update_data)
|
|
184
|
+
|
|
185
|
+
# Process results
|
|
186
|
+
success_count = 0
|
|
187
|
+
failed_records = []
|
|
188
|
+
|
|
189
|
+
for idx, result in enumerate(results):
|
|
190
|
+
record_id = update_data[idx]["Id"]
|
|
191
|
+
if result.success:
|
|
192
|
+
success_count += 1
|
|
193
|
+
self.logger.info(f"Updated record: {record_id}")
|
|
194
|
+
else:
|
|
195
|
+
error_msg = f"Failed to update record {record_id}: {result.error}"
|
|
196
|
+
failed_records.append({"id": record_id, "error": result.error})
|
|
197
|
+
self.logger.error(error_msg)
|
|
198
|
+
|
|
199
|
+
# Summary logging
|
|
200
|
+
self.logger.info(
|
|
201
|
+
f"Bulk update complete: {success_count}/{len(update_data)} records updated successfully"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# Handle failures
|
|
205
|
+
if failed_records and self.parsed_options.fail_on_error:
|
|
206
|
+
error_summary = "\n".join(
|
|
207
|
+
[f" - {rec['id']}: {rec['error']}" for rec in failed_records]
|
|
208
|
+
)
|
|
209
|
+
raise SalesforceException(
|
|
210
|
+
f"Failed to update {len(failed_records)} record(s):\n{error_summary}"
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
except Exception as e:
|
|
214
|
+
if self.parsed_options.fail_on_error:
|
|
215
|
+
raise SalesforceException(f"Bulk update failed: {str(e)}")
|
|
216
|
+
else:
|
|
217
|
+
self.logger.error(f"Bulk update failed: {str(e)}")
|
|
@@ -2,23 +2,43 @@ import json
|
|
|
2
2
|
|
|
3
3
|
from cumulusci.cli.ui import CliTable
|
|
4
4
|
from cumulusci.core.exceptions import CumulusCIException
|
|
5
|
-
from cumulusci.core.utils import
|
|
5
|
+
from cumulusci.core.utils import (
|
|
6
|
+
determine_managed_mode,
|
|
7
|
+
process_bool_arg,
|
|
8
|
+
process_list_arg,
|
|
9
|
+
)
|
|
6
10
|
from cumulusci.tasks.salesforce import BaseSalesforceApiTask
|
|
11
|
+
from cumulusci.tasks.salesforce.assign_ps_psg import build_name_conditions
|
|
12
|
+
from cumulusci.utils import inject_namespace
|
|
7
13
|
|
|
8
14
|
|
|
9
15
|
class AssignPermissionSets(BaseSalesforceApiTask):
|
|
10
16
|
task_docs = """
|
|
11
17
|
Assigns Permission Sets whose Names are in ``api_names`` to either the default org user or the user whose Alias is ``user_alias``. This task skips assigning Permission Sets that are already assigned.
|
|
18
|
+
|
|
19
|
+
Permission Set names can include namespace tokens that will be replaced based on the context:
|
|
20
|
+
- ``%%%NAMESPACE%%%`` is replaced with the package's namespace in managed contexts (e.g., when the package is installed)
|
|
21
|
+
- ``%%%NAMESPACED_ORG%%%`` is replaced with the package's namespace in namespaced orgs only (e.g., packaging orgs)
|
|
22
|
+
- ``%%%NAMESPACE_OR_C%%%`` is replaced with the namespace in managed contexts, or 'c' otherwise
|
|
23
|
+
- ``%%%NAMESPACED_ORG_OR_C%%%`` is replaced with the namespace in namespaced orgs, or 'c' otherwise
|
|
24
|
+
|
|
25
|
+
The managed mode and namespaced org detection is automatic based on the org context.
|
|
12
26
|
"""
|
|
13
27
|
|
|
14
28
|
task_options = {
|
|
15
29
|
"api_names": {
|
|
16
|
-
"description": "API Names of desired Permission Sets, separated by commas.",
|
|
30
|
+
"description": "API Names of desired Permission Sets, separated by commas. Can include namespace tokens like %%%NAMESPACE%%%.",
|
|
17
31
|
"required": True,
|
|
18
32
|
},
|
|
19
33
|
"user_alias": {
|
|
20
34
|
"description": "Target user aliases, separated by commas. Defaults to the current running user."
|
|
21
35
|
},
|
|
36
|
+
"namespace_inject": {
|
|
37
|
+
"description": "Namespace to use for Permission Set names. If not provided, the namespace from the project config will be used.",
|
|
38
|
+
},
|
|
39
|
+
"managed": {
|
|
40
|
+
"description": "Whether the deployment is managed. If not provided, the managed mode will be determined based on the org config.",
|
|
41
|
+
},
|
|
22
42
|
}
|
|
23
43
|
|
|
24
44
|
permission_name = "PermissionSet"
|
|
@@ -35,6 +55,38 @@ Assigns Permission Sets whose Names are in ``api_names`` to either the default o
|
|
|
35
55
|
self.options["user_alias"] = process_list_arg(
|
|
36
56
|
self.options.get("user_alias") or []
|
|
37
57
|
)
|
|
58
|
+
self._init_namespace_injection()
|
|
59
|
+
|
|
60
|
+
def _init_namespace_injection(self):
|
|
61
|
+
self.options["namespace_inject"] = (
|
|
62
|
+
self.options.get("namespace_inject")
|
|
63
|
+
or self.project_config.project__package__namespace
|
|
64
|
+
)
|
|
65
|
+
self.options["managed"] = self.options.get("managed") or determine_managed_mode(
|
|
66
|
+
self.options, self.project_config, self.org_config
|
|
67
|
+
)
|
|
68
|
+
self.options["namespaced_org"] = process_bool_arg(
|
|
69
|
+
True
|
|
70
|
+
if self.options["namespace_inject"] is not None
|
|
71
|
+
and self.options["namespace_inject"]
|
|
72
|
+
== getattr(self.org_config, "namespace", None)
|
|
73
|
+
else False
|
|
74
|
+
)
|
|
75
|
+
self.options["api_names"] = [
|
|
76
|
+
self._inject_namespace(api_name) for api_name in self.options["api_names"]
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
def _inject_namespace(self, text):
|
|
80
|
+
"""Inject the namespace into the given text if running in managed mode."""
|
|
81
|
+
_, name_processed = inject_namespace(
|
|
82
|
+
"",
|
|
83
|
+
text,
|
|
84
|
+
namespace=self.options.get("namespace_inject"),
|
|
85
|
+
managed=self.options.get("managed"),
|
|
86
|
+
namespaced_org=self.options.get("namespaced_org"),
|
|
87
|
+
logger=self.logger,
|
|
88
|
+
)
|
|
89
|
+
return name_processed
|
|
38
90
|
|
|
39
91
|
def _run_task(self):
|
|
40
92
|
users = self._query_existing_assignments()
|
|
@@ -84,12 +136,17 @@ Assigns Permission Sets whose Names are in ``api_names`` to either the default o
|
|
|
84
136
|
return assigned_perms
|
|
85
137
|
|
|
86
138
|
def _get_perm_ids(self):
|
|
87
|
-
|
|
139
|
+
name_conditions, _ = build_name_conditions(
|
|
140
|
+
self.options["api_names"], field_name=self.permission_name_field
|
|
141
|
+
)
|
|
88
142
|
perms = self.sf.query(
|
|
89
|
-
f"SELECT Id,{self.permission_name_field} FROM {self.permission_name} WHERE {
|
|
143
|
+
f"SELECT Id, NamespacePrefix, {self.permission_name_field} FROM {self.permission_name} WHERE ({' OR '.join(name_conditions)})"
|
|
90
144
|
)
|
|
91
145
|
perms_by_ids = {
|
|
92
|
-
p["Id"]: p[self.permission_name_field]
|
|
146
|
+
p["Id"]: f"{p['NamespacePrefix']}__{p[self.permission_name_field]}"
|
|
147
|
+
if p["NamespacePrefix"]
|
|
148
|
+
else p[self.permission_name_field]
|
|
149
|
+
for p in perms["records"]
|
|
93
150
|
}
|
|
94
151
|
|
|
95
152
|
missing_perms = [
|