4

I have a custom APEX REST point /organization-contacts/* that accepts an array of queries to run. This is to try and find matching contacts (based on phone number in this example) in "batch". I realize I am limited by the 100 SOQL queries, but it's better to do this than 100 API calls.

I am new to Java and APEX, and although the code compiles, I am getting a "result":[{"message":" at [line:1, column:44]","errorCode":"JSON_PARSER_ERROR"}],"code":400 [...] error when I ping the endpoint with the following array of data, JSON encoded. My posting headers are correct.

What am I doing wrong in my APEX class that I can't even get to running searchBulk()? I am building the dynamic query (it may/may not contain additional WHERE parameters depending on the data) on my consumer side and passing the fully formed query to the endpoint.

The APEX Class:

@RestResource(urlMapping='/organization-contacts/*')
global with sharing class ContactsSearchRestController {

   global class RequestBody {
       global List<sObject> contactstofind;
   }

@HttpPost   
    global static List<sObject> searchBulk(ContactsSearchRestController.RequestBody req) {
        List<sObject> foundcontacts = new List<sObject>();

        for(sObject contacttofind : req.contactstofind) {
            List<sObject> foundcontact = Database.query(String.valueOf(contacttofind.get('contactquery')));
            //Loop over the found contacts and make sure we add our unique identifier to the results so that
            //our processor knows what searchcontact the results belong to
            for(sObject fc : foundcontact) {
                fc.put('externalid', contacttofind.get('externalid'));
                foundcontacts.add(fc);
            }                
        }

        return foundcontacts;
    }
}

The data being sent:

Array
(
[contactstofind] => Array
    (
        [0] => Array
            (
                [contactquery] => SELECT Id FROM Contact WHERE Phone LIKE '%555%555%7781%' OR MobilePhone LIKE '%555%555%7781%' OR HomePhone LIKE '%555%555%7781%' OR OtherPhone LIKE '%555%555%7781%'
                [externalid] => 65202639238
            )

        [1] => Array
            (
                [contactquery] => SELECT Id FROM Contact WHERE Phone LIKE '%555%555%3993%' OR MobilePhone LIKE '%555%555%3993%' OR HomePhone LIKE '%555%555%3993%' OR OtherPhone LIKE '%555%555%3993%'
                [externalid] => 652054239
            )

    )
)
2
  • I dont have time for a full answer here but here's a hint - ContactsSearchRestController.RequestBody req in your POST argument. SFDC will automatically deserialize the incoming POST body to the type ContactsSearchRestController.RequestBody and place it into variable req. Since it looks like you are sending an array, it won't deserialize into a list of sobjects
    – cropredy
    Aug 20, 2014 at 19:00
  • @crop1645 Thanks for the answer - if I change req to anything but an sObject, I get a Initial term of field expression must be a concrete SObject: Object compile error for the Database.query() line. Any idea what I may be doing wrong with that? Aug 20, 2014 at 19:16

1 Answer 1

7

You can design your own interface using simple Apex classes. So to allow this JSON to be posted:

[
    {"query": "SELECT ...", "externalId": "65202639238"},
    {"query": "SELECT ...", "externalId": "652054239"}
]

you just need a class with matching field names and to make the method signature accept an array of these classes:

@RestResource(urlMapping='/organization-contacts/*')
global with sharing class ContactsSearchRestController {

    public class Item {
        public String query;
        public String externalId;
    }

    @HttpPost   
    global static List<SObject> searchBulk(Item[] items) {
        List<SObject> allSobs = new List<SObject>();
        for (Item i : items) {
            List<SObject> sobs = Database.query(i.query);
            for (SObject sob : sobs) {
                sob.put('externalid', i.externalId);
            }
            allSobs.addAll(sobs);
        }
        return allSobs;
    }
}

The platform will automatically deserialize the JSON and populate the items variable just as it will automatically serialize the List<SObject> return value.

3
  • Works perfectly. Thanks, the Typing is something I'm getting used to. This makes perfect sense. Aug 20, 2014 at 20:21
  • And, free bonus if you switch to XML. The platform will deserialize that, too.
    – sfdcfox
    Aug 20, 2014 at 22:30
  • 1
    +1, but that inner class should also be global. Just had the following error: Result: [COMPILE FAILED]: (WS_Document) Argument type of global method must also be global: WS_Document.InnerHelperClass (Line: 5, Column: -1)
    – Novarg
    Apr 18, 2016 at 8:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .