-3
trigger updateTwitterFollowers on Contact (after update) {
 Map<Id, String> twitterContacts = new Map<Id, String>();
 for (Contact newContact : Trigger.new){
    System.debug(newContact.Id);
    System.debug(Trigger.oldMap.get(newContact.Id));
    Contact oldContact = Trigger.oldMap.get(newContact.Id);
    System.debug(newContact.Id);
    if (String.isNotBlank(newContact.Twitter_Handle__c) && (newContact.Twitter_Handle__c != oldContact.Twitter_Handle__c)) {
        twitterContacts.put(newContact.Id, newContact.Twitter_Handle__c);
    System.debug(newContact.Twitter_Handle__c + newContact.Id);
    System.debug(twitterContacts);
    }

   }
    System.debug(twitterContacts.size());
if (twitterContacts.size() > 0 ) {

    TwitterCallout.updateFollowers(twitterContacts);
    System.debug(twitterContacts);

    }
  }

Debug Log

12:32:41.2 (214750848)|USER_DEBUG|[6]|DEBUG|0030r000007LQEGAA4 12:32:41.2 (215894488)|USER_DEBUG|[7]|DEBUG|Contact:{Id=0030r000007LQEGAA4, IsDeleted=false, MasterRecordId=null, AccountId=0010r000008TFWzAAO, Salutation=null, FirstName=test, LastName=artesto, MiddleName=null, Suffix=null, RecordTypeId=0120r0000008bmPAAQ, OtherStreet=null, OtherCity=null, OtherState=null, OtherPostalCode=null, OtherCountry=null, OtherLatitude=null, OtherLongitude=null, OtherGeocodeAccuracy=null, MailingStreet=null, MailingCity=null, MailingState=null, MailingPostalCode=null, MailingCountry=null, MailingLatitude=null, MailingLongitude=null, MailingGeocodeAccuracy=null, Phone=null, Fax=null, MobilePhone=null, HomePhone=null, OtherPhone=null, AssistantPhone=null, ReportsToId=null, Email=null, Title=null, Department=null, AssistantName=null, LeadSource=null, Birthdate=null, Description=null, OwnerId=0052E00000J0MKUQA3, HasOptedOutOfEmail=false, HasOptedOutOfFax=false, DoNotCall=false, CreatedDate=2019-05-10 19:32:41, CreatedById=0052E00000J0MKUQA3, LastModifiedDate=2019-05-10 19:32:41, LastModifiedById=0052E00000J0MKUQA3, SystemModstamp=2019-05-10 19:32:41, LastActivityDate=null, LastCURequestDate=null, LastCUUpdateDate=null, LastViewedDate=null, LastReferencedDate=null, EmailBouncedReason=null, EmailBouncedDate=null, IsEmailBounced=false, PhotoUrl=null, Jigsaw=null, JigsawContactId=null, Twitter_Handle__c=whole30, Twitter_Followers__c=null} 12:32:41.2 (215989248)|USER_DEBUG|[9]|DEBUG|0030r000007LQEGAA4 12:32:41.2 (216131271)|USER_DEBUG|[12]|DEBUG|melissa_hartwig0030r000007LQEGAA4 12:32:41.2 (216154924)|USER_DEBUG|[13]|DEBUG|{0030r000007LQEGAA4=melissa_hartwig} 12:32:41.2 (216189317)|USER_DEBUG|[17]|DEBUG|1 12:32:41.2 (217644134)|USER_DEBUG|[23]|DEBUG|{0030r000007LQEGAA4=melissa_hartwig} 12:32:41.221 (232025334)|FATAL_ERROR|System.ListException: List index out of bounds: 0

Class.TwitterProvider.: line 15, column 1 Class.TwitterCallout.updateFollowers: line 4, column 1 12:32:41.221 (232042788)|FATAL_ERROR|System.ListException: List index out of bounds: 0

Class.TwitterProvider.: line 15, column 1 Class.TwitterCallout.updateFollowers: line 4, column 1

Twitter Callout Class

public class TwitterCallout {
    @future(callout = true)
    public static void updateFollowers(Map<Id, String> twitterContacts){
        TwitterProvider twitterProvider = new TwitterProvider();
        List<Contact> contacts = new List<Contact>();

        for (Id contactId : twitterContacts.keySet()) {
            //this is where the callout happens - the API only returns one record but serializes it as an array
            List<TwitterProvider.UserRecord> userRecord = twitterProvider.FetchUserFromServer(twitterContacts.get(contactId));
            contacts.add(new Contact(Id = contactId, Twitter_Followers__c = userRecord[0].Followers_Count));
            System.debug(twitterContacts);
        }
        update contacts;


    }




}

Twitter Provider

public class TwitterProvider{

        public Twitter_Callout_Setting__mdt twitterCalloutSetting;
        public String accessToken;


        public TwitterProvider() {

            twitterCalloutSetting = [
                SELECT EndPoint__c, ConsumerKey__c, ConsumerSecret__c
                FROM Twitter_Callout_Setting__mdt
                WHERE DeveloperName = 'users'
                LIMIT 1

            ][0];

            accessToken =this.GetToken();

         }

         public List<UserRecord> FetchUserFromServer(String screenName){

         //Prepare and execute the callout
         HttpRequest req = new HttpRequest();
         req.setMethod('GET');
         req.setEndpoint(twitterCalloutSetting.Endpoint__c + screenName);
         req.setHeader('Authorization', 'Bearer'+ accessToken);
         HttpResponse res = new Http().send(req);

         //SUCCESS?, We expect to get back a 200 with json formatted as {"id": "123456"}
         if (res.getStatusCode() == 200) {
             System.debug('FetchUserFromServer.res.getBody(): ' + res.getBody());
             list<UserRecord> userRecord = (List<UserRecord>) JSON.deserialize('[' + res.getBody() + ']', List<UserRecord>.class);

             return userRecord;

            }   

            //Failure: We get back a non desired status code. log it for debugging and return null
            System.debug('TwitterProvider:FetchUserFromServer() Error callingTwitter /users/show.json. ' + res.toString()) ;
            return null;

          }


       public String GetToken(){

       String consumerKey = EncodingUtil.urlEncode(twitterCalloutSetting.ConsumerKey__c, 'UTF-8');
       String consumerSecret = EncodingUtil.urlEncode(twitterCalloutSetting.ConsumerSecret__c, 'UTF-8');
       Blob bearerToken = Blob.valueOf(consumerKey + ':' + consumerSecret);

       //Create the Authorization Header
       String authorizationHeader = 'Basic ' + EncodingUtil.base64Encode(bearerToken);

       //Prepare and execute the callout
       HttpRequest req = new HttpRequest();
       req.setEndpoint('https://api.twitter.com/oauth2/token');
       req.setMethod('POST');
       req.setHeader('Authorization', authorizationHeader);
       req.setBody('grant_type=client_credentials');
       HttpResponse res = new Http().send(req);

       //SUCCESS?, we expect to get back a 200 with json formatted as {"id":"123456"}
       if (res.getStatusCode() == 200){
       JSONParser parser = JSON.createParser(res.getBody());
       while (parser.nextToken() != null) {
           if (parser.getCurrentToken() == JSONToken.FIELD_NAME){
               String fieldName = parser.getText();
               parser.nextToken();
               if (fieldName == 'access_token'){
                   accessToken = parser.getText();

                               }
                           }


                   }
                      System.debug('GetToken.res.getBody(): ' + res.getBody());
                      return accessToken;



            }

             //FAILURE: We got back a non desired ststus code. Log it for debugging and return null
             System.debug('TwitterProvider:GetToken() Error calling Twitter /oauth2/token. ' + res.toString());
             return null;
       }

       public virtual class UserRecord {

           public String Id {get; set; } //"96848570"
           public String Name { get; set;} // "Sean Plott"
           public String Screen_Name {get; set; } // "day9tv"      
           public String Description {get; set; } // "Learn lots. "
           public String Followers_Count {get; set; } //"215645"



           }







       }
  • The debug log indicates that your error is thrown by the TwitterCallout class, which you have not included here. Please edit your post to include it. – Adrian Larson May 10 at 19:47
  • i added the other two classes (Twitter provider and Twitter callout) as well – Clint SCC May 10 at 19:56
  • 2
    Based on your title saying that this happens in a test class and the classname TwitterCallout, perhaps you need to implement a callout mock? – Thomas Taylor May 10 at 19:56
1

A likely cause of your current error is this code:

        twitterCalloutSetting = [
            SELECT EndPoint__c, ConsumerKey__c, ConsumerSecret__c
            FROM Twitter_Callout_Setting__mdt
            WHERE DeveloperName = 'users'
            LIMIT 1

        ][0];

that assumes there are 1 or more records. See this Testing Custom Metadata Types article for some approaches to testing when MDTs are involved.

But as Adrian suggests, once you are past this error, the next is likely to be that HTTP callouts don't work in tests and one way to address that is described in Testing HTTP Callouts.

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.