Как создать нового лидера в SalesForce с помощью библиотеки TwilioForce APEX

Всем доброе утро,

Прежде всего, Force.com IDE и Salesforce - это новый для меня набор навыков. Я пытаюсь использовать библиотеку TwilioForce APEX: https://www.twilio.com/docs/salesforce/install

для создания нового лида Salesforce для каждого входящего звонка в учетной записи Twilio моего клиента. Я дошел до создания нового проекта Force.com в Eclipse, скопировав в проект компоненты, классы и страницы Twilioforce, но мне нужны некоторые рекомендации по написанию логики для создания лида.

У меня есть вопросы: 1. Можете ли вы предоставить ссылки на справочные материалы, демонстрирующие, как программно создать нового потенциального клиента в Salesforce? 2. Как мне протестировать компоненты TwilioForce, особенно те, которые я изменил, чтобы отразить номер телефона Twilio и токен моего клиента? Можно ли их вызывать из проекта Force.com в Eclipse или их нужно вызывать из моей учетной записи developer.org? 3. Как только я выясню, как выполнить вышеуказанное создание лида из входящих вызовов Twilio, как мне развернуть созданную мной кодовую базу для моего клиента?

Спасибо, Сид

РЕДАКТИРОВАТЬ: EyeScream, ваш образец очень помог. Вот класс TwilioRestResponse, поставляемый с кодовой базой TwilioForce:

public class TwilioRestResponse {

private String responseText;
private integer httpStatus;
private String url;
private String queryString;
private boolean error;


public TwilioRestResponse(String url, String text, integer status){
    Pattern p = Pattern.compile('([^?]+)\\??(.*)');
    Matcher m = p.matcher(url);
    m.matches();
    this.url = m.group(1);
    this.queryString = m.group(2);
    this.responseText = text;
    this.httpStatus=status;
    this.error = (status>=400);  
}

// getters and setters 
public String getResponseText() {
    return responseText;
}
public void setResponseText(String responseText) {
    this.responseText = responseText;
}
public integer getHttpStatus() {
    return httpStatus;
}
public void setHttpStatus(integer httpStatus) {
    this.httpStatus = httpStatus;
}
public String getUrl() {
    return url;
}
public void setUrl(String url) {
    this.url = url;
}
public String getQueryString() {
    return queryString;
}
public void setQueryString(String queryString) {
    this.queryString = queryString;
}
public boolean isError() {
    return error;
}
public void setError(boolean error) {
    this.error = error;
}   
}

Также существует класс CallsXmlParser, как показано ниже:

public class CallsXmlParser{
//All Parsed records will be in this list
public List<Call> listRecords = new List<Call>();
//Data Model to store all response elements
public class Call{
    public string Sid{get;set;}
    public string DateCreated{get;set;}
    public string DateUpdated{get;set;}
    public string CallSegmentSid{get;set;}
    public string AccountSid{get;set;}
    public string Called{get;set;}
    public string Caller{get;set;}
    public string PhoneNumberSid{get;set;}
    public string Status{get;set;}
    public string StartTime{get;set;}
    public string EndTime{get;set;}
    public string Duration{get;set;}
    public string Price{get;set;}
    public string Flags{get;set;}
    public string Annotation{get;set;}
}
public CallsXmlParser(){

}
public CallsXmlParser(string data){
    XmlStreamReader xsr = new XmlStreamReader(data);
    listRecords = parse(xsr);
}
public Call[] parse(XmlStreamReader reader) {
    Call[] members = new Call[0];
    while(reader.hasNext()) {
        if (reader.getEventType() == XmlTag.START_ELEMENT) {
           if ('Call' == reader.getLocalName()) {                 
                Call member = parseMember(reader);
                members.add(member);
            }
         }
        reader.next();
    }
    return members;
}
//Parsing Each Call Tag and its nested tags
public Call parseMember(XmlStreamReader reader){
    Call callObject = new Call();
    while(reader.hasNext()) {

       if ('Call' == reader.getLocalName() && reader.getEventType() == XmlTag.END_ELEMENT) {
           break;
        }
        else if('Sid' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.Sid = reader.getText(); 
          }
        }else if('DateCreated' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.DateCreated= reader.getText(); 
          }
        }else if('DateUpdated' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.DateUpdated= reader.getText(); 
          }
        }else if('CallSegmentSid' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.CallSegmentSid= reader.getText(); 
          }
        }else if('AccountSid' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.AccountSid= reader.getText(); 
          }
        }else if('Called' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.Called= reader.getText(); 
          }
        }else if('Caller' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.Caller= reader.getText(); 
          }
        }else if('PhoneNumberSid' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.PhoneNumberSid= reader.getText(); 
          }
        }else if('Status' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.Status = reader.getText(); 
          }
        }else if('StartTime' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.StartTime = reader.getText(); 
          }
        }else if('EndTime' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.EndTime = reader.getText(); 
          }
        }else if('Duration' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.Duration = reader.getText(); 
          }
        }else if('Price' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.Price = reader.getText(); 
          }
        }else if('Flags' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.Flags = reader.getText(); 
          }
        }else if('Annotation' == reader.getLocalName() && reader.getEventType() == XmlTag.START_ELEMENT){
          reader.next();                   
          if(reader.getEventType() == XmlTag.CHARACTERS) { 
            callObject.Annotation = reader.getText(); 
          }
        }

       reader.next();
   }
   return callObject;
} 
}

Мой самый простой класс insertLead выглядит следующим образом:

public with sharing class insertLead {
Lead1 = new Lead(Phone='TwilioRestResponse.GetResponseText');
}

Мне нужно прочитать номер телефона и имя вызывающего абонента из классов TwilioRestResponse или CallXmlParser и вставить в соответствующие поля в новом лиде. Каков подходящий синтаксис для ссылки на CallObject.PhoneNumberSid в моем новом лиде? Как вариант, лучше разобрать TwilioRestResponse? Если да, как мне выбрать только номер телефона и имя вызывающего абонента из GetResponseText?

Еще раз спасибо, Сид


person SidC    schedule 15.12.2010    source источник


Ответы (2)


Ответ обновлен, прокрутите вниз

public class SidTest {

    //  1. How to create a new Lead programatically on Force.com (in Apex)
    /* This method is marked as test method, meaning that you can use it to run tests but in the end no data will be saved
        (transaction rollback). You'll need similar code in a class that intercepts messages from Twilio.
    */
    public static testMethod void insertLead(){
        Lead l = new Lead(FirstName='Test', LastName='Lead', Email='[email protected]', Company='test', NumberofEmployees=7);
        insert l;

        // 2. How do I test the TwilioForce components, especially those I've changed to reflect my client's Twilio phone number and token?
    /*  Not sure what do you mean, but most likely by writing test classes like this one and checking their test code coverage.
        See also http://stackoverflow.com/questions/4372202/how-to-unit-test-works-in-salesforce/4381941
        Below sample test that checks if our insert above succeeded.
        You can run it from Eclipse (preferred) or Salesforce GUI in Setup->Develop->Apex classes.
    */
        Lead[] leads = [SELECT Name, Email FROM Lead WHERE Name = 'Test Lead'];

        System.debug(leads);    // if you want to see results in detailed debug log
        System.assertEquals(1, leads.size());
        System.assertEquals('[email protected]', leads[0].Email);
    }
}

Вот результат, когда вы запустите его в Eclipse: http://dl.dropbox.com/u/709568/stackoverflow/Sid.png.

Что касается последнего вопроса: когда вы будете довольны функциональностью, протестированной в вашей версии для разработчиков, вы должны попросить клиента предоставить вам доступ к «песочнице» своей организации. Вы можете создать в Eclipse новый проект, указывающий на эту песочницу, и просто создать в нем все классы, запустить тесты с подмножеством реальных данных и т.д. охват в автоматизированных тестах начнет иметь значение.

В качестве альтернативы вы можете создать пакет со своим кодом и продавать его на AppExchange как плагин для Salesforce, который любой может загрузить в свою организацию. Вы, конечно, можете взимать плату за это. Но пока это выглядит слишком большим скачком ...

Надеюсь, это поможет вам начать.


Обновление. Вот быстрый и грязный класс на основе TestCallsXmlParser, который включен в пакет кода, который вы используете:

public class TestCallsXmlParser{
    @isTest
    public static void TestCallsXmlParserMethod1(){
        CallsXmlParser callxml = new CallsXmlParser();
        String xmlData = '<TwilioResponse> <Calls page=\"0\" numpages=\"1\" pagesize=\"50\" total=\"38\" start=\"0\" end=\"37\"> <Call> <Sid>CA42ed11f93dc08b952027ffbc406d0868</Sid> <DateCreated>Sat, 07 Feb 2009 13:15:19 -0800</DateCreated><DateUpdated>Sat, 07 Feb 2009 13:15:19 -0800</DateUpdated><CallSegmentSid/>';
        xmlData = xmlData + '<AccountSid>AC309475e5fede1b49e100272a8640f438</AccountSid><Called>4159633717</Called><Caller>4156767925</Caller><PhoneNumberSid>PN01234567890123456789012345678900</PhoneNumberSid><Status>2</Status><StartTime>Thu, 03 Apr 2008 04:36:33 -0400</StartTime><EndTime>Thu, 03 Apr 2008 04:36:47 -0400</EndTime>';
        xmlData = xmlData + '<Duration>14</Duration><Price/><Flags>1</Flags></Call>';
        xmlData = xmlData + '<Call><Sid>CA751e8fa0a0105cf26a0d7a9775fb4bfb</Sid><DateCreated>Sat, 07 Feb 2009 13:15:19 -0800</DateCreated><DateUpdated>Sat, 07 Feb 2009 13:15:19 -0800</DateUpdated><CallSegmentSid/>';
        xmlData = xmlData + '<AccountSid>AC309475e5fede1b49e100272a8640f438</AccountSid><Called>2064287985</Called><Caller>4156767925</Caller><PhoneNumberSid>PNd59c2ba27ef48264773edb90476d1674</PhoneNumberSid><Status>2</Status>';
        xmlData = xmlData + '<StartTime>Thu, 03 Apr 2008 01:37:05 -0400</StartTime><EndTime>Thu, 03 Apr 2008 01:37:40 -0400</EndTime><Duration>35</Duration><Price/> <Flags>1</Flags> </Call></Calls></TwilioResponse> ';
        CallsXmlParser callxml1 = new CallsXmlParser(xmlData);

        // eyescream's modification starts here

        // list to store our leads and bulk save them in blocks up to 100 records at 1 insert
        List<Lead> leads = new List<Lead>();

        for(CallsXmlParser.Call c : callxml1.listRecords) {
            System.debug('2 new Leads will be created from phone numbers: ' + c.Caller + ', ' + c.Called);
            leads.add(new Lead(MobilePhone = c.Caller, Company='x', LastName='x')); // Company & Last Name are mandatory fields
            leads.add(new Lead(MobilePhone = c.Called, Company='y', LastName='y')); // without them insert will fail.

            if(leads.size() == 100) { // 100 is the limit of records saved at once. Inserting in batches speeds up execution.
                insert leads;
                leads.clear();
            }
        }

        // If we have any leftovers, we'll insert them too.
        if(leads.size() > 0) {
            insert leads;
            leads.clear();
        }
    }
}

Конечно, вы можете улучшить его, проверить дубликаты, заполнить реальными именами и т.д., но примерно так вы можете использовать проанализированные результаты. Я не думаю, что сам анализ XML был бы хорошим вариантом ... Ваш вопрос предполагает, что в XML будет больше полей (или существующие будут использоваться для хранения разных данных), но, вероятно, вы сможете расширить парсер соответственно.

person eyescream    schedule 15.12.2010
comment
К сожалению, я получаю ошибку компиляции в строке для (CallsXmlParser.Call c: callxml1.ListRecords) {ожидая правой фигурной скобки - person SidC; 17.12.2010
comment
Я только что создал в своей организации 2 класса из библиотеки Twilio: TestCallsXmlParser и CallsXmlParser. Как в последних версиях из транка, так и работает без проблем ... Может, в названиях классов допустили опечатку? Является ли внутренний класс Calls общедоступным? Может быть, у вас старая версия плагина eclipse / force.com? Я использую версию плагина Summer '10 (19.0.0), и моя полная кодовая база здесь для справки: dl.dropbox.com/u/709568/stackoverflow/ - person eyescream; 17.12.2010
comment
Моя проблема :) У меня не хватало правой фигурной скобки в самом конце файла. Теперь, когда он создан, как мне запустить его в моей учетной записи developerforce? Огромное спасибо!! - person SidC; 17.12.2010
comment
Щелкните правой кнопкой мыши по нему в левом меню (с каталогом классов и т. Д., Как тот, который вы видите на моем снимке экрана), затем Force.com- ›Запустить тесты. Или щелкните правой кнопкой мыши весь каталог классов. Теперь ... Это всего лишь пример, ваш реальный код должен быть разделен на класс, который фактически читает XML и вставляет данные, и второй (тестовый) класс, используемый только в модульном тестировании ... но это должно помочь вам начать. - person eyescream; 17.12.2010

Если у кого-то возникнут проблемы с развертыванием библиотеки twilio, здесь приведены подробные сведения, которые решают большинство задач. Как бы то ни было, если бы это могло помочь http://redcurrantscloud.blogspot.in/

Спасибо.

person Chris    schedule 26.07.2013