Source: index.js

import {header, Urls} from "./constants.js";
import { Periods } from "./periods.js";

/**
 * @class
 */
export class Tefas {
    /**
     * Initializes the wrapper for the specified fund name
     * @constructor
     * @param {string} name Fund Name (Can be upper or lower case)
     */
    constructor(name) {
        if(!name) {
            throw new Error("TEFAS API Error: Argument 'name' cannot be empty!")
        }

        this.name = name.toUpperCase()
    }

    /**
     * Fetches the historical price data of the fund
     * @route /api/funds/fonFiyatBilgiGetir
     * @param {Number} period Can only be weekly (13), YTD(0), 1 month(1), 3 months(3), 6 months(6), 1 year(12), 3 years(36), 5 years(60) <br>
     * Default to 1 month
     * 
     * @returns {JSON} Historical price data as returned by the API
     */
    async fetchPrice(period = Periods.ONE_MONTH) {
        let response = await tefas_fetch(
            `${Urls.base_url}${Urls.price_url}`,
            {
                method: "POST",
                headers: header,
                body: JSON.stringify({
                    "fonKodu": this.name,
                    "dil": "TR",
                    "periyod": String(period)
                })
            },
            this.fetchPrice.name
        )

        return response?.["resultList"]
    }

    /**
     * Fetches the type of the fund
     * @route /api/funds/fonTipiGetir
     * @returns {String} Fund type as returned by the API. <br>
     * Possible results: F (For mutual funds), M (For retirement funds) 
     */
    async fetchFundType() {
        let response = await tefas_fetch(
            `${Urls.base_url}${Urls.type_url}`,
            {
                method: "POST",
                headers: header,
                body: JSON.stringify({
                    "fonKodu": this.name
                })
            },
            this.fetchFundType.name
        )

        return response?.["fonTipi"]
    }

    /**
     * Fetches 8 comparisons (ALTIN, BIST100, BIST30, EUR, MEVDUAT FAIZI, SERBEST SEMSIYE FONU, TUFE, USD) 
     * along with the fund return for the given period
     * 
     * @route /api/funds/fonProfilDtyGetir
     * @param {Number} period - Can only be weekly (13), YTD(0), 1 month(1), 3 months(3), 6 months(6), 1 year(12), 3 years(36), 5 years(60) <br>
     * Default to 1 year
     * 
     * @returns {JSON} An array containing 9 elements
     */
    async fetchComparisons(period = Periods.ONE_YEAR) {
        let response = await tefas_fetch(
            `${Urls.base_url}${Urls.comparisons_url}`,
            {
                method: "POST",
                headers: header,
                body: JSON.stringify({
                    "fonKodu": this.name,
                    "dil": "TR",
                    "periyod": String(period)
                })
            },
            this.fetchComparisons.name
        )

        return response?.["resultList"]
    }

    /**
     * Returns the list of all funds on TEFAS
     * @route /api/statistics/tefas/getFplFonList
     * @returns {JSON} An array containing all funds with their relative informations
     */
    static async fetchFundList() {
        let response = await tefas_fetch(
            `${Urls.base_url}${Urls.fund_list_url}`,
            {
                method: "POST",
                headers: header,
                body: "{}"
            },
            this.fetchFundList.name
        )

        return response?.["data"]
    }

    /**
     * Returns the list of all forex currencies used for the website
     * @route /api/statistics/tefas/getFplDovizList/v2
     * @returns {JSON} An array containing the currencies
     */
    static async fetchForexList() {
        let response = await tefas_fetch(
            `${Urls.base_url}${Urls.forex_list_url}`,
            {
                method: "POST",
                headers: header,
                body: `{"fonKodu":"","dil":"TR"}` // Website actually uses this body
            },
            this.fetchForexList.name
        )

        return response?.["data"]
    }
}

// A handy fetch function to deal with errors
async function tefas_fetch(url, init, func_name) {
        // Send HTTPS request to API
        let response = await fetch(url, init)

        // Check for HTTPS errors
        if(!response.ok) {
            throw new Error(`TEFAS API Error: HTTPS request for ${func_name} failed with status code ${response.status}!`)
        }

        let json_res = await response.json()
        // Check if json body is null or empty
        if(!json_res || Object.keys(json_res).length === 0) {
            throw new Error(`TEFAS API Error: API responded with empty body on ${func_name}!`)
        }
        // Check for API errors
        if(json_res?.["errorCode"] != null || json_res?.["errorMessage"] != null) {
            console.log(json_res)
            throw new Error(`TEFAS API Error: API returned error ${json_res?.["errorCode"]} on ${func_name}! Error message: ${json_res?.["errorMessage"]}`)
        }

        return json_res
}