2

It's fairly easy to create an ApexClass in the SOAP Tooling API:

ConnectorConfig partnerConfig = new ConnectorConfig();
partnerConfig.setManualLogin(true);

PartnerConnection partnerConnection = com.sforce.soap.partner.Connector.newConnection(partnerConfig);
LoginResult lr = partnerConnection.login(<username>, <password> + <security_token>);

ConnectorConfig toolingConfig = new ConnectorConfig();
toolingConfig.setSessionId(lr.getSessionId());
toolingConfig.setServiceEndpoint(lr.getServerUrl().replace('u', 'T'));

toolingConnection = com.sforce.soap.tooling.Connector.newConnection(toolingConfig);

try
{
    String classBody = "public class Messages {\n"
        + "public string SayHello() {\n"
        + " return 'Hello';\n" + "}\n"
        + "}";

    ApexClass apexClass = new ApexClass();
    apexClass.setBody(classBody);
    ApexClass[] classes = {apexClass};
    classId = null;

    SaveResult[] saveResults = toolingConnection.create(classes);
    if (saveResults[0].isSuccess())
    {
        System.out.println("Successfully created Class: " + saveResults[0].getId());
        classId = saveResults[0].getId();
    }
    else
    {
        System.out.println("Error: could not create Class ");
        System.out.println("The error reported was: " + saveResults[0].getErrors()[0].getMessage() + "\n");
        throw new Exception("Error");
    }
}
catch(Exception e)
{
    //Handle exception
}

This immediately creates the class in your org. However, I would like to be able to utilize the MetadataContainer functionality to create classes in this manner:

ConnectorConfig partnerConfig = new ConnectorConfig();
partnerConfig.setManualLogin(true);

PartnerConnection partnerConnection = com.sforce.soap.partner.Connector.newConnection(partnerConfig);
LoginResult lr = partnerConnection.login(<username>, <password> + <security_token>);

ConnectorConfig toolingConfig = new ConnectorConfig();
toolingConfig.setSessionId(lr.getSessionId());
toolingConfig.setServiceEndpoint(lr.getServerUrl().replace('u', 'T'));

toolingConnection = com.sforce.soap.tooling.Connector.newConnection(toolingConfig);

try
{
    MetadataContainer Container = new MetadataContainer();
    Container.setName("SampleContainer2");

    MetadataContainer[] Containers = { Container };
    SaveResult[] containerResults = toolingConnection.create(Containers);
    if (containerResults[0].isSuccess())
    {
        System.out.println("Successfully created MetadataContainer: " + containerResults[0].getId());
        containerId = containerResults[0].getId();
    }
    else
    {
        System.out.println("Error: could not create MetadataContainer ");
        System.out.println("The error reported was: " + containerResults[0].getErrors()[0].getMessage() + "\n");
        throw new Exception("Error");
    }

    String classBody = "public class Messages {\n"
        + "public string SayHello() {\n"
        + " return 'Hello';\n" + "}\n"
        + "}";

    ApexClassMember createClassMember = new ApexClassMember();
    createClassMember.setFullName("Messages");
    createClassMember.setBody(classBody);

    createClassMember.setMetadataContainerId(containerId);
    ApexClassMember[] createClassMembers = { createClassMember };

    SaveResult[] createResults = toolingConnection.create(createClassMembers);
    if (createResults[0].isSuccess())
    {
        System.out.println("Successfully created class in metadata container: " + createResults[0].getId());
    }
    else
    {
        System.out.println("Error: could not create Class Member in metadata container");
        System.out.println("The error reported was: " + createResults[0].getErrors()[0].getMessage() + "\n");
        throw new Exception("Error");
    }
}
catch(Exception e)
{
    //Handle exception
}

When I try this, I get an error as follows:

Error: could not create Class Member in metadata container
The error reported was: Required fields are missing: [Content]

Adding a line to populate the Content field on ApexClassMetadata only results in a different error:

The error reported was: Unable to create/update fields: Content. Please check the security settings of this field and verify that it is read/write for your profile or permission set.

My question, therefore: is it possible to create classes using MetatadataContainers instead of just updating them? Salesforce's own documentation isn't clear on this matter, which is why I've come here.

0

I've always split the Apex classes based on them being an insert or an update. The inserts I processed via the direct create method. The updates needed to be handled separately via a MetadataContainer and ContainerAsyncRequest.

The trick is that the ApexClassMember has ContentEntityId as a required field. You won't be able to populate that without first inserting the ApexClass via the create method. You could create an empty class with the correct name first and then use the ApexClassMember with the correct body and Id. Id does mean more API calls to get the same work done. The only advantage I can think of would be handling any dependencies between the classes being updated and created.

There is a similar historic question in How do I use the Tooling API to create a new Apex Trigger?.

  • The advantage I see is the ability to do a test compile using ContainerAsyncRequest with created classes as well as updated classes. I had seen the question you linked, but it wasn't clear to me if using the create() method directly was the only way to create the classes / triggers / etc. If the ID really is required for ApexClassMember then it looks like create() genuinely is the only way. Still, I'll leave the question open for a little while longer to see if anyone else has any input. – Ginour Nov 4 '15 at 16:36
  • Another option if you want to do a test compile with a mixture of new and updated apex classes is the Metadata API. In the DeployOptions set checkOnly to true. I've used this in the past with Using the Salesforce Metadata API to run a single test method in isolation – Daniel Ballinger Nov 4 '15 at 18:53

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.