I have a fairly complex class dealing with http callouts. I have taken great care to avoid performing any dml operations before after all callout operations are complete. All of my callout operations are carried out in separate methods that return wrappers containing lists of ids along with status codes that are used to determine if the callout was successful. The lists of IDs are then used to carry out necessary DML at the end of the method.
In its current state the GUID string method successfully runs following the "getToken" callout method. It is during "Addattachments" that there is a problem and the exception is triggered
The thing that is most perplexing is in our developer and demo orgs (demo is managed with namespace prefix) this code is running correctly. It is only in one of our client orgs that there is a problem.
I am looking for other issues that can potentially cause this exception other than dml operations before a callout?
I have not included all of the code relating to the methods relating to the failure (sorry there is a lot!)
public pagereference APIRequest(){
Type = ApexPages.currentPage().getParameters().get('Type');
NoticeID = ApexPages.currentPage().getParameters().get('NoticeID');
system.debug('Test Debug');
Id EERT = Schema.SObjectType.Contracts_Finder_Submission__c.getRecordTypeInfosByName().get('Early Engagement').getRecordTypeId();
Id FORT = Schema.SObjectType.Contracts_Finder_Submission__c.getRecordTypeInfosByName().get('Future Opportunity').getRecordTypeId();
Id ORT = Schema.SObjectType.Contracts_Finder_Submission__c.getRecordTypeInfosByName().get('Opportunity').getRecordTypeId();
Id OART = Schema.SObjectType.Contracts_Finder_Submission__c.getRecordTypeInfosByName().get('Opportunity - Awarded').getRecordTypeId();
//api endpoint from custom setting
ProSpend_Config__c spConfig = ProSpend_Config__c.getInstance();
String API = spConfig.Contracts_Finder_API__c;
String Creds = spConfig.Contracts_Finder_API_Creds__c;
boolean EmailAlerts = spConfig.Send_apex_email_alerts__c;
Map<String, Schema.SObjectType> schemaMap = Schema.getGlobalDescribe();
Map<String, Schema.SObjectField> fieldMap = schemaMap.get(utils.NameSpacePrefix()+'Contracts_Finder_Submission__c').getDescribe().fields.getMap();
string CFSQuery = 'SELECT ';
for(String fieldName : fieldMap.keyset()){
CFSQuery = CFSQuery + fieldName + ',';
}
CFSQuery = CFSQuery.substring(0,CFSQuery.length()-1);
CFSQuery = CFSQuery + ' ,Contract__r.Supplier__c, Contract__r.Estimated_Value__c, Contact__r.Name FROM Contracts_Finder_Submission__c WHERE ID = : NoticeID';
List<Contracts_Finder_Submission__c> SubList = database.query(CFSQuery);
IDsToUpdate = new List<ID>();
If(SubList.size()>0){
Contracts_Finder_Submission__c Submission = SubList[0];
String AuthToken = getToken(Creds,API,EmailAlerts) ;
system.debug('AuthToken------'+AuthToken);
String AwardResponse = '';
String PublishResponse = '';
String AttachResponse = '';
List<ID> SuppliersToUpdate = new List<ID>();
List<ID> FilesToUpdate = new List<ID>();
String ResponseString = '';
If(Type == 'Post'){
String GUID = DraftGUID(API, EmailAlerts, Submission, AuthToken);
system.debug('GUID----'+GUID);
If(GUID.left(3)=='---'){
Submission.HTTP_Response__c = GUID;
update Submission;
pagereference pr = new pagereference('/'+NoticeID);
return pr;
}
system.debug('GUID----'+GUID);
If(!String.isblank(GUID) && GUID <> null){
If(Submission.RecordTypeID == OART){
AwardReturn Award = AwardSuppliersToDraft(Submission, AuthToken, API, EmailAlerts, GUID);
AwardResponse = Award.AwardResp;
SuppliersToUpdate = Award.AwardIDs;
}
AttachReturn Attach = AddAttachments (Submission, GUID, AuthToken, API, emailalerts,'Post');
AttachResponse = Attach.AttachResp;
FilesToUpdate = Attach.AttachIDs;
PublishResponse = PublishNotice(API, AuthToken, GUID);
ResponseString = GUID + '\n' + PublishResponse + '\n' + AttachResponse + '\n' + AwardResponse;
}
Else{
ResponseString = 'No GUID Returned';
}
Submission.GUID__c = GUID;
}
Else If(Type == 'UPDATE'){
String UpdateResponse = UpdateNotice (API, EmailAlerts, Submission, AuthToken);
If(Submission.RecordTypeID == OART){
AwardReturn Award = AwardSuppliersToDraft(Submission, AuthToken, API, EmailAlerts, Submission.GUID__c);
AwardResponse = Award.AwardResp;
SuppliersToUpdate = Award.AwardIDs;
}
AttachReturn Attach = AddAttachments (Submission, Submission.GUID__c, AuthToken, API, emailalerts,'Update');
AttachResponse = Attach.AttachResp;
FilesToUpdate = Attach.AttachIDs;
If(UpdateResponse == 'Notice Update request to contracts finder was SUCCESSFUL.'){
PublishResponse = PublishNotice(API, AuthToken, Submission.GUID__c);
ResponseString = Submission.GUID__c + '\n' + PublishResponse + '\n' + AttachResponse + '\n' + AwardResponse;
}
Else{
ResponseString = UpdateResponse;
}
}
Else If(Type == 'WITHDRAW'){
String WithdrawResponse = WithdrawNotice (API, EmailAlerts, AuthToken, Submission.GUID__c);
ResponseString = WithdrawResponse;
}
Submission.HTTP_Response__c = ResponseString;
update submission;
system.debug(Filestoupdate);
If(FilesToUpdate<>null){
If(FilesToUpdate.size()>0){
List<ContentVersion> CVList = [SELECT ID, Published_to_contracts_finder__c FROM ContentVersion WHERE ContentDocumentID IN : FilesToUpdate];
For(ContentVersion CV : CVList){
CV.Published_to_contracts_finder__c = true;
}
update CVList;
}
}
If(SuppliersToUpdate<>null){
If(SuppliersToUpdate.size()>0){
List<Supplier_Bidding__c> SuppList = [SELECT ID, Supplier_Award_on_Contracts_Finder__c FROM Supplier_Bidding__c WHERE ID IN : SuppliersToUpdate];
For(Supplier_Bidding__c S : SuppList){
S.Supplier_Award_on_Contracts_Finder__c = true;
}
update SuppList;
}
}
}
pagereference pr = new pagereference('/'+NoticeID);
return pr;
}
public String DraftGUID (String API, Boolean EmailAlerts, Contracts_Finder_Submission__c Submission, String Token){
//GET ALL NOTICE RECORDTYPES
Id EERT = Schema.SObjectType.Contracts_Finder_Submission__c.getRecordTypeInfosByName().get('Early Engagement').getRecordTypeId();
Id FORT = Schema.SObjectType.Contracts_Finder_Submission__c.getRecordTypeInfosByName().get('Future Opportunity').getRecordTypeId();
Id ORT = Schema.SObjectType.Contracts_Finder_Submission__c.getRecordTypeInfosByName().get('Opportunity').getRecordTypeId();
Id OART = Schema.SObjectType.Contracts_Finder_Submission__c.getRecordTypeInfosByName().get('Opportunity - Awarded').getRecordTypeId();
String DraftBody = NoticeJSONBody(Submission);
String LenString = String.valueof(DraftBody.length());
//SUBMIT DRAFT NOTICE TO API
HttpRequest UD = new HttpRequest();
UD.setHeader('Authorization','bearer ' + Token);
UD.setHeader('Content-Type','application/json');
UD.setHeader('Content-Length',LenString);
UD.setBody(DraftBody);
UD.setmethod('POST');
UD.setEndpoint(API+'/api/rest/2/create_draft_notice/JSON');
system.debug('DRAFT REQUEST --- ' + UD);
system.debug('DRAFT BODY --- ' + UD.getbody());
HTTP U = new HTTP();
HTTPResponse UpdateResponse = new HttpResponse();
try{
If(!Test.isrunningtest()){
UpdateResponse = U.send(UD);
}
system.debug('draft response ----' + UpdateResponse.getbody());
}
catch(Exception ex){
If(EmailAlerts){
es_Utils.sendMailToOrgAdmins('Contracts Finder Draft Issue', ex.getMessage() + ' ###RESPONSE### ' + UpdateResponse.getbody());
}
}
If(EmailAlerts){
es_Utils.sendMailToOrgAdmins('Contracts Finder Submission - Draft','A submission has been made to the contracts finder API, details here: update Response \n' + UpdateResponse.getbody() ) ;
}
//GET GUID OF DRAFT NOTICE TO THEN USE IN PUBLISHING NOTICE
String GUID = '';
If(test.isRunningTest()){
GUID = '1231231231213';
return GUID;
}
If(UpdateResponse.getbody().left(1)=='"'){
system.debug('CHARACTER CHECK');
system.debug('draft response ----' + UpdateResponse.getbody());
GUID = UpdateResponse.getbody().removeend('"');
GUID = GUID.right(GUID.length()-1);
return GUID;
}
Else{
GUID = '---'+UpdateResponse.getbody()+'---';
If(EmailAlerts){
es_Utils.sendMailToOrgAdmins('Contracts Finder Post Draft Issue', GUID);
}
return GUID;
}
}
public class AttachReturn {
String AttachResp {get;set;}
List<ID> AttachIDs {get;set;}
}
public AttachReturn AddAttachments (Contracts_Finder_Submission__c Submission, String GUID, String Token, String API, boolean emailalerts, String Type){
ID SubmissionID = Submission.ID;
//GET ALL FILES ATTACHED TO CONTRACTTS FINDER Notice
List<ContentDocumentLink> FileID= [SELECT ContentDocumentId, LinkedEntityId FROM ContentDocumentLink WHERE LinkedEntityId =: SubmissionID];
system.debug('FileID ---- ' + FileID);
List<ContentDocument> CDList = new List<ContentDocument>();
List<ID> AttachIDsToUpdate = new List<ID>();
AttachReturn Response = new AttachReturn();
If(FileID.size()>0 || !String.isblank(Submission.Notes__c)){
List<ContentDistribution> AllFiles = new List<ContentDistribution>();
Map<ID,ID> PreviouslyPublished = new Map<ID,ID>();
For(ContentDocumentLink C: FileID){
List<ContentDistribution> File = [SELECT ID, ContentDocumentID, DistributionPublicUrl FROM ContentDistribution WHERE ContentDocumentID =: C.ContentDocumentId];
If(File.size()>0){
AllFiles.add(File[0]);
List<ContentDocument> CD = [SELECT ID, Title, Description FROM ContentDocument WHERE ID = : File[0].ContentDocumentID];
system.debug('CD ---- ' + CD);
List<ContentVersion> ContentVersionList = [SELECT ID, Published_to_contracts_finder__c, ContentDocumentID FROM ContentVersion WHERE ContentDocumentID = :File[0].ContentDocumentID AND Published_to_contracts_finder__c=true];
system.debug('ContentVersionList ---- ' + ContentVersionList);
If(ContentVersionList.size()>0){
PreviouslyPublished.PUT(ContentVersionList[0].ContentDocumentID,ContentVersionList[0].ContentDocumentID);
}
If(CD.size()>0){
CDList.add(CD[0]);
AttachIDsToUpdate.add(CD[0].ID);
}
}
}
system.debug('AllFiles ---- ' + AllFiles);
system.debug('CDList ---- ' + CDList);
system.debug('PreviouslyPublished ---- ' + PreviouslyPublished);
boolean submitattachmentrequest = false;
system.debug('!String.isblank(Submission.Notes__c)----'+!String.isblank(Submission.Notes__c));
If(CDList.size()>0 || !String.isblank(Submission.Notes__c)){
String FileBody = '{'+
'"noticeId": "'+GUID+'",'+
'"additionalDetailsList": [';
system.debug('AllFiles----' + AllFiles);
For(ContentDistribution CV : AllFiles){
If((CV.DistributionPublicUrl <> null || test.isRunningTest()) && PreviouslyPublished.get(CV.contentdocumentid)==null){
submitattachmentrequest = true;
//ContentToUpdate.add(CV.ContentDocumentID);
String TitleDescription = '';
For(ContentDocument CD : CDList){
If(CD.ID == CV.ContentDocumentID){
If(!String.isblank(CD.Description)){
TitleDescription = CD.Title + ' - ' + CD.Description;
}
Else{
TitleDescription = CD.Title;
}
FileBody = FileBody + '{'+
'"id": null,' +
'"noticeId": "'+GUID+'",'+
'"description": "'+TitleDescription+'",'+
'"dataType": "Link",'+
'"link": "'+CV.DistributionPublicUrl+'",'+
'"mimeType": null,'+
'"filename": null,'+
'"fileContent": null,'+
'"awardGuid": null' +
'},';
TitleDescription = '';
}
}
}
}
If(!String.isblank(Submission.Notes__c)){
system.debug('Note Included with Request');
submitattachmentrequest = true;
FileBody = FileBody + '{'+
'"id": null,' +
'"noticeId": "'+GUID+'",'+
'"dataType": "AdditionalText",'+
'"mimeType": null,'+
'"TextData": "'+Submission.Notes__c+'",'+
'"filename": null,'+
'"fileContent": null,'+
'"awardGuid": null' +
'},';
}
FileBody = FileBody.removeend(',');
FileBody = FileBody + ']}';
If(submitattachmentrequest){
String LenStringAttch = String.valueof(FileBody.length());
HttpRequest FileReq = new HttpRequest();
FileReq.setHeader('Authorization','bearer ' + Token);
//req.setHeader('Accept','text/json');
FileReq.setHeader('Content-Type','application/json');
FileReq.setHeader('Content-Length',LenStringAttch);
FileReq.setBody(FileBody);
FileReq.setmethod('POST');
FileReq.setEndpoint(API+'/Draft/AdditionalDetail/Set');
system.debug(FileReq);
system.debug(FileReq.getbody());
HTTP File = new HTTP();
HTTPResponse FileResponse =new HttpResponse();
try{
If(!Test.isrunningtest()){
FileResponse = File.send(FileReq);
}
Response.AttachResp = FileResponse.getbody();
Response.AttachIDs = AttachIDsToUpdate;
}
catch(Exception ex){
If(EmailAlerts){
es_Utils.sendMailToOrgAdmins('Contracts Finder Attachment Issue', ex.getMessage() + ' ###RESPONSE### ' + FileResponse.getbody());
}
}
If(EmailAlerts){
es_Utils.sendMailToOrgAdmins('Contracts Finder Submission - Attachment','A submission has been made to the contracts finder API, details here: update Response \n' + FileResponse.getbody() ) ;
}
system.debug('FileResponse.getbody() ---- ' + FileResponse.getbody());
}
If(Response.attachresp == null){
Response.attachresp = 'No New Files Included With Notice.';
}
return Response;
}
Else{
Response.AttachResp = 'No New Files Included With Notice.';
return Response;
}
}
Else{
Response.AttachResp = 'No Files Included With Notice.';
}
return Response;
}
ContentVersion
had a dml op on it). It seems like this object type has some additional processing on it which may interfere with callout code. What or why I'm unsure of, which is why this is a comment. If anything, I would try to do these operations in very separate contexts, but that might not be possible.