Created
September 26, 2013 13:45
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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