7

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

  • You don't need to use an mdapi wrapper for custom metadata records. You can use the new metadata interface to create the records. – sfdcfox Jul 31 '17 at 15:38
  • Thanks @sfdcfox. I see this is a wrapper provided so API calls do not have to be used. However, when I try again to give a parent custom metadata record id for the child using the Metadata interface, I receive the following error message "Screen_Configuration__c - no CustomMetadata named Screen_Configuration.m0015000000L9vtAAC found". 'm0015000000L9vtAAC' is the record id of the Screen_Configuration__mdt object which is the parent object. It's the same error message as before. – Daniel Fuller Jul 31 '17 at 18:51
  • @sfdcfox, I found the answer to the issue. I have posted it below. However, thanks for mentioning about the new Metadata interface wrapper. That will help a lot to save API calls in the org. – Daniel Fuller Jul 31 '17 at 19:01
8

I actually just found the answer to this question here.

The documentation states the following:

When setting the value for relationship fields, use the qualified API name of the related metadata, not the ID.

Thanks everyone!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.