Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transform to remove invalid references from profiles #3688

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
88d774b
Clean functionality works for select metadata types
mjawadtp Oct 18, 2023
b75aae9
Support added for customMetadata and customSettings
mjawadtp Oct 18, 2023
20bfc49
functionality for user permissions
aditya-balachander Oct 19, 2023
6f7c22a
removed print
aditya-balachander Oct 19, 2023
7a5ce95
slight renaming
aditya-balachander Oct 20, 2023
f3ff786
Merge branch 'feature/clean-profiles-deploy-mutation' of https://gith…
mjawadtp Oct 20, 2023
d712a11
resolved issue where source format was failing
aditya-balachander Oct 20, 2023
9e66ab2
Merge branch 'feature/clean-profiles-deploy-mutation' of https://gith…
mjawadtp Oct 20, 2023
98a7ff0
Added tests for clean_invalid_references.py
aditya-balachander Oct 25, 2023
6d4b4dc
changed usage of api_version
aditya-balachander Oct 25, 2023
f2fc5b1
Merge branch 'feature/clean-profiles-deploy-mutation' of https://gith…
mjawadtp Oct 25, 2023
781c96e
improved coverage
aditya-balachander Oct 25, 2023
de442fe
Tests written for transforms.py
mjawadtp Oct 25, 2023
975349d
Merge branch 'feature/clean-profiles-deploy-mutation' of https://gith…
mjawadtp Oct 25, 2023
fd4eb78
Added tests for transforms.py and added logs
aditya-balachander Oct 25, 2023
b179b01
Handled corner cases
aditya-balachander Nov 1, 2023
150fe99
Merge branch 'main' into feature/clean-profiles-deploy-mutation
aditya-balachander Nov 3, 2023
1d2a52c
Made requested changes
aditya-balachander Nov 8, 2023
4f2694b
Merge branch 'main' into feature/clean-profiles-deploy-mutation
aditya-balachander Nov 8, 2023
5fdc7d4
Merge branch 'main' into feature/clean-profiles-deploy-mutation
aditya-balachander Nov 20, 2023
d009aad
Merge branch 'main' into feature/clean-profiles-deploy-mutation
aditya-balachander Dec 18, 2023
7729743
Merge branch 'main' into feature/clean-profiles-deploy-mutation
aditya-balachander Dec 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions cumulusci/core/source_transforms/tests/test_transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
import zipfile
from pathlib import Path, PurePosixPath
from unittest import mock
from unittest.mock import patch
from zipfile import ZipFile

import pytest
from lxml import etree as ET
from pydantic import ValidationError
from simple_salesforce import Salesforce

from cumulusci.core.exceptions import CumulusCIException, TaskOptionsError
from cumulusci.core.source_transforms.transforms import (
Expand All @@ -23,6 +25,7 @@
StripUnwantedComponentsOptions,
StripUnwantedComponentTransform,
)
from cumulusci.salesforce_api.metadata import BaseMetadataApiCall
from cumulusci.salesforce_api.package_zip import MetadataPackageZipBuilder
from cumulusci.utils import temporary_dir

Expand Down Expand Up @@ -1089,3 +1092,145 @@ def test_strip_unwanted_files(task_context):
)
== builder.zf
)


# Tests for CleanInvalidReferencesMetaXMLTransform
up_result_dict = {
"fields": [
{"name": "PermissionsEdit", "type": "boolean"},
{"name": "PermissionsView", "type": "boolean"},
{"name": "PermissionsViewPackage", "type": "boolean"},
]
}
pd_result_dict = {
"records": [
{"Permission": "ViewSetup", "RequiredPermission": "View"},
{"Permission": "EditSetup", "RequiredPermission": "Edit"},
{"Permission": "EditSetup", "RequiredPermission": "View"},
{"Permission": "ManageSetup", "RequiredPermission": "Manage"},
{"Permission": "ViewPackage", "RequiredPermission": "View"},
]
}
field_result_dict = {
"fields": [
{
"name": "Field",
"picklistValues": [{"value": "Option1"}, {"value": "Option2"}],
}
],
}
tabs_result_dict = {
"records": [
{"Name": "Tab1"},
{"Name": "Tab2"},
],
}
objects_result_dict = {
"fields": [
{
"name": "SobjectType",
"picklistValues": [{"value": "Object1"}, {"value": "Object2"}],
},
],
}


def return_response(method, urlpath):
response = mock.Mock()
if "sobjects/PermissionSet/describe" in urlpath:
response.json.return_value = up_result_dict
elif "sobjects/FieldPermissions/describe" in urlpath:
response.json.return_value = field_result_dict
elif "sobjects/ObjectPermissions/describe" in urlpath:
response.json.return_value = objects_result_dict
elif "query/?q=SELECT+Name+FROM+PermissionSetTabSetting+GROUP+BY+Name" in urlpath:
response.json.return_value = tabs_result_dict
elif (
"tooling/query/?q=SELECT+Permission,+RequiredPermission+FROM+PermissionDependency+WHERE+PermissionType+=+'User Permission'+AND+RequiredPermissionType+=+'User Permission'"
in urlpath
):
response.json.return_value = pd_result_dict

return response


def test_clean_invalid_references(task_context):
xml_data = (
"<root>\n"
" <objectPermissions>\n"
" <object>Account</object>\n"
" </objectPermissions>\n"
" <objectPermissions>\n"
" <object>Contact</object>\n"
" </objectPermissions>\n"
" <fieldPermissions>\n"
" <field>Opportunity.SomeField</field>\n"
" </fieldPermissions>\n"
" <customPermissions>\n"
" <name>CustomPermission1</name>\n"
" </customPermissions>\n"
" <tabVisibilities>\n"
" <tab>Standard-Account</tab>\n"
" </tabVisibilities>\n"
" <tabVisibilities>\n"
" <tab>Standard-Fake</tab>\n"
" </tabVisibilities>\n"
" <userPermissions>\n"
" <name>ViewPackage</name>\n"
" </userPermissions>\n"
"</root>\n"
)

xml_data_clean = (
"<?xml version='1.0' encoding='UTF-8'?>\n"
"<root>\n"
" <objectPermissions>\n"
" <object>Account</object>\n"
" </objectPermissions>\n"
" <fieldPermissions>\n"
" <field>Opportunity.SomeField</field>\n"
" </fieldPermissions>\n"
" <tabVisibilities>\n"
" <tab>Standard-Account</tab>\n"
" </tabVisibilities>\n"
" <userPermissions>\n"
" <name>ViewPackage</name>\n"
" </userPermissions>\n"
"</root>\n"
)

obj_xml = (
"<root>\n"
" <fields>\n"
" <fullName>SomeField</fullName>\n"
" </fields>\n"
" <recordTypes>\n"
" <fullName>RecordType1</fullName>\n"
" </recordTypes>\n"
"</root>"
)

package_xml = "<Package>\n" " <version>58.0</version>\n" "</Package>"

output_zip = zipfile.ZipFile(io.BytesIO(), "w", zipfile.ZIP_DEFLATED)
output_zip.writestr("objects/Opportunity.object", obj_xml)
output_zip.writestr("objects/Account.object", obj_xml)

with patch.object(
Salesforce, "_call_salesforce", side_effect=return_response
), patch.object(BaseMetadataApiCall, "__call__", return_value=output_zip):
builder = MetadataPackageZipBuilder.from_zipfile(
ZipFileSpec(
{
Path("profiles/Foo.profile"): xml_data,
Path("package.xml"): package_xml,
}
).as_zipfile(),
options={"clean_invalid_ref": True},
context=task_context,
)
for name in builder.zf.namelist():
if name == "profiles/Foo.profile":
result_root = ET.parse(builder.zf.open(name)).getroot()
expected_root = ET.fromstring(xml_data_clean.encode("utf-8"))
assert ET.iselement(result_root) == ET.iselement(expected_root)
21 changes: 20 additions & 1 deletion cumulusci/core/source_transforms/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
tokenize_namespace,
zip_clean_metaxml,
)
from cumulusci.utils.clean_invalid_references import zip_clean_invalid_references
from cumulusci.utils.xml import metadata_tree
from cumulusci.utils.ziputils import process_text_in_zipfile

Expand Down Expand Up @@ -209,7 +210,25 @@ def process(self, zf: ZipFile, context: TaskContext) -> ZipFile:
context.logger.info(
"Cleaning meta.xml files of packageVersion elements for deploy"
)
return zip_clean_metaxml(zf)
zip_dest = zip_clean_metaxml(zf)
context.logger.info("[Done]\n")
return zip_dest


class CleanInvalidReferencesMetaXMLTransform(SourceTransform):
"""Source transform that cleans *-meta.xml files of invalid references."""

options_model = None

identifier = "clean_invalid_ref"

def process(self, zf: ZipFile, context: TaskContext) -> ZipFile:
context.logger.info(
"Cleaning profiles and permission sets meta.xml files of invalid references"
)
zip_dest = zip_clean_invalid_references(zf, context)
context.logger.info("Done cleaning profiles and permission sets\n")
return zip_dest


class BundleStaticResourcesOptions(BaseModel):
Expand Down
7 changes: 7 additions & 0 deletions cumulusci/salesforce_api/package_zip.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
from cumulusci.core.source_transforms.transforms import (
BundleStaticResourcesOptions,
BundleStaticResourcesTransform,
CleanInvalidReferencesMetaXMLTransform,
CleanMetaXMLTransform,
NamespaceInjectionOptions,
NamespaceInjectionTransform,
RemoveFeatureParametersTransform,
SourceTransform,
)
from cumulusci.core.utils import process_bool_arg
from cumulusci.utils.ziputils import hash_zipfile_contents

INSTALLED_PACKAGE_PACKAGE_XML = """<?xml version="1.0" encoding="utf-8"?>
Expand Down Expand Up @@ -189,6 +191,11 @@ def _process(self):
# -meta.xml cleaning
if self.options.get("clean_meta_xml", True):
transforms.append(CleanMetaXMLTransform())

# To clean profiles and permissionsets of invalid references
if process_bool_arg(self.options.get("clean_invalid_ref") or False):
transforms.append(CleanInvalidReferencesMetaXMLTransform())

# Static resource bundling
relpath = self.options.get("static_resource_path")
if relpath and os.path.exists(relpath):
Expand Down
3 changes: 3 additions & 0 deletions cumulusci/tasks/salesforce/Deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ class Deploy(BaseSalesforceMetadataApiTask):
"description": "Apply source transforms before deploying. See the CumulusCI documentation for details on how to specify transforms."
},
"rest_deploy": {"description": "If True, deploy metadata using REST API"},
"clean_invalid_ref": {
"description": "If True, all profiles and permission sets are cleaned of invalid references before deployment."
},
}

namespaces = {"sf": "http://soap.sforce.com/2006/04/metadata"}
Expand Down
Loading
Loading