0

I'm fairly new to custom development/coding in general. I've created some Stock__c records in my playground and am trying to update their price using an Http callout to IEX Cloud API. What I can't figure out is how to correctly pass data to the apex class to fetch and update their prices. My end goal is to automate this process so their price is updated every hour or something like that. Research just keeps confusing me and I could use some pointers/advice on the approach.

Apex Class (SOQL here is probably wrong, but for debug purposes it seems to work when using hardcoded Ids)

public class HttpStockQuote {
    @future(callout=true)
    public static void getQuotes() {
        // Get the stock
        Stock__c stock = [select id, Ticker__c, Quote__c from Stock__c where Ticker__c <> null];

        String url = 'https://cloud.iexapis.com/stable/stock/' + stock.Ticker__c + '/quote/latestPrice?token=pk_xxxxxxxxxx';
        System.debug(url);

        // Make HTTP callout
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint(url);
        request.setMethod('GET');
        HttpResponse response = http.send(request);

        // Assign new price
        String newquote = response.getBody();
        System.debug('New Quote');
        System.debug(newquote);

        // Update records
        stock.Quote__c = decimal.valueOf(response.getBody());
        update stock;
        System.debug(stock);
    }
 }

Trigger (probably need to be using Trigger.New somewhere but when I pass it to the method, I get incorrect signature void getQuotes(List) error)

trigger GetStockQuoteTrigger on Stock__c (before insert, before update) {
        HttpStockQuote.getQuotes();
    }

Poke holes in this and tell me what is needed to make this work. I've ran into 'Callout from triggers are currently not supported' error as well as the List errors but I just cannot get past this. Open to all suggestions/advice.

1 Answer 1

0

You can't pass in whole objects to a future method, only primitives and collection of primitives. So your code should start out as:

@future(callout=true)
public static void getQuotes(Set<Id> stockIds) {
    // Get the stock
    Stock__c[] stocks = [select Ticker__c from Stock__c where Ticker__c <> null and Id = :stockIds];

And in your trigger:

trigger GetStockQuoteTrigger on Stock__c (after insert, after update) {
    HttpStockQuote.getQuotes(Trigger.newMap.keySet());

Note the use of after events, because in a before event record, there is no Id for you to use.

Note that your code won't work on bulk updates, so you might want to break down your code so it can work on groups of up to 100 records.

trigger GetStockQuoteTrigger on Stock__c (after insert, after update) {
  Set<Id> recordIds = new Set<Id>();
  for(Stock__c record: Trigger.new) {
    if(record.Ticker__c != null && (Trigger.isInsert || record.Ticker__c != Trigger.oldMap.get(record.Id).Ticker__c) {
      recordIds.add(record.Id);
      if(recordIds.size() == 100) {
        HttpStockQuote.getQuotes(recordIds);
        recordIds.clear();
      }
    }
  }
  if(!recordIds.isEmpty()) {
    HttpStockQuote.getQuotes(recordIds);
  }
}

You'll need to rewrite your code to support making up to 100 callouts. To do this, query the records, perform all of the callouts, and then perform an update. Note that we also add a check to see if the callout should be made if the ticker has changed (or was set on insert). This prevents recursion problems that would otherwise kill your future method calls.


Edit: Refactoring for bulk updates

public class HttpStockQuote {
    @future(callout=true)
    public static void getQuotes(Set<Id> stockIds) {
        // Get the stock
        Stock__c[] stocks = [select Ticker__c from Stock__c where Ticker__c <> null and Id = :stockIds];
        for(Stock__c stock: stocks) {
            getQuoteForStock(stock);
        }
        update stocks;
        System.debug(stocks);
    }

    static getQuoteForStock(Stock__c stock) {
        String url = 'https://cloud.iexapis.com/stable/stock/' + stock.Ticker__c + '/quote/latestPrice?token=pk_xxxxxxxxxx';
        System.debug(url);

        // Make HTTP callout
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint(url);
        request.setMethod('GET');
        HttpResponse response = http.send(request);

        // Assign new price
        String newquote = response.getBody();
        System.debug('New Quote');
        System.debug(newquote);

        // Update records
        stock.Quote__c = decimal.valueOf(response.getBody());
    }

}
3
  • Thanks, it'll take me a bit to understand everything here but the approach makes sense. One thing I'm not sure about is rewriting the class to support multiple callouts. How exactly is that done when the URL is dependent on data from each record? I guess I'm just not understanding how callouts are made and updates are made in bulk vs one at a time.
    – tpayne2345
    May 20, 2021 at 18:10
  • @tpayne2345 I reworked your example so you can see how you might refactor to support bulk API calls.
    – sfdcfox
    May 20, 2021 at 18:19
  • Had to adjust for <= 100 record size in the trigger but it's working as expected now. Thanks for the help!
    – tpayne2345
    May 20, 2021 at 20:45

You must log in to answer this question.

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