Skip to content

Conversation

@paustint
Copy link
Contributor

Allow exporting fields in a format that can be imported using create fields

resolves #1516

Allow exporting fields in a format that can be imported using create fields

resolves #1516
Copilot AI review requested due to automatic review settings January 24, 2026 16:11
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for exporting Salesforce field metadata in a format compatible with the Create Object and Fields import template feature. Users can now choose between "Describe Format" (existing functionality) and "Metadata Format" (new functionality) when exporting field definitions.

Changes:

  • Added metadata export format that retrieves field definitions via Metadata API and transforms them into Create Fields import template format
  • Added support for new field types (EncryptedText and Location) with their specific attributes
  • Added UI controls to select export format and conditionally show relevant options

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
ListWithFilterMultiSelect.tsx Added omitFilter prop to conditionally hide filter UI when displaying fixed metadata attributes
create-fields-utils.tsx Added field definitions for displayLocationInDecimal, maskChar, and maskType to support EncryptedText and Location field types
create-fields-types.ts Added EncryptedText and Location to SalesforceFieldType, and new field definition types for the new field attributes
sobject-export-utils.ts Added getMetadataAttributes() function to define exportable metadata field attributes
sobject-export-types.ts Added ExportFormat type and extended SobjectExportFieldName with metadata-specific field names
sobject-export-metadata-utils.ts New file containing logic to fetch field metadata via Metadata API, parse XML responses, and transform to Create Fields import format
SObjectExport.tsx Added export format picker, conditional UI rendering based on format selection, and integration with metadata export functionality

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

const customFields = await listMetadata(org, [{ type: 'CustomField' }]).then((items) =>
items.data.filter((item) => customFieldsSet.has(item.fullName.split('.')[0])),
);

Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If no custom fields exist for the selected objects, the customFields array will be empty. Calling retrieveMetadataFromListMetadata with an empty array could lead to errors or unexpected behavior. Consider adding a check to handle the case where customFields is empty, returning an empty result array early or providing a meaningful message to the user that no custom fields were found.

Suggested change
if (customFields.length === 0) {
// No custom fields found for the selected sObjects; return an empty result set
return [];
}

Copilot uses AI. Check for mistakes.
const templatePrecision = !isNil(metadata.precision) && !isNil(metadata.scale) ? String(metadata.precision - metadata.scale) : '';
const templateScale = !isNil(metadata.scale) ? String(metadata.scale) : '';

// Handle referenceTo - can be string or array
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Polymorphic lookup fields that reference multiple objects will only export the first referenced object. This is a known limitation since the Create Fields feature only supports single-object lookups. Consider adding a comment or warning to inform users that polymorphic lookups cannot be fully exported in this format, or adding this information to the user-facing notification message.

Suggested change
// Handle referenceTo - can be string or array
// Handle referenceTo - can be string or array
// Note: Polymorphic lookups (referenceTo as an array of multiple objects) are only partially supported.
// In this Create Fields export format, only the first referenced object will be used.
if (Array.isArray(metadata.referenceTo) && metadata.referenceTo.length > 1) {
logger.warn(
`Polymorphic lookup field "${metadata.fullName ?? key}" references multiple objects ` +
`(${metadata.referenceTo.join(', ')}). Only the first value ("${metadata.referenceTo[0]}") ` +
'will be exported in the Create Fields format.'
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +137 to +139
if (isMetadataExport) {
picklistHeaderOptionRef.current?.selectItem('name');
picklistWorksheetLayoutRef.current?.selectItem('split');
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When switching to metadata export mode, the worksheet layout picklist is programmatically set to 'split' via the ref, but the underlying options.worksheetLayout state is not updated. This creates an inconsistency where the picklist display might not match the actual state value. Additionally, the worksheet layout picklist is not disabled for metadata exports (unlike other options), allowing users to change it. Consider either: (1) disabling the worksheet layout picklist for metadata exports if 'split' is the required layout, or (2) updating the options state when switching to metadata export mode to keep the UI and state in sync.

Suggested change
if (isMetadataExport) {
picklistHeaderOptionRef.current?.selectItem('name');
picklistWorksheetLayoutRef.current?.selectItem('split');
if (isMetadataExport) {
// Ensure the UI picklists show the correct options for metadata exports
picklistHeaderOptionRef.current?.selectItem('name');
picklistWorksheetLayoutRef.current?.selectItem('split');
// Keep underlying options state in sync with the picklist selections
setOptions((prev) => ({
...prev,
headerOption: 'name',
worksheetLayout: 'split',
}));

Copilot uses AI. Check for mistakes.
Comment on lines 170 to 171
useEffect(() => {
setHasSelectionsMade(!!selectedSObjects?.length && !!selectedAttributes?.length);
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hasSelectionsMade check requires selectedAttributes to have values, but in metadata export mode, the attribute selection is disabled and fixed. The metadata export doesn't actually use selectedAttributes (it uses allFields directly), so requiring selectedAttributes to have values for metadata exports doesn't make logical sense. Consider updating the hasSelectionsMade logic to skip the selectedAttributes check when isMetadataExport is true: setHasSelectionsMade(!!selectedSObjects?.length && (isMetadataExport || !!selectedAttributes?.length))

Copilot uses AI. Check for mistakes.
Comment on lines +350 to +351
Metadata format will include all metadata fields and can be used as an import template in the Create Object and Fields
feature.
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notification message doesn't mention that only custom fields (not standard fields) are exported in metadata format. Users might be confused when they don't see standard fields in the export. Consider updating the message to clarify this limitation, for example: "Metadata format will include all custom field metadata and can be used as an import template in the Create Object and Fields feature. Note: Only custom fields are exported in this format."

Suggested change
Metadata format will include all metadata fields and can be used as an import template in the Create Object and Fields
feature.
Metadata format will include all custom field metadata and can be used as an import template in the Create Object and Fields
feature. Note: Only custom fields are exported in this format.

Copilot uses AI. Check for mistakes.
displayFormat: { value: metadata.displayFormat || '', ...fieldValueCommon },
populateExistingRows: { value: toBoolean(metadata.populateExistingRows) || false, ...fieldValueCommon },
formula: { value: metadata.formula || '', ...fieldValueCommon },
formulaTreatBlanksAs: { value: toBoolean(metadata.formulaTreatBlanksAs) || 'Blanks', ...fieldValueCommon },
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formulaTreatBlanksAs field should accept string values ('BlankAsZero' or 'Blanks'), not boolean values. Using toBoolean on this field is incorrect and will result in boolean values instead of the expected string values. The value should be assigned directly from metadata.formulaTreatBlanksAs with a fallback to 'Blanks' if not specified.

Suggested change
formulaTreatBlanksAs: { value: toBoolean(metadata.formulaTreatBlanksAs) || 'Blanks', ...fieldValueCommon },
formulaTreatBlanksAs: { value: metadata.formulaTreatBlanksAs || 'Blanks', ...fieldValueCommon },

Copilot uses AI. Check for mistakes.
exportFormat: 'describe', // describe, metadata
worksheetLayout: 'combined', // combined, split
headerOption: 'label', // label, name
headerOption: 'name', // label, name
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default headerOption was changed from 'label' to 'name', which changes the default behavior for all users. This could be unexpected for existing users who rely on the previous default of 'label'. Consider either reverting this change to maintain backward compatibility, or documenting this breaking change in the PR description and release notes. If the intent is to use 'name' as the default only for metadata exports, this should be handled conditionally rather than changing the global default.

Suggested change
headerOption: 'name', // label, name
headerOption: 'label', // label, name

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +21
| 'displayFormat'
| 'startingNumber'
| 'populateExistingRows'
| 'maskChar'
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type SobjectExportFieldName is missing 'displayLocationInDecimal' and 'maskType' which are used in getMetadataAttributes() (lines 770 and 780 in sobject-export-utils.ts). These field names should be added to the SobjectExportFieldName union type to ensure type safety.

Suggested change
| 'displayFormat'
| 'startingNumber'
| 'populateExistingRows'
| 'maskChar'
| 'displayFormat'
| 'displayLocationInDecimal'
| 'startingNumber'
| 'populateExistingRows'
| 'maskChar'
| 'maskType'

Copilot uses AI. Check for mistakes.
| 'formulaTreatBlanksAs'
| 'writeRequiresMasterRead'
| 'secondaryType'
| 'writeRequiresMasterRead'
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type 'writeRequiresMasterRead' is duplicated in the FieldDefinitionType union. This duplicate should be removed as it serves no purpose and could indicate a copy-paste error where another field type might have been intended.

Suggested change
| 'writeRequiresMasterRead'

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow alternative field export format

2 participants