2

I'm building aura app and I have trouble sending custom body to HTTP callout. I have problem constructing custom body for HTTP API callout in Apex. I want to get data from third party system (piwik.pro) and I need to send them the website_id, which columns I want to fetch, dateFrom-dateTo and optionally if I want to filter fetched data, I need to send them the filters.

I have successfully fetched session data from Postman using this JSON body (from their official HTTP API page) https://prnt.sc/xUf_GoXvTC79:

{
"website_id": "47cd13e8-42ac-4787-9431-53df4db449d8",
"columns": [
    {
    "column_id": "session_total_page_views"
    },
    {
    "column_id": "device_type"
    }
],
"date_from": "2020-01-10",
"date_to": "2020-02-10",
"filters": {
    "operator": "and",
    "conditions": [
    {
        "column_id": "browser_name",
        "condition": {
        "operator": "eq",
        "value": "FF"
        }
    }
    ]
},
"offset": 0,
"limit": 100,
"format": "json"
}

First is webSiteId and user chooses that through picklist on the frontend, next is what is confusing me. There is list of columns that user can choose from to fetch data about (in this example there're 2 objects but that is a variable - sometimes 1 sometimes 10). The dates (date_from, date_to) also need to be set as a variables so that is not hardcoded.

What I've tried so far:

I've tried using http://json2apex.herokuapp.com/ to generate my class as I've read multiple questions that suggested that but I still don't understand how can I use that to create my request body.

Also, I tried to hardcode

    String json = '{'+
            '  \"website_id\": \"47cd13e8-42ac-4787-9431-53df4db449d8\",'+
            '  \"columns\": ['+
            '    {'+
            '      \"column_id\": \"session_total_page_views\"'+
            '    },'+
            '    {'+
            '      \"column_id\": \"device_type\"'+
            '    }'+
            '  ],'+
            '  \"date_from\": \"2020-01-10\",'+
            '  \"date_to\": \"2020-02-10\",'+
            '  \"filters\": {'+
            '    \"operator\": \"and\",'+
            '    \"conditions\": ['+
            '      {'+
            '        \"column_id\": \"browser_name\",'+
            '        \"condition\": {'+
            '          \"operator\": \"eq\",'+
            '          \"value\": \"FF\"'+
            '        }'+
            '      }'+
            '    ]'+
            '  },'+
            '  \"offset\": 0,'+
            '  \"limit\": 100,'+
            '  \"format\": \"json\"'+
            '}'; 
HttpRequest req = new HttpRequest();
req.setBody(json);

But it doesn't work for me, because I'm hardcoding the number of columns, the columns, the dateFrom, dateTo, filter... Virtually everything.

So, what I'm asking is - how can I code this so I don't hardcode number of columns, columns and let the user choose how many she/he wants and choose the dateFrom, dateTo and serialize all the and send the String to req.setBody(String);

UPDATE 08/24/2022

I got method that has args that I'm sending to it from aura components attributes.

//================================ FECHES SESSION DATA ========================================
    @AuraEnabled(cacheable=true)
    public static String fetchSessions (String webSiteId, List<String> stringColumns, String date_from, String date_to, Integer offset, String format, String token){
        try{
            // CUSTOM ENDPOINT
            String customEndpoint = webSiteId + '/api/analytics/v1/sessions/';
            // CUSTOM BODY 
            Data data = new Data();
            data.website_id = webSiteId;
            // data.columns = columns;
            data.date_from = date_From;
            data.date_to = date_to;
            data.offset = offset;
            data.format = format;
            // MAKE THE API CALL
            HttpRequest req = new HttpRequest();
            req.setMethod('POST');
            req.setHeader('Content-Type','json');
            req.setHeader('Authorization','Bearer ' + token);
            req.setEndpoint(customEndpoint);
            req.setBody(JSON.serialize(data));
            
            Http h = new Http();
            HTTPResponse res = h.send(req);
            
            String strResponse = res.getBody();
            if (res.getStatusCode() == 200){ 
                Map<String,Object> results = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
            }
            else {
                return res.getStatus(); //return the responses StatusCode message
            }
            return null; //return the data
        }
        catch (exception ex){
            System.debug(ex.getMessage());
            return ex.getMessage(); //return the exception
        }
    }
}

I instantiate an object of type Data but I can't understand how can I set data.columns when I have List that I accept as an argument and I cannot declare List name = new List(); because I have the Data class with all its sub-classes in separate apxc class. Moreover I tried to copy-paste the Data class into my main class and It gave me the error that I cannot have classes and sub-classes and methods in one class.

1
  • 1
    Please edit your question and show your attempt with json2apex
    – identigral
    Commented Aug 16, 2022 at 15:48

1 Answer 1

6

We can make this dynamic by defining class structures, i.e.:

public class Data {
    public String website_id;
    public List<Column> columns;
    public String date_from;
    public String date_to;
    public Integer offset;
    public String format;
    public Filter filters;

    public class Column {
        public String column_id;
    }

    public class Filter {
        public String operator;
        public List<Condition> conditions;
    }

    public class Condition {
        public String column_id;
        public List<Operation> conditions;
    }
    
    public class Operation {
        public String operator;
        public String value;
    }
}

Then, we can deserialize the payload into this structure like so:

Data responseData = (Data) JSON.deserialize(payload, Data.class);

Conversely, you can instantiate your Data object, assign its properties and then serialize it into JSON for your HTTP request's body:

Data d = new Data();
d.website_id = 'someid';
// assign other properties...

HttpRequest req = new HttpRequest();
req.setBody(JSON.serialize(d));

For the column attribute, you'd need to iterate over the string array (myColumnStringList in this example) and initialize column objects, i.e.:

List<Column> columns = new List<Column>();
for (String column_id : myColumnStringList) {
    Column c = new Column();
    c.column_id = column_id;
    columns.add(c);
}
5
  • How can I send the body to HTTP POST method request? I understand that setBody accepts only string argument and I need to json.serialize(payload).
    – Stojchesky
    Commented Aug 17, 2022 at 7:26
  • @Stojcheski - you would instantiate the object and assign whatever values needed - I've updated my answer with an example. Commented Aug 17, 2022 at 15:17
  • One more question. I need to set the value of the List<Column> columns to my entry List<String> columns and I get illegal assignment from List to List which is to be expected. So, how do I make the method accept List<Column>?
    – Stojchesky
    Commented Aug 17, 2022 at 18:47
  • The JSON example provided shows that columns is not an array of Strings, but an array of Objects - this is why we defined an inner class called Column. To convert from your List<String> variable, you may need to iterate over its contents and instantiate column objects. I've provided an example above in my answer. Commented Aug 17, 2022 at 22:17
  • Hey @JesseWheeler I've updated my question with my method and I still cannot solve this problem of how can I send data.columns in my method. Can you provide further explanation on your thought process. I'm sorry for still bothering you about this. Thank you.
    – Stojchesky
    Commented Aug 24, 2022 at 18:14

You must log in to answer this question.

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