Thursday, February 28, 2013

Using the Migration Tool - Part 2

This post is a continuation of Working Sandbox and Production Using the Force Migration Tool
This post assumes we have made the build.xml file described in that posting.

Using the Ant Force.com Migration Tool:

  • I'll show you how to perform a typical retrieve from Production; deploy to Sandbox; fix; deploy to Sandbox and test; then deploy to Producton. 
  • Then I'll show you how to create a new APEX  Class.
  • Then I'll show you how to create a new APEX Trigger.

Typical workflow to fix something on Production.  

  1. Edit the workArea/package.xml file to specify what classes, triggers or object I need to fix. See this post for more how to do this.
  2. Run the ant tool to retrieve from production.  
    1. ant -v -lib . -f build.xml -Dtarget=prod retrieve
  3. Run the ant tool to deploy to the sandbox.   
    1. ant -v -lib . -f build.xml -Dtarget=test deploy
  4. Assuming the deploy worked it is now time to fix what you need to fix.  Include test cases verify your fix and provide the 75+% test coverage required by Salesforce.  Strive for 90% coverage.
  5. Run the ant tool to deploy to the sandbox.  Test cases will run to validate your fix.
    1. ant -v -lib . -f build.xml -Dtarget=test deploy
  6. Run the ant tool to deploy to production.
    1. ant -v -lib . -f build.xml -Dtarget=prod deploy
Nice and clean.


Here is the complete build file:

<project name="Salesforce Project"  default="work" basedir="." xmlns:sf="antlib:com.salesforce">
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>

<target name="setup" >
  <!-- on command line include "-DtargetDir=src" to change the target -->
  <property name="targetDir"  value="workarea" />
  <if>
   <equals arg1="${target}" arg2="test" />
   <then>
     <property file="buildTest.properties"/>
     <property name="system"  value="TEST" />
   </then>
   <elseif>
    <equals arg1="${target}" arg2="prod" />
    <then>
     <property file="buildProd.properties"/>
     <property name="system"  value="PROD" />
    </then>
   </elseif>
   <else>
     <echo message="The value of property target is not 'test' or 'prod'" level="error" />
     <fail/>
   </else>
  </if>
</target>

<target name="work"  depends="setup">
   <echo>The ${system} user name is ${sf.username}</echo>
</target>


<!-- Retrieve an unpackaged set of metadata from your org -->
<!-- The file ${targetDir}/package.xml lists what is to be retrieved -->
<target name="retrieve" depends="setup">
   <!-- Retrieve the contents into another directory -->
   <sf:retrieve username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" retrieveTarget="${targetDir}" unpackaged="${targetDir}/package.xml"/>
</target>

<!-- Deploy the unpackaged set of metadata retrieved with retrieveUnpackaged -->
<!-- The file ${targetDir}/package.xml lists what is to be depployed -->
<target name="deploy" depends="setup">
   <sf:deploy username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" deployRoot="${targetDir}"/>
</target>

</project>



Create APEX Class Using Ant

So here is a snippet to include in your build.xml file to generate a blank APEX class.

<!--
Sample: Create an after upsert trigger on object Opportunity
    ant -v -lib . -f build.xml -Dtarget=test -DclassName=Foo -Dtype=after createClass
Results:
Two files:
Foo.cls
Foo.cls-meta.xml

-->
<target name="createClass" depends="setup" >
    <fail unless="className" message="Must provide className (-DclassName=Foo) "/>
    <mkdir dir="${targetDir}/classes"/>
    <echo file="${targetDir}/classes/${className}.cls" append="false">public with sharing class ${className} {
}</echo>
    <copy file="templates/classes.meta.xml"  tofile="${targetDir}/triggers/${className}.cls-meta.xml" />
</target>
 


Before running this create a directory for the templates

~/Documents/salesforce/dev$ mkdir templates/

Then create the file classes.meta.xml  and insert the following:

<?xml version="1.0" encoding="UTF-8"?>
<ApexClasses xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>26.0</apiVersion>
    <status>Active</status>
</
ApexClasses>


Now when you run ant like this
ant -v -lib . -f build.xml -Dtarget=test -Dobject=Opportunity -Dtype=after createTrigger

you get triggers/OpportunityAfterUpsertTrigger.trigger  with this content

trigger OpportunityAfterUpsertTrigger on Opportunity (after insert, after update) {
}


and you get   triggers/OpportunityAfterUpsertTrigger.trigger-meta.xml  with the necessary meta definition.  When you deploy this new class will be pushed to the server.




Create APEX Trigger Using Ant

I have a simple naming convention for my trigger files.   Object + Before or After + "UpsertTrigger".  For example  OpportunityAfterUpsertTrigger works on Opportunity objects after insert or update.    I generally only build before or after upsert triggers; meaning I generally have triggers like this
trigger OpportunityAfterUpsertTrigger on Opportunity (after insert, after update) {
}

or this
trigger OpportunityBeforeUpsertTrigger on Opportunity (before insert, before update) {
}


Inside these triggers I generally collect the objects that need to be worked on and ship them over to another class to do the work and testing.

So here is a snippet to include in your build.xml file to generate a blank trigger. 

<!--
Sample: Create an after upsert trigger on object Opportunity
    ant -v -lib . -f build.xml -Dtarget=test -Dobject=Opportunity -Dtype=after createTrigger
Results:
Two files:
OpportunityAfterUpsertTrigger.trigger
OpportunityAfterUpsertTrigger.trigger-meta.xml

-->
<target name="createTrigger" depends="setup" >
    <fail unless="object" message="Must provide Object for the trigger"/>
    <fail unless="type" message="Must provide type 'before' or 'after' "/>
    <if>
    <equals arg1="${type}" arg2="before" />
    <then>
            <property name="triggerName" value="${object}BeforeUpsertTrigger" />
    </then>
    <elseif>
    <equals arg1="${type}" arg2="after" />
    <then>
            <property name="triggerName" value="${object}AfterUpsertTrigger" />
    </then>
    </elseif>
    <else>
     <echo message="The value of property type is not 'before' or 'after'" level="error" />
     <fail/>
    </else>
    </if>
    <mkdir dir="${targetDir}/triggers"/>
    <echo file="${targetDir}/triggers/${triggerName}.trigger" append="false">trigger ${triggerName} on ${object}</echo>
    <if> <equals arg1="${type}" arg2="before" />
    <then>
        <echo file="${targetDir}/triggers/${triggerName}.trigger" append="true"> (before insert, before update) {
}</echo>
    </then>
    <elseif> <equals arg1="${type}" arg2="after" />
    <then>
        <echo file="${targetDir}/triggers/${triggerName}.trigger" append="true"> (after insert, after update) {
}</echo>
    </then>
    </elseif>
    </if>
  
    <copy file="templates/trigger.meta.xml"  tofile="${targetDir}/triggers/${triggerName}.trigger-meta.xml" />
 
</target>


Before running this create a directory for the templates

~/Documents/salesforce/dev$ mkdir templates/

Then create the file trigger.meta.xml  and insert the following:

<?xml version="1.0" encoding="UTF-8"?>
<ApexTrigger xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>26.0</apiVersion>
    <status>Active</status>
</ApexTrigger>



Now when you run ant like this
ant -v -lib . -f build.xml -Dtarget=test -Dobject=Opportunity -Dtype=after createTrigger

you get triggers/OpportunityAfterUpsertTrigger.trigger  with this content

trigger OpportunityAfterUpsertTrigger on Opportunity (after insert, after update) {
}


and you get   triggers/OpportunityAfterUpsertTrigger.trigger-meta.xml  with the necessary meta definition.  When you deploy this new class will be pushed to the server.










2 comments:

  1. Have you tried this module for testing: https://github.com/DeveloperForceJapan/ApexTestPlus/blob/master/README.md

    ReplyDelete
  2. is there any way or the slightest possibility for us to hack to to see the managed package code in salesforce apps ?

    ReplyDelete