Search code examples
jsongoresponse

My API response body seems to be getting cut-off?


I'm dabbling with GO for the first time and an API I'm using for getting stock prices accepts multiple tickers (think stock ids; NVDA => Nvidia) to cut down on my limited API calls. However when trying to get with multiple tickers, the body seems to get cut off:

{
    "meta ": {
        "requested ": 2,
        "returned ": 2
    },
    "data ": [
        {
            "ticker ": "MSFT ",
            "name ": "Microsoft Corporation ",
            "exchange_short ": null,
            "exchange_long ": null,
            "mic_code ": "IEXG ",
            "currency ": "USD ",
            "price ": 374.72,
            "day_high ": 376.92,
            "day_low ": 373.54,
            "day_open ": 374.94,
            "52_week_high ": 349.84,
            "52_week_low ": 213.43,
            "market_cap ": null,
            "previous_close_price ": 374.58,
            "previous_close_price_time ": "2023-12-22T15:59:56.000000 ",
            "day_change ": 0.04,
            "volume ": 165558,
            "is_extended_hours_price ": false,
            "last_trade_time ": "2023-12-26T16:00:00.000000 "
        },
        {
            "ticker ": ""

Making the exact call directly to the stock API in postman however returns:

{
    "meta": {
        "requested": 2,
        "returned": 2
    },
    "data": [
        {
            "ticker": "MSFT",
            "name": "Microsoft Corporation",
            "exchange_short": null,
            "exchange_long": null,
            "mic_code": "IEXG",
            "currency": "USD",
            "price": 374.72,
            "day_high": 376.92,
            "day_low": 373.54,
            "day_open": 374.94,
            "52_week_high": 349.84,
            "52_week_low": 213.43,
            "market_cap": null,
            "previous_close_price": 374.58,
            "previous_close_price_time": "2023-12-22T15:59:56.000000",
            "day_change": 0.04,
            "volume": 165558,
            "is_extended_hours_price": false,
            "last_trade_time": "2023-12-26T16:00:00.000000"
        },
        {
            "ticker": "PLTR",
            "name": "Palantir Technologies Inc",
            "exchange_short": null,
            "exchange_long": null,
            "mic_code": "IEXG",
            "currency": "USD",
            "price": 17.68,
            "day_high": 17.75,
            "day_low": 17.34,
            "day_open": 17.45,
            "52_week_high": 17.16,
            "52_week_low": 5.92,
            "market_cap": null,
            "previous_close_price": 17.42,
            "previous_close_price_time": "2023-12-22T15:59:56.000000",
            "day_change": 1.47,
            "volume": 236201,
            "is_extended_hours_price": false,
            "last_trade_time": "2023-12-26T15:59:58.000000"
        }
    ]
}

I assume I'm doing something wrong (I've never touched GO before yesterday) Here is the relevant code:

func getStockPriceData(ticker string) []Stock {
    baseURL, _ := url.Parse("https://api.stockdata.org")

    baseURL.Path += "v1/data/quote"

    params := url.Values{}
    params.Add("symbols", ticker)
    params.Add("api_token", apiToken.ApiToken)

    baseURL.RawQuery = params.Encode()

    req, _ := http.NewRequest("GET", baseURL.String(), nil)

    res, _ := http.DefaultClient.Do(req)

    defer res.Body.Close()

    body, err := io.ReadAll(res.Body)
    if err != nil {
        panic(err.Error())
    }

    apiResponseData := ApiResponseStockData{}

    //json.NewDecoder(res.Body).Decode(&apiResponseData)
    err2 := json.Unmarshal(body, &apiResponseData)
    if err2 != nil {
        fmt.Println("whoops:", err2)
    }

    stocks := []Stock{}
    for _, data := range apiResponseData.ResponseStockData {
        stock := Stock{}
        stock.Name = data.Name
        stock.Ticker = data.Ticker
        stock.Price = data.Price
        stock.DayHigh = data.DayHigh
        stock.DayLow = data.DayLow
        stock.DayOpen = data.DayOpen
        stock.PreviousClosePrice = data.PreviousClosePrice
        stock.DayChange = data.DayChange
        stocks = append(stocks, stock)
    }
    return stocks
}

Structs for context:

type Stock struct {
    Name               string  `json:"name"`
    Ticker             string  `json:"ticker"`
    Price              float64 `json:"price"`
    DayHigh            float64 `json:"day_high"`
    DayLow             float64 `json:"day_low"`
    DayOpen            float64 `json:"day_open"`
    DayChange          float64 `json:"day_change"`
    PreviousClosePrice float64 `json:"previous_close_price"`
}

type ApiResponseStockData struct {
    ResponseStockData []ResponseData `json:"data"`
}

type ResponseData struct {
    Name                 string    `json:"name"`
    Ticker               string    `json:"ticker"`
    ExchangeShort        bool      `json:"exchange_short"`
    ExchangeLong         bool      `json:"exchange_long"`
    MicCode              string    `json:"mic_code"`
    Currency             string    `json:"currency"`
    Price                float64   `json:"price"`
    DayHigh              float64   `json:"day_high"`
    DayLow               float64   `json:"day_low"`
    DayOpen              float64   `json:"day_open"`
    DayChange            float64   `json:"day_change"`
    YearHigh             float64   `json:"52_week_high"`
    YearLow              float64   `json:"52_week_low"`
    MarketCap            float64   `json:"market_cap"`
    PreviousClosePrice   float64   `json:"previous_close_price"`
    PreviousCloseTime    time.Time `json:"previous_close_time"`
    Volume               float64   `json:"volume"`
    IsExtendedHoursPrice bool      `json:"is_extended_hours_price"`
    LastTradeTime        time.Time `json:"last_trade_time"`
}

After reviewing a few threads I tried using both json.Unmarshal and json.NewDecoder().Decode() but still no luck. The cutoff string from above comes from body, and when using either the Decode or Unmarshal it successfully adds the single full object to apiResponseData. I could understand if this was a colossal response, but this seems pretty simple, so I have to assume I'm making a pretty glaring rookie error.

Any help would be greatly appreciated.


Solution

  • You are trying to parse a time string using a format that includes the time zone offset ("Z07:00").

    You need to adjust the time layout to the following.

    layout := "2006-01-02T15:04:05.000000"
    

    Your error handling is not proper that is why you are able to exit with incorrect responses as well.