Skip to content

Instantly share code, notes, and snippets.

Created September 26, 2013 13:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/6714433 to your computer and use it in GitHub Desktop.
Save anonymous/6714433 to your computer and use it in GitHub Desktop.
this is a failed attempt to figure out how ApexClassMember.LastSyncDate from SFDC Tooling API works
import com.sforce.soap.tooling._
/**
*
* this is a failed attempt to figure out how ApexClassMember.LastSyncDate works
* Example below demonstrates that my pathetic attempts to make any sense of SFDC Tooling API documentation around
* LastSyncDate were unsuccessful
*
* Here is the debug trace of the code below
*
[DEBUG] Tester$ - Deleted Container and Members
[DEBUG] Tester$ - Created new MetadataContainer; Id=1dci0000000A2LPAA0
[DEBUG] Tester$ - Before ApexClassMember update
[DEBUG] Tester$ - MetadataContainer:: tooling-force.com; Id=1dci0000000A2LPAA0
[DEBUG] Tester$ - ApexClass:: Test1; Id=01pi0000003PavdAAC; LastModifiedDate=2013-09-26T13:25:59.000Z
[DEBUG] Tester$ - Created Class Member
[DEBUG] Tester$ - MetadataContainer:: tooling-force.com; Id=1dci0000000A2LPAA0
[DEBUG] Tester$ - ApexClassMember:: Id=400i0000000DjzrAAC; ContentEntityId01pi0000003PavdAAC; MetadataContainerId=1dci0000000A2LPAA0; LastSyncDate=2013-09-26T13:25:59.000Z
[DEBUG] Tester$ - ApexClass:: Test1; Id=01pi0000003PavdAAC; LastModifiedDate=2013-09-26T13:25:59.000Z
[DEBUG] Tester$ - Request succeeded
[DEBUG] Tester$ - AsyncRequest Done
[DEBUG] Tester$ - MetadataContainer:: tooling-force.com; Id=1dci0000000A2LPAA0
[DEBUG] Tester$ - ApexClassMember:: Id=400i0000000DjzrAAC; ContentEntityId01pi0000003PavdAAC; MetadataContainerId=1dri0000000FjrRAAS; LastSyncDate=2013-09-26T13:26:01.000Z
[DEBUG] Tester$ - ApexClass:: Test1; Id=01pi0000003PavdAAC; LastModifiedDate=2013-09-26T13:26:01.000Z
[DEBUG] Tester$ - go and modify class Test1 in web browser
At this point I manually login via Web as another SFDC user and modify class Test1 (change method code)
[DEBUG] Tester$ - Web browser update Done
[DEBUG] Tester$ - MetadataContainer:: tooling-force.com; Id=1dci0000000A2LPAA0
[DEBUG] Tester$ - ApexClassMember:: Id=400i0000000DjzrAAC; ContentEntityId01pi0000003PavdAAC; MetadataContainerId=1dri0000000FjrRAAS; LastSyncDate=2013-09-26T13:26:01.000Z
[DEBUG] Tester$ - ApexClass:: Test1; Id=01pi0000003PavdAAC; LastModifiedDate=2013-09-26T13:26:17.000Z
[DEBUG] Tester$ - Update Successful. Test failed
Above I expected SFDC to stop me from updating the class modified by someone else, but it did not.
[DEBUG] Tester$ - Request succeeded
[DEBUG] Tester$ - AsyncRequest Done
[DEBUG] Tester$ - MetadataContainer:: tooling-force.com; Id=1dci0000000A2LPAA0
Since I used create() operation to update class body, now we have two ApexClassMember records
[DEBUG] Tester$ - ApexClassMember:: Id=400i0000000DjzrAAC; ContentEntityId01pi0000003PavdAAC; MetadataContainerId=1dri0000000FjrRAAS; LastSyncDate=2013-09-26T13:26:01.000Z
[DEBUG] Tester$ - ApexClassMember:: Id=400i0000000DjzwAAC; ContentEntityId01pi0000003PavdAAC; MetadataContainerId=1dri0000000FjrWAAS; LastSyncDate=2013-09-26T13:26:25.000Z
Disconnected from the target VM, address: '127.0.0.1:61920', transport: 'socket'
[DEBUG] Tester$ - ApexClass:: Test1; Id=01pi0000003PavdAAC; LastModifiedDate=2013-09-26T13:26:25.000Z
*/
object Tester extends Logging {
def runTest(session: SfdcSession) {
//Step 1 - make sure there is no existing Container
deleteMetadataContainer(session)
debugQueries("Deleted Container and Members", session)
//Step 2 - create new Container, ApexClass and ApexClassMember
val container = new MetadataContainer()
container.setName(Processor.containerName)
val containerSaveResults: Array[SaveResult] = session.create(Array(container))
val containerId = if (containerSaveResults.head.isSuccess) {
container.setId(containerSaveResults.head.getId)
logger.debug("Created new MetadataContainer; Id=" + container.getId)
container.getId
} else {
logger.debug("Failed to create Metadata Container. " + containerSaveResults.head.getErrors.head.getMessage)
throw new IllegalStateException("Failed to create Metadata Container. " + containerSaveResults.head.getErrors.head.getMessage)
}
val classBody = """
public class Test1 {
public string SayHello() {
return 'Hello';
}
}"""
val apexClass = new ApexClass()
apexClass.setBody(classBody)
val saveResults = session.create(Array(apexClass))
val res = saveResults.head
val classId = if (res.isSuccess) res.getId else throw new IllegalStateException("Failed Class Insert. " + res.getErrors.head.getMessage)
debugQueries("Before ApexClassMember update", session)
//create Member
val updatedclassBody = """
public class Test1 {
public string SayHello() {
return 'Hello1';
}
}"""
val classMember = new ApexClassMember()
classMember.setMetadataContainerId(containerId)
classMember.setBody(updatedclassBody)
classMember.setContentEntityId(classId)
val membersSaveResults = session.create(Array(classMember))
val memberRes = membersSaveResults.head
val memberId = if (memberRes.isSuccess) memberRes.getId else throw new IllegalStateException("Failed Member Insert. " + memberRes.getErrors.head.getMessage)
debugQueries("Created Class Member", session)
waitForRequestToComplete(session, containerId)
//Step 3 - go and modify class Test1 in web browser
logger.debug("go and modify class Test1 in web browser")
debugQueries("Web browser update Done", session)
val classMember2 = new ApexClassMember()
classMember2.setBody(updatedclassBody)
classMember2.setMetadataContainerId(containerId)
classMember2.setContentEntityId(classId)
//classMember2.setId(memberId)
//NOTE update(Array(classMember2)) can not be used below because at this point (in SFDC DB)
// classMember2.MetadataContainerId points to the Id of ContainerAsyncRequest and any updates on that
// ApexClassMember record fail with error saying that MetadataContainer with Id... (here goes ContainerAsyncRequest Id) is invalid.
//
//There is also no way to reset MetadataContainerId back to the proper ContainerId because MetadataContainerId is not Updateable field
//val updateResults = session.update(Array(classMember2)) // Can not use update() here
val updateResults = session.create(Array(classMember2))
val updateRes = updateResults.head
if (updateRes.isSuccess) {
logger.debug("Update Successful. Test failed")
} else {
logger.debug("Update Result: " + updateRes.getErrors.head.getMessage)
}
waitForRequestToComplete(session, containerId)
}
private def deleteMetadataContainer(session: SfdcSession) {
val containerIds = session.query("select Id, Name from MetadataContainer ").getRecords.map(_.getId)
if (!containerIds.isEmpty) {
session.delete(containerIds)
}
val memberIds = session.query("select Id from ApexClassMember ").getRecords.map(_.getId)
if (!memberIds.isEmpty) {
session.delete(memberIds)
}
val classIds = session.query("select Id from ApexClass where Name='Test1' ").getRecords.map(_.getId)
if (!classIds.isEmpty) {
session.delete(classIds)
}
}
private def debugQueries(msg: String, session: SfdcSession) {
logger.debug(msg)
for (req <- session.query("select Id, Name from MetadataContainer ").getRecords) {
val container = req.asInstanceOf[MetadataContainer]
logger.debug("MetadataContainer:: " + container.getName + "; Id=" + container.getId)
}
for (req <- session.query("select Id, ContentEntityId, MetadataContainerId, LastSyncDate from ApexClassMember ").getRecords) {
val member = req.asInstanceOf[ApexClassMember]
logger.debug("ApexClassMember:: " + "Id=" + member.getId + "; ContentEntityId=" + member.getContentEntityId +
"; MetadataContainerId=" + member.getMetadataContainerId + "; LastSyncDate=" + ZuluTime.format(member.getLastSyncDate.getTime))
}
for (req <- session.query("select Id, Name, LastModifiedDate from ApexClass where Name='Test1' ").getRecords) {
val apexClass = req.asInstanceOf[ApexClass]
logger.debug("ApexClass:: " + apexClass.getName+ "; Id=" + apexClass.getId +
"; LastModifiedDate=" + ZuluTime.format(apexClass.getLastModifiedDate.getTime))
}
}
private def waitForRequestToComplete(session: SfdcSession, containerId: String) {
val request = new ContainerAsyncRequest()
request.setIsCheckOnly(false)
request.setMetadataContainerId(containerId)
val requestResults = session.create(Array(request))
val requestRes = requestResults.head
val requestId = if (requestRes.isSuccess) requestRes.getId else throw new IllegalStateException("Failed Request Insert. " + requestRes.getErrors.head.getMessage)
val soql = "SELECT Id, State, CompilerErrors, ErrorMsg FROM ContainerAsyncRequest where id = '" + requestId + "'"
val asyncQueryResult = session.query(soql)
if (asyncQueryResult.getSize > 0) {
var _request = asyncQueryResult.getRecords.head.asInstanceOf[ContainerAsyncRequest]
while ("Queued" == _request.getState) {
Thread.sleep(2000)
_request = session.query(soql).getRecords.head.asInstanceOf[ContainerAsyncRequest]
}
_request.getState match {
case "Completed" =>
logger.debug("Request succeeded")
//containerId = _request.getId //As part of a successful deployment, this field is reset from the ID
// of the deployed MetadataContainer to the ID of the corresponding
// ContainerAsyncRequest object
case state =>
logger.error("Request Failed with status: " + state)
logger.error("Request Failed with error: " + _request.getErrorMsg)
throw new IllegalStateException("Failed to send Async Request. Status= " + state)
}
}
debugQueries("AsyncRequest Done", session)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment