I have a requirement where I need to create parent and child custom metadata in an org programmatically. Since DML operations are not supported for creating custom metadata records, I am calling the Metadata API via Apex in order to create the parent and child custom metadata records. The parent records are created successfully. However, when I attempt to create the child records and pass the parent record id to the child in Apex, I receive an INVALID CROSS REFERENCE KEY error. I am not sure if this is because setting a parent record when calling Metadata API via Apex is not allowed or if there is something else I am doing wrong.
Please see the code block below with relevant comments. This is the full method I am using to create both parent and child custom metadata records:
public PageReference createMetadata() {
// Initialize Web Service
MetadataService.MetadataWebSvc service = POC_MetadataServiceUtil.createService();
if(mPageConfigs.containsKey(pageName)) {
// Retrieve PageConfig object for selected page name
PageConfig config = mPageConfigs.get(pageName);
system.debug(config);
// Intialize Custom Metadata type for Metadata API - should be made dynamic after initial POC is complete so field names are not hard coded
MetadataService.CustomMetadata newCustomMetadataType = new MetadataService.CustomMetadata();
newCustomMetadataType.description = '';
newCustomMetadataType.label = config.name;
newCustomMetadataType.fullName = 'Screen_Configuration.' + config.screenConfigName;
MetadataService.CustomMetadataValue pageNameValue = new MetadataService.CustomMetadataValue();
pageNameValue.field = 'Page_Name__c';
pageNameValue.value = config.name;
MetadataService.CustomMetadataValue screenConfigNameValue = new MetadataService.CustomMetadataValue();
screenConfigNameValue.field = 'Screen_Name__c';
screenConfigNameValue.value = config.screenConfigName;
MetadataService.CustomMetadataValue lastSyncDateValue = new MetadataService.CustomMetadataValue();
lastSyncDateValue.field = 'Last_Sync_Date__c';
// Populate Last Synce Date using GMT format
lastSyncDateValue.value = String.valueOf(System.now());
MetadataService.CustomMetadataValue typeValue = new MetadataService.CustomMetadataValue();
typeValue.field = 'Type__c';
// Populate Last Synce Date using GMT format
typeValue.value = config.type_x;
newCustomMetadataType.values = new MetadataService.CustomMetadataValue[]{};
newCustomMetadataType.values.add(pageNameValue);
newCustomMetadataType.values.add(screenConfigNameValue);
newCustomMetadataType.values.add(lastSyncDateValue);
newCustomMetadataType.values.add(typeValue);
****** THIS IS WHERE I UPSERT THE PARENT CUSTOM METADATA RECORD *******
List<MetadataService.UpsertResult> results = service.upsertMetadata(new MetadataService.Metadata[] { newCustomMetadataType });
*** If parent metadata creation is successful, create child metadata records ***
if(!results.isEmpty() && results[0].success == true) {
// Retrieve Id for parent custom metadata record
String screenName = config.screenConfigName;
*** I AM SELECTING THE NEWLY CREATED CUSTOM METADATA PARENT RECORD ID ***
Screen_Configuration__mdt[] lCreatedMetadata = [SELECT Id FROM Screen_Configuration__mdt WHERE Screen_Name__c = :screenName LIMIT 1];
if(!lCreatedMetadata.isEmpty()) {
// Initiate list of custom metadata objects to create
MetadataService.Metadata[] lMetadataObjects = new MetadataService.Metadata[]{};
Map<Schema.SObjectField,String> mFieldValues = new Map<Schema.SObjectField,String>();
// Loop through component list in page config object
for(Component cmp : config.components) {
// Build temporary map of all custom metadata fields to pass to util method
mFieldValues.put(Screen_Component_Configuration__mdt.Component_Name__c, cmp.name);
mFieldValues.put(Screen_Component_Configuration__mdt.Component_Namespace__c, 'c');
mFieldValues.put(Screen_Component_Configuration__mdt.Order__c, cmp.name);
mFieldValues.put(Screen_Component_Configuration__mdt.Component_Name__c, String.valueOf(cmp.sortOrder));
mFieldValues.put(Screen_Component_Configuration__mdt.Properties__c, 'test');
**SET PARENT RECORD IF FOR EACH CHILD CUSTOM METADATA RECORD. I AM USING ID CLASS TO MAKE SURE RECORD ID IS 18 CHARACTERS**
mFieldValues.put(Screen_Component_Configuration__mdt.Screen_Configuration__c, String.valueOf(Id.valueOf(lCreatedMetadata[0].id)));
mFieldValues.put(Screen_Component_Configuration__mdt.Type__c, 'TESTTYPE');
// Build field value objects for custom metadata
MetadataService.CustomMetadataValue[] lFieldValues = POC_MetadataServiceUtil.createCustomMetadataValues(mFieldValues);
// Build custom metadata object
MetadataService.CustomMetadata childCustomMetadataType = POC_MetadataServiceUtil.createCustomMetadataObject('Test Description', 'Screen_Component_Configuration.'+cmp.name, cmp.name, lFieldValues);
lMetadataObjects.add(childCustomMetadataType);
}
// Upsert list of child custom metadata records through metadata api
List<MetadataService.UpsertResult> childResults = service.upsertMetadata(new List<MetadataService.Metadata>(lMetadataObjects));
system.debug(childResults);
}
}
PageReference p = ApexPages.CurrentPage();
p.setRedirect(true);
return p;
} else {
return null;
}
}
When the upsert on the child records occurs, the SOAP message I receive is :
In field: Screen_Configuration__c - no CustomMetadata named Screen_Configuration.m0015000000L9vtAAC found.
The status code for the SOAP response is :
INVALID_CROSS_REFERENCE_KEY