Home > Software engineering >  Google Apps Script Working on backend but not on sheets
Google Apps Script Working on backend but not on sheets

Time:11-29

I am trying to create a script that pulls from the coin market cap API and displays the current price. The script is working fine on the back end when I assign the variable a value. However, when I try to run the function on sheets the returned value is null.

function marketview(ticker) {
  var url = "https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?CMC_PRO_API_KEY=XXX&symbol="   ticker;
  var data = UrlFetchApp.fetch(url);
  const jsondata = JSON.parse(data);

  Logger.log(jsondata.data[ticker].quote['USD'].price)
  
}

My execution logs show that the scripts are running, but when when I use the function and try and quote ETH for example, the script is running for BTC.

When I do this on the backend and assign ETH the script works fine and returns the right quote. Any ideas on what I'm missing?

CodePudding user response:

I did the same with coingecko API and add an issue having all my requests being rejected with quota exceeded error.

I understood that Google sheets servers IPs address were already spamming coingecko server. (I was obviously not the only one to try this).

This is why I used an external service like apify.com to pull the data and re-expose data over their API.

This is my AppScripts coingecko.gs:

/**
 * get latest coingecko market prices dataset
 */
async function GET_COINGECKO_PRICES(key, actor) {
  const coinGeckoUrl = `https://api.apify.com/v2/acts/${actor}/runs/last/dataset/items?token=${key}&status=SUCCEEDED`
  return ImportJSON(coinGeckoUrl);
}

You need ImportJSON function, available here: https://github.com/bradjasper/ImportJSON/blob/master/ImportJSON.gs

Then in a cell I write: =GET_COINGECKO_PRICES(APIFY_API_KEY,APIFY_COINGECKO_MARKET_PRICES), you will have to create two field named APIFY_API_KEY and APIFY_COINGECKO_MARKET_PRICES in order for this to work.

Then register on apify.com, then you'll have to create an actor by forking apify-webscraper actor.

I set the StartURLs with https://api.coingecko.com/api/v3/coins/list, this will give me the total number of existing crypto (approx 11000 as of today), and number of page so I can run the request concurrently (rate limit is 10 concurrent requests on coingecko), then I just replace /list with /market and set the proper limit to get all the pages I need.

I use the following for the tasks page function:

async function pageFunction(context) {
    let marketPrices = [];
    const ENABLE_CONCURRENCY_BATCH = true;
    const PRICE_CHANGE_PERCENTAGE = ['1h', '24h', '7d'];
    const MAX_PAGE_TO_SCRAP = 10;
    const MAX_PER_PAGE = 250;
    const MAX_CONCURRENCY_BATCH_LIMIT = 10;
    await context.WaitFor(5000);
    const cryptoList = readJson();
    const totalPage = Math.ceil(cryptoList.length / MAX_PER_PAGE);

    context.log.info(`[Coingecko total cryptos count: ${cryptoList.length} (${totalPage} pages)]`)
    
    function readJson() {
        try {
            const preEl = document.querySelector('body > pre');
            return JSON.parse(preEl.innerText);
        } catch (error) {
            throw Error(`Failed to read JSON: ${error.message}`)
        }
    }


    async function loadPage($page) {
        try {
            const params = {
                vs_currency: 'usd',
                page: $page,
                per_page: MAX_PER_PAGE,
                price_change_percentage: PRICE_CHANGE_PERCENTAGE.join(','),
                sparkline: true,
            }
            
            let pageUrl = `${context.request.url.replace(/\/list$/, '/markets')}?`;
            pageUrl  = [
                `vs_currency=${params.vs_currency}`,
                `page=${params.page}`,
                `per_page=${params.per_page}`,
                `price_change_percentage=${params.price_change_percentage}`,
            ].join('&');
                        
            context.log.info(`GET page ${params.page} URL: ${pageUrl}`);
            const page = await fetch(pageUrl).then((response) => response.json());
            context.log.info(`Done GET page ${params.page} size ${page.length}`);
            marketPrices = [...marketPrices, ...page];
            return page
        } catch (error) {
            throw Error(`Fail to load page ${$page}: ${error.message}`)
        }
    }

    try {
        if (ENABLE_CONCURRENCY_BATCH) {
            const fetchers = Array.from({ length: totalPage }).map((_, i) => {
                const pageIndex = i   1;
                if (pageIndex > MAX_PAGE_TO_SCRAP) {
                    return null;
                }
                return () => loadPage(pageIndex);
            }).filter(Boolean);
            while (fetchers.length) {   
                await Promise.all(
                    fetchers.splice(0, MAX_CONCURRENCY_BATCH_LIMIT).map((f) => f())
                );
            }
        } else {
            let pageIndex = 1
            let page = await loadPage(pageIndex)
            while (page.length !== 0 && page <= MAX_PAGE_TO_SCRAP) {
                pageIndex  = 1
                page = await loadPage(pageIndex)
            }
        }
    } catch (error) {
        context.log.info(`Fetchers failed: ${error.message}`);
    }

    context.log.info(`End: Updated ${marketPrices.length} prices for ${cryptoList.length} cryptos`);
    const data = marketPrices.sort((a, b) => a.id.toLowerCase() > b.id.toLowerCase() ? 1 : -1);
    context.log.info(JSON.stringify(data.find((item) => item.id.toLowerCase() === 'bitcoin')));

    function sanitizer(item) {
        item.symbol = item.symbol.toUpperCase()
        return item;
    }
    return data.map(sanitizer)
}

I presume you are hiting the same issue I had with coinmarketcap, and that you could do the same with it.

CodePudding user response:

You're not return ing anything to the sheet, but just logging it. Return it:

return jsondata.data[ticker].quote['USD'].price
  • Related