You need to sign in to do that
Don't have an account?

Apex code to create(add) custom fields to the object dynamically using Metadata API.
Hi All,
I am new to Saleforce development. Please can any one guide me to sample apex code to create(add) custom fields to custom or standard objects dynamically using Salesforce Metadata API in my development org.
Thanks in advance.
You can't access the metadata API via Apex, I'm afraid. Its a web service API used by external clients.
Hi Bob, thanks for your response.
Can I use Metadata API in my Apex ?
or Is there any way to add customer fields to objects dynamically by programmatic with apex code using metadata API, with out using external clients
Unfortunately the answer is no to both of these. I can't find much in the way of ideas regarding this either.
Hi Bob,
I just now started reading about Apex Callouts from the force.com Documentation. This seems to be a way to access external web services. There is an example describing how to use Amazon's Webservices after we get the WSDL from Amazon.
Now my doubt is, can't I Use the Metadata API WSDL to generate Apex classes and then consume the web services in my Apex. If I cannot, what is the difference between the Metadata API WSDL and other External Web services WSDL.
Thank you for your responce.
You can't callout to any Salesforce web services from within Salesforce. You can only callout to external web services.
Hi,
I understand that we cannot create objects/fields in SFDC either through programatic means using APEX or by using APIs in APEX code.
Is there an alternate means to create objects or fields in SFDC using APEX?
Aster
Hi All,
Not using API's, Can we create fields for the objects using apex programming dynamically. Any one had know about it please provide the code or suggestions to create fields.
Thanks
Sreenath
Hi,
You can try the following. Consume the Metadata api in apex. This will require a bit of tweaking in the Metadata api wsdl. Modify the generate apex code so that it could be saved. You might need to rename the update and delete method of the generated class to something else.
Once you have done that with few minor modification in the generated class you can use it to create fields and custom object programatically.
Regards
Prateek Sengar
I have been successful in getting parts of the Metadata API to work from Apex.
I've uploaded the code and a write up to Github, include pros and cons of this approach.
MetadataService.CustomObject customObject = new MetadataService.CustomObject();
customObject.fullName = 'Test__c';
customObject.label = 'Test';
customObject.pluralLabel = 'Tests';
customObject.nameField = new MetadataService.CustomField();
customObject.nameField.type_x = 'Text';
customObject.nameField.label = 'Test Record';
customObject.deploymentStatus = 'Deployed';
customObject.sharingModel = 'ReadWrite';
MetadataService.AsyncResult[] results = service.create(new List<MetadataService.Metadata> { customObject });
Enjoy!
https://github.com/financialforcedev/apex-mdapi
Hi AndylnTheCloud,
It is working fine and i also needed this...............
Thanks for sharing code for creating a new Custom field using metadata API.......
Great Job.......
@AndyInTheCloud
Is there some kind of update step missing from the code you posted, as there might be in a trigger, for example? I implemented the code and your MetadataService, almost verbatim and nothing happened to my object/field structure (coupled with the fact that I;m not really what the update and metadata labelled methods of your code do or how to utilise them...). Any help greatly appreciated.
Lee
The Metadata API from Salesforce is asyncronous, notice the return result from the 'create' operation.
See the note in the read me file about this.
"Most operations return AsyncResult which gives you an Id to call back on to determine the fate of your request. While this can be called, you will need to do this via AJAX, Apex Future or Apex Job. The deploy and retrieve samples utilise apex:actionPoller."
My hunch is that it is failing and you need to utilise one of the above approaches to find out why. If your in a Visualforce context its best to store the return AsyncResult in your controller then allow the page to rerender with an actionPoller on it, which will check the request for completion. Once done you can retrieve any error messages.
Though it is doing a 'deploy' operation and not a 'create' operation, this example can be adapted to show the results of a 'create' operation. https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataDeployController.cls, see checkAsyncRequest method. The page then uses this to call it periodically,
Or if your just trying things out, you could execute the code via Anonymous Apex and output the AsyncResult ID to the debug log. Then a few moments latter issue the 'checkStatus' operation (again via Anonymous Apex) with the ID.
If in the meantime you want to share the code your running I can try to spot an obvious issues or problems.
That's all I needed, it's all slotting into place now, thanks!
Hi again Andy,
I'm actually still having a couple of issues in that my AsyncResult is essentially blank (numberofComponentsDeployed=null).
I'm trying to create custom fields based upon the names of some of my SFDC objects, triggered by user actions. So, I'm seeing a "Completed" Apex Job, using an @future class that calls on some of the methods/classes as per your metadata service, but the fields don't appear. My trigger, and the extra method I added to your MetadataService are below:
And the "callRequest" method:
I don't really see what I'm missing. Any tips?
Thanks,
Lee
Your still not checking the AsyncResult status, you need to do this via calling MetadataService.checkStatus as as per the Metadata API's documentation, see steps here. To do this you need to use actionPoller in Visualforce context or via Batch Apex job (@future is not suitable as you cannot creating a polling condition in this context) in other contexts, such as Apex Trigger in your case.
As I can see your working in a Apex Trigger context, I assume you don't have a Visualforce page or prefer not to create one for the user action being performed? This has prompted me to create a new sample showing how to achieve what your doing via Batch Apex, it will be a generate example for any Metadata component. And will information the user (by default) via email of any failures. However it will allow the caller to provide its own handler, for example to update your trigger record with any errors. I should have this done today, I'll follow up here.
In the meantime if you want to check your async results, simply dump the Async Result ID to the debug log and manually call via Execute Annoymous Apex from Force.com IDE or Developer Console the checkStatus method to retrieve the actual error causing your problem.
I've commited some expirmental code to call checkStatus from a Batch Apex context (as apposed to Visualforce actionPoller). You can find it here. I am sitll working on the test for it and would not consider it production at this stage. However i wanted to share with you to let you give it a try and give me some feedback. Here is an example of how to use it.
As you can see you can provide your own Apex callback if you want, in this case I've wrriten a email handler to email the results to the user. If it works OK for you, I can show you how to write your own handler to pass to it, to update for example your trigger records with the result. If thats what you prefer.
Oh and make sure to update MetadataService.cls, I've made a change to that for Batch Apex context.
Hi AndlntheCloud,
Can you write calback hander for below code so, when I get the result from that call back, I want to do next process.
Thanks,
Rishabh Shah
Hi AndyInTheCloud,
I have seen your post and its really useful. But i am not getting how to get that class in apex. it is giving error.
Also read your post:
Generating a valid Apex MetadataService class
- Edit the WSDL
- Change the Port name from 'Metadata' to 'MetadataPort'
- As of Summer'13 (API 28) there was a small bug in the CustomField type definition, change the 'type' element definition to include a minOccurs="0" atttribute, as per the other elements in this type.
- Attempt to generate Apex from this WSDL
- Give it a better name if you want when prompted, e.g. MetadataService
- In earlier platform releases this would error, as update and delete are reserved words.
- It seems this has now been fixed and as of Summer'13 the Metadata API WSDL generates without errors!
- Open Eclipse (or your favourite editor)
- Open the class
- To be compatible with the samples here, edit the method name update_x to updateMetadata
- To be compatible with the samples here, edit the method name delete_x to deleteMetadata
- To be compatible with the samples here, edit the method name retrieve_x to retrieve
- Save the class
- Update the MetadataServiceText class
- Observe the uncovered items (new metadata operations, types added since last release)
- Add new code to cover operations and types
- See this for guidelines http://andyinthecloud.com/2013/05/11/code-coverage-for-wsdl2apex-generated-classes
- Making further edits to the Apex code
- Modify the end point to be dynamic
- public String endpoint_x = URL.getSalesforceBaseUrl().toExternalForm() + '/services/Soap/m/28.0';
- Make 'Metadata' inner class 'virtual'
- Make 'MetadataWithContent' inner class 'virtual'
- Review WSDL for types that extend 'tns:Metadata' and update related Apex classes to also extend Metadata
- Review WSDL for types that extend 'tns:MetadataWithContent' and update related Apex classes to also extend MetadataWithContent
- Apply the following to each class that extends Metadata, e.g. for CustomObject
Add the following at the top of the class
public String type = 'CustomObject';
public String fullName;
Add the following at the top of the private static members
private String[] type_att_info = new String[]{'xsi:type'};
private String[] fullName_type_info = new String[]{'fullName','http://www.w3.org/2001/XMLSchema','string','0','1','false'};
Add 'fullName' as the first item in the field_order_type_info String array, e.g.
private String[] field_order_type_info = new String[]{'fullName', 'actionOverrides' …. 'webLinks'};
- Apply the following to each class that extends MetadataWithContent, e.g. for ApexPage
Add the following after 'fullName'
public String content;
Add the following after 'fullName_type_info'
private String[] content_type_info = new String[]{'content','http://www.w3.org/2001/XMLSchema','base64Binary','0','1','false'};
Add 'content' after 'fullName' in the field_order_type_info String array, e.g.
private String[] field_order_type_info = new String[]{'fullName', 'content', 'apiVersion','description','label','packageVersions'};
But i am not geeting how to start. Can you please help me giving me basic steps to save that class in apex.
Please. It is much needed.
Thanks & Regards
Prateek Sengar
This e-mail and any files transmitted with it are for the sole use of the intended recipient(s) and may contain confidential and privileged information. If you are not the intended recipient(s), please reply to the sender and destroy all copies of the original message. Any unauthorized review, use, disclosure, dissemination, forwarding, printing or copying of this email, and/or any action taken in reliance on the contents of this e-mail is strictly prohibited and may be unlawful.
If it helps there is an API 28 version in Apex already of this Web Service in the GitHub repository (complete with test class). So you don't need to do the WSDL import process yourself. https://github.com/financialforcedev/apex-mdapi/tree/master/apex-mdapi/src/classes
[https://ap1.salesforce.com/img/approvals/stopsign_16x16.gif] Error: Compile Error: Invalid type: MetadataService.MetadataPort at line 48 column 9
Please help. I just want create field from apex class.
Thanks
Jack
________________________________
This message is for the designated recipient only and may contain privileged, proprietary, or otherwise confidential information. If you have received it in error, please notify the sender immediately and delete the original. Any other use of the e-mail by you is prohibited.
Where allowed by local law, electronic communications with Accenture and its affiliates, including e-mail and instant messaging (including content), may be scanned by our systems for the purposes of information security and assessment of internal compliance with Accenture policy.
______________________________________________________________________________________
www.accenture.com
Just to confirm you need as a minimum these two classes...
https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataService.cls
https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataServiceTest.cls
Then take a look at the MetadataServiceExamples.cls for sample code.
You may also be interested in the MetadataCreateJob.cls.
As explained in your blog and over here,I was successful in creating a custom object and associated custom fields, using metadata api through Apex, There is a strange issue, That I cannot access these fields, in any of the external tools like workbench, dataloader.io, Apex Data Loader as well, and thus cannot perform any data operations.
I believe, we need to add some more metadata properties, while creating a field,
Currently it is:
MetadataService.CustomField customField = new MetadataService.CustomField();
customField.fullName = 'Test__c.TestField__c';
customField.label = 'Test Field';
customField.type_x = 'Text';
customField.length = 42;
MetadataService.AsyncResult[] results = service.create(new List<MetadataService.Metadata> { customField });
Could you or any one in the forum, could throw some light on this?
Also, Can we replicate entire functionality of creating an Object and Fields using REST?
Marking this as resolved :) :P Thank you everyone.
I used same code as you given , but getting error "Invalid type: MetadataService.MetadataPort"
Please help me to resolve this error.
It's urgent.
Thanx in advance.
I need your help. Can we update the api name of a field using update metadata api?
Here is my Code:
I want to update the field api name from Mp_aadatetime__c to something else. I can change the other attributes like label, type but unable to change its api name.
MetadataService.MetadataPort service = MetadataServiceExamples.createService();
MetadataService.CustomField customField = new MetadataService.CustomField();
customField.fullName = 'Contact.Mp_aadatetime__c';
customField.label='New Test 123 Field Label';
customField.type_x = 'Text';
customField.length = 62;
List<MetadataService.SaveResult> results =
service.updateMetadata(
new MetadataService.Metadata[] { customField });
Regards
Nikhil Sharma
https://ayubansarii.wordpress.com/2018/03/24/apex-code-to-create-custom-field-programmatically/
By any chance, is there any similar way to delete salesforce field ? I need to create a new field whenever there is new field at 3rd party system, similar way I need to delete field in Salesforce when a field is deleted at 3rd party.
I would like to have advice on this.
Thanks!!
I have not found a way to do this for deleting. I've moved my solution to use the Metadata API instead. I made it in a class called Tooling. So far it has creating fields and deleting fields. For this you must have the MetadataService class located here (https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataService.cls).
public class UpdateContactPageLayout {
public Metadata.Layout addLayoutItem() {
// Retrieve Account layout and section
List<Metadata.Metadata> layoutsList = Metadata.Operations.retrieve(Metadata.MetadataType.Layout,
new List<String> {'Contact-Contact Layout'});
Metadata.Layout layoutMetadata = (Metadata.Layout) layoutsList.get(0);
Metadata.LayoutSection contactLayoutSection = null;
List<Metadata.LayoutSection> layoutSections = layoutMetadata.layoutSections;
for (Metadata.LayoutSection section : layoutSections) {
if (section.label == 'Additional Information') {
contactLayoutSection = section;
break;
}
}
// Add the field under Account info section in the left column
List<Metadata.LayoutColumn> contactColumns = contactLayoutSection.layoutColumns;
List<Metadata.LayoutItem> contactLayoutItems = contactColumns .get(0).layoutItems;
// Create a new layout item for the custom field
Metadata.LayoutItem newField = new Metadata.LayoutItem();
newField .behavior = Metadata.UiBehavior.Edit;
newField .field = 'AMAPI__Apex_MD_API_Twitter_name__c';
contactLayoutItems.add(newField);
return layoutMetadata ;
}
}
when I run this code am getting following error, please help me to fix
[{"message":"This session is not valid for use with the REST API","errorCode":"INVALID_SESSION_ID"}]
There are a few things that could cause this error. I would make sure your user has access to API. I would also make sure you're logged in. Where are you trying to run the code? Class? Trigger? Visualforce Page? Execute Anonymous window in dev console?
I have created custom fields under an object using the tooling API. but by default, there is no Field Level Permission assigned to any specific profile. So can you please let me know, is there any way to assign FLS while creating fields using tooling API?
Thanks,
I tried to reuse your code.
But I am getting one error at last line of your code which says "Variable does not exists :service for the line
MetadataService.AsyncResult[] results = service.create(new List<MetadataService.Metadata> { dayFields }); } }