-
-
Notifications
You must be signed in to change notification settings - Fork 28
feat: allow alternative field export format #1517
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
base: main
Are you sure you want to change the base?
Conversation
Allow exporting fields in a format that can be imported using create fields resolves #1516
There was a problem hiding this 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])), | ||
| ); | ||
|
|
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
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.
| if (customFields.length === 0) { | |
| // No custom fields found for the selected sObjects; return an empty result set | |
| return []; | |
| } |
| 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 |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
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.
| // 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.' | |
| ); | |
| } |
| if (isMetadataExport) { | ||
| picklistHeaderOptionRef.current?.selectItem('name'); | ||
| picklistWorksheetLayoutRef.current?.selectItem('split'); |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
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.
| 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', | |
| })); |
| useEffect(() => { | ||
| setHasSelectionsMade(!!selectedSObjects?.length && !!selectedAttributes?.length); |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
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))
| Metadata format will include all metadata fields and can be used as an import template in the Create Object and Fields | ||
| feature. |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
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."
| 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. |
| displayFormat: { value: metadata.displayFormat || '', ...fieldValueCommon }, | ||
| populateExistingRows: { value: toBoolean(metadata.populateExistingRows) || false, ...fieldValueCommon }, | ||
| formula: { value: metadata.formula || '', ...fieldValueCommon }, | ||
| formulaTreatBlanksAs: { value: toBoolean(metadata.formulaTreatBlanksAs) || 'Blanks', ...fieldValueCommon }, |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
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.
| formulaTreatBlanksAs: { value: toBoolean(metadata.formulaTreatBlanksAs) || 'Blanks', ...fieldValueCommon }, | |
| formulaTreatBlanksAs: { value: metadata.formulaTreatBlanksAs || 'Blanks', ...fieldValueCommon }, |
| exportFormat: 'describe', // describe, metadata | ||
| worksheetLayout: 'combined', // combined, split | ||
| headerOption: 'label', // label, name | ||
| headerOption: 'name', // label, name |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
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.
| headerOption: 'name', // label, name | |
| headerOption: 'label', // label, name |
| | 'displayFormat' | ||
| | 'startingNumber' | ||
| | 'populateExistingRows' | ||
| | 'maskChar' |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
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.
| | 'displayFormat' | |
| | 'startingNumber' | |
| | 'populateExistingRows' | |
| | 'maskChar' | |
| | 'displayFormat' | |
| | 'displayLocationInDecimal' | |
| | 'startingNumber' | |
| | 'populateExistingRows' | |
| | 'maskChar' | |
| | 'maskType' |
| | 'formulaTreatBlanksAs' | ||
| | 'writeRequiresMasterRead' | ||
| | 'secondaryType' | ||
| | 'writeRequiresMasterRead' |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
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.
| | 'writeRequiresMasterRead' |
Allow exporting fields in a format that can be imported using create fields
resolves #1516