Skip to content
This repository has been archived by the owner on Oct 23, 2022. It is now read-only.

Custom Object with Compact Layout #226

Closed
froucher opened this issue Mar 17, 2020 · 7 comments
Closed

Custom Object with Compact Layout #226

froucher opened this issue Mar 17, 2020 · 7 comments

Comments

@froucher
Copy link
Contributor

froucher commented Mar 17, 2020

Custom Object with Compact Layout

When a Compact Layout is added to a Custom Object the change set can't be deployed, and it returns the following error:

TYPE   FILE                                      NAME                 PROBLEM
─────  ────────────────────────────────────────  ───────────────────  ──────────────────────────────────────────────────────────
Error  delta/objects/CustomObjectTest__c.object  CustomObjectTest__c  Must specify a non-empty label for the CustomObject

Steps to reproduce the error

  1. Create a new repository

    mkdir force-dev-tool-test
    cd force-dev-tool-test
    git init
  2. Create the simplest Custom Object and commit

    Init the folders

    mkdir src
    mkdir src/objects

    Add a src/package.xml file:

    <?xml version="1.0" encoding="UTF-8"?>
    <Package xmlns="http://soap.sforce.com/2006/04/metadata">
      <types>
        <name>CustomObject</name>
        <members>CustomObjectTest__c</members>
      </types>
      <version>46.0</version>
    </Package>

    Then, create a new file src/objects/CustomObjectTest__c.object with the following content:

    <?xml version="1.0" encoding="UTF-8"?>
    <CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
        <actionOverrides>
            <actionName>Accept</actionName>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Accept</actionName>
            <formFactor>Large</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Accept</actionName>
            <formFactor>Small</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>CancelEdit</actionName>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>CancelEdit</actionName>
            <formFactor>Large</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>CancelEdit</actionName>
            <formFactor>Small</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Clone</actionName>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Clone</actionName>
            <formFactor>Large</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Clone</actionName>
            <formFactor>Small</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Delete</actionName>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Delete</actionName>
            <formFactor>Large</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Delete</actionName>
            <formFactor>Small</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Edit</actionName>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Edit</actionName>
            <formFactor>Large</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Edit</actionName>
            <formFactor>Small</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>List</actionName>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>List</actionName>
            <formFactor>Large</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>List</actionName>
            <formFactor>Small</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>New</actionName>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>New</actionName>
            <formFactor>Large</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>New</actionName>
            <formFactor>Small</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>SaveEdit</actionName>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>SaveEdit</actionName>
            <formFactor>Large</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>SaveEdit</actionName>
            <formFactor>Small</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Tab</actionName>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Tab</actionName>
            <formFactor>Large</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>Tab</actionName>
            <formFactor>Small</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>View</actionName>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>View</actionName>
            <formFactor>Large</formFactor>
            <type>Default</type>
        </actionOverrides>
        <actionOverrides>
            <actionName>View</actionName>
            <formFactor>Small</formFactor>
            <type>Default</type>
        </actionOverrides>
        <allowInChatterGroups>false</allowInChatterGroups>
        <compactLayoutAssignment>SYSTEM</compactLayoutAssignment>
        <deploymentStatus>Deployed</deploymentStatus>
        <enableActivities>false</enableActivities>
        <enableBulkApi>true</enableBulkApi>
        <enableFeeds>false</enableFeeds>
        <enableHistory>false</enableHistory>
        <enableLicensing>false</enableLicensing>
        <enableReports>false</enableReports>
        <enableSearch>true</enableSearch>
        <enableSharing>true</enableSharing>
        <enableStreamingApi>true</enableStreamingApi>
        <gender>Feminine</gender>
        <label>CustomObjectTest</label>
        <listViews>
            <fullName>All</fullName>
            <filterScope>Everything</filterScope>
            <label>All</label>
        </listViews>
        <nameField>
            <label>CustomObjectTest Name</label>
            <type>Text</type>
        </nameField>
        <pluralLabel>CustomObjectTest</pluralLabel>
        <searchLayouts/>
        <sharingModel>ReadWrite</sharingModel>
        <visibility>Public</visibility>
    </CustomObject>

    Commit this change

    git add .
    git commit -m "init basic custom object"
    git tag FIRST-COMMIT

    And deploy these changes in a new Salesforce Sandbox called testSandbox

    sfdx force:mdapi:deploy -u testSandbox -d src -w 10
  3. Add a Compact Layout and commit the change

    Add a Compact Layout to src/objects/CustomObjectTest__c.object:

    ...
    <allowInChatterGroups>false</allowInChatterGroups>
    <compactLayoutAssignment>SYSTEM</compactLayoutAssignment>
    <compactLayouts>
        <fullName>compactLayoutTest</fullName>
        <fields>CreatedById</fields>
        <fields>CurrencyIsoCode</fields>
        <fields>Name</fields>
        <fields>LastModifiedById</fields>
        <fields>OwnerId</fields>
        <label>compactLayoutTest</label>
    </compactLayouts>
    <deploymentStatus>Deployed</deploymentStatus>
    ...

    Or if you want to create the Compact Layout directly in the Salesforce Setup then remember to retrieve the changes:

    sfdx force:mdapi:retrieve -u testSandbox -r mdAPIZip -k src/package.xml
    unzip -p mdAPIZip/unpackaged.zip unpackaged/objects/CustomObjectTest__c.object > src/objects/CustomObjectTest__c.object

    Commit this change:

    git add .
    git commit -m "adding compact layout"
    git tag COMPACT-LAYOUT

    And deploy these changes in a new Salesforce Sandbox called testSandbox

    sfdx force:mdapi:deploy -u testSandbox -d src -w 10
  4. Create a change set

    Check differences between two last commits:

    git diff --no-renames FIRST-COMMIT COMPACT-LAYOUT -- src/objects/CustomObjectTest__c.object

    Will return:

    diff --git a/src/objects/CustomObjectTest__c.object b/src/objects/CustomObjectTest__c.object
    index 967b38e..ea324ea 100644
    --- a/src/objects/CustomObjectTest__c.object
    +++ b/src/objects/CustomObjectTest__c.object
    @@ -142,6 +142,15 @@
        </actionOverrides>
        <allowInChatterGroups>false</allowInChatterGroups>
        <compactLayoutAssignment>SYSTEM</compactLayoutAssignment>
    +    <compactLayouts>
    +        <fullName>compactLayoutTest</fullName>
    +        <fields>CreatedById</fields>
    +        <fields>CurrencyIsoCode</fields>
    +        <fields>Name</fields>
    +        <fields>LastModifiedById</fields>
    +        <fields>OwnerId</fields>
    +        <label>compactLayoutTest</label>
    +    </compactLayouts>
        <deploymentStatus>Deployed</deploymentStatus>
        <enableActivities>false</enableActivities>
        <enableBulkApi>true</enableBulkApi>
    (END)

    So create a change set

    git diff --no-renames FIRST-COMMIT COMPACT-LAYOUT -- src/objects/CustomObjectTest__c.object | force-dev-tool changeset create -f delta --apiVersion 46.0 -d ./publish

    The delta generates the following file publish/delta/objects/CustomObjectTest__c.object:

    <?xml version="1.0" encoding="UTF-8"?>
    <CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
        <compactLayouts>
            <fullName>compactLayoutTest</fullName>
            <fields>CreatedById</fields>
            <fields>CurrencyIsoCode</fields>
            <fields>Name</fields>
            <fields>LastModifiedById</fields>
            <fields>OwnerId</fields>
            <label>compactLayoutTest</label>
        </compactLayouts>
    </CustomObject>

    If you try to deploy:

    sfdx force:mdapi:deploy -u testSandbox -c -d publish/delta -w 10

    It will return the following in sequence errors:

    TYPE   FILE                                      NAME                 PROBLEM
    ─────  ────────────────────────────────────────  ───────────────────  ──────────────────────────────────────────────────────────
    Error  delta/objects/CustomObjectTest__c.object  CustomObjectTest__c  Must specify a non-empty label for the CustomObject
    Error  delta/objects/CustomObjectTest__c.object  CustomObjectTest__c  Must specify a non-empty plural label for the CustomObject
    Error  delta/objects/CustomObjectTest__c.object  CustomObjectTest__c  Must specify a nameField of type Text or AutoNumber
    Error  delta/objects/CustomObjectTest__c.object  CustomObjectTest__c  Must specify a deployment status for the Custom Object
    Error  delta/objects/CustomObjectTest__c.object  CustomObjectTest__c  Must specify a sharing model value for the Custom Object

    Fixing the missing metadata then it will work:

    <?xml version="1.0" encoding="UTF-8"?>
    <CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
        <compactLayouts>
            <fullName>compactLayoutTest</fullName>
            <fields>CreatedById</fields>
            <fields>CurrencyIsoCode</fields>
            <fields>Name</fields>
            <fields>LastModifiedById</fields>
            <fields>OwnerId</fields>
            <label>compactLayoutTest</label>
        </compactLayouts>
        <deploymentStatus>Deployed</deploymentStatus>
        <label>CustomObjectTest</label>
        <nameField>
            <label>CustomObjectTest Name</label>
            <type>Text</type>
        </nameField>
        <pluralLabel>CustomObjectTest</pluralLabel>
        <sharingModel>ReadWrite</sharingModel>
    </CustomObject>

Solution

I think the change set in Custom Object must always contains the following xml properties:

<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
  ...
  change set
  ...
    <deploymentStatus>Deployed</deploymentStatus>
    <label>CustomObjectTest</label>
    <nameField>
        <label>CustomObjectTest Name</label>
        <type>Text</type>
    </nameField>
    <pluralLabel>CustomObjectTest</pluralLabel>
    <sharingModel>ReadWrite</sharingModel>
</CustomObject>

Do you agree with this solution? If you do then please let me know if I can help developing the required changes...

@amtrack
Copy link
Owner

amtrack commented Mar 19, 2020

Hey @froucher,
thank you very much for your very detailed report!
I really appreciate that! 🙏

Yes, I agree that the changeset create command needs to include required properties.
Additionally to CustomObject this should also be the case for PermissionSet, Profiles and probably more Metadata Types.

Do you already have an idea on how to implement this?
As you know, I've deprecated this project long time ago and didn't want to start new development but I'm open to collaborating on this.
How about a web meeting to brainstorm and discuss a rough plan?

@froucher
Copy link
Contributor Author

froucher commented Mar 23, 2020

Hi,

Yes I would really appreciate your help with a web meeting.

During these days I have reviewed the code, but I am not sure where is the best point to add it.

Maybe the end of the 'changeset' process:

changeset.js:

	es.merge(stdin, metadataContainer.getStream())
		.pipe(MetadataContainer.completeMetadataStream())
		.pipe(MetadataContainer.outputStream({
			apiVersion: apiVersion
		}))
		.pipe(vinylFs.dest(deploymentPath))
		.on('end', function() {
			callback(null, "exported metadata container to " + path.relative(proc.cwd, deploymentPath));
		});

Inside of the function completeMetadataStream() and completeMetadataWith()

MetadataContainer.completeMetadataStream = function(opts) {
	opts = opts || {};
	return miss.through.obj(function(metadataContainer, enc, cb) {

		metadataContainer = metadataContainer.completeMetadataWith({
			path: opts.path || 'src'
		}).filter(metadataContainer.manifest);
		metadataContainer.manifest.rollup();
		metadataContainer.destructiveManifest.filterUnnamed();

		cb(null, metadataContainer);
	});
}

But not sure if it is the best point to add this patch.

Thanks.

@froucher
Copy link
Contributor Author

I am working in the PR #228

@froucher
Copy link
Contributor Author

froucher commented Mar 31, 2020

As we have talked, we will reject the PR, because the solution must be implemented differently due to "Compact Layout" isn't a "independent child", that means can't be treated separately from the rest of the metadata file. So we are going to remove "Compact Layout" from ChildXmlName.

So the solution I understood in our talk is more o less:

Given we create a change set with force-dev-tool
When a list of independent child is added/modified
Then it will create a change set with the list of independent "component" 
 And excluding any metadata of the parent metadata

Given we create a change set with force-dev-tool
When a list property that doesn't belong to an independent child is added/modified
Then it will create a change set with the all "metadata" 
 And excluding all independent child metadata

Given we create a change set with force-dev-tool
When a list of independent child is added/modified
 And a list property that doesn't belong to an independent child is added/modified
Then it will create a independent "component" with the property value
 And it will create a change set with the all "metadata" 

Do you agree with the above behaviours?

Thank you for your support.

@amtrack
Copy link
Owner

amtrack commented Apr 1, 2020

@froucher Yes, I agree.

Let's continue this in #13 .

@froucher
Copy link
Contributor Author

froucher commented May 4, 2020

This is the new PR #232

amtrack pushed a commit that referenced this issue May 6, 2020
@amtrack
Copy link
Owner

amtrack commented May 6, 2020

resolved by #232

@amtrack amtrack closed this as completed May 6, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants