Home > other >  Having trouble with persistent data in struct due to pointer. How can I grab most recent instance of
Having trouble with persistent data in struct due to pointer. How can I grab most recent instance of

Time:07-16

I have this microservice that prepares data for users to retrieve via API paths.

However, before users can get the requested data, I need to do some data processing. This is how I do it:

I pass a populated struct with data to two functions for processing getDataOriginWeather() and getDataDestinationWeather(). After both functions run, the API server becomes available for users to request data, aka the struct.

The issue is, the struct that users are pulling contains a mix of records from before and after the data has been processed through both functions.

The expected data for the struct should only be data after it's been passed through both functions.

I'm passing this struct from one package to another.

Here is my code.

// the struct I'm working with and that will eventually be get requested by users.
package models

type TravelItenaries struct {
    Origin                string
    Destination           string
    Flight_num            string
    Origin_latitude       string
    Origin_longitude      string
    Destination_latitude  string
    Destination_longitude string
    Coordinates_ori       string
    Coordinates_dest      string
    Temp_c_ori            string
    Temp_f_ori            string
    Temp_c_dest           string
    Temp_f_dest           string
    LastUpdated           string
}

Here is where I work with the data, before allowing it to be sent to the user.

package data

// for brevity I have removed how I populated the struct. I do so by using a CSV file. 

func getDataOriginWeather() (travel *models.TravelItenaries, err error) {
    fmt.Println("Getting origin data from Weather API...")
    // construct url params for weather API calls.
    params := url.Values{
        "key": []string{"xxx"},
        "q":   []string{""},
    }

    // build URL for request
    u := &url.URL{
        Scheme: "https",
        Host:   "api.weatherapi.com",
        Path:   "/v1/current.json",

        // encode params for URL syntax
        RawQuery: params.Encode(),
    }

    client := &http.Client{}

    values := u.Query()
    var responseData models.OriginWeather
    for _, flight := range TravelItenaries {
        values.Set("q", flight.Coordinates_ori)
        u.RawQuery = values.Encode()
        req, err := http.NewRequest("GET", u.String(), nil)
        if err != nil {
            fmt.Println(err)
        }

        resp, err := client.Do(req)
        if err != nil {
            fmt.Println(err)
        }
        defer resp.Body.Close()
        body, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            fmt.Println(err)
        }

        json.Unmarshal(body, &responseData)
        flight.Temp_f_ori = strconv.FormatFloat(responseData.Current.Temp_f_ori, 'g', -1, 64)
        flight.Temp_c_ori = strconv.FormatFloat(responseData.Current.Temp_c_ori, 'g', -1, 64)
        flight.LastUpdated = responseData.Current.LastUpdated
        //fmt.Println(utils.PrettyPrint(flight))
        TravelItenaries = append(TravelItenaries, flight)
    }
    
    return travel, nil

}

// Run processes the data for me.
func Run() {
    fmt.Println("Starting Weather API requests...")
    getDataDestinationWeather() // note that for brevity, I won't preset  getDataDestinationWeather() here because it's basically the exact same function as the one below
    getDataOriginWeather() // this function and getDataDestinationWeather() work on the same struct. 
    fmt.Println("Completed Weather API requests... \n...Data loaded & ready.")
}

The issue for me is passing this struct to my handlers package with the expected data after it's been processed by getDataDestinationWeather() and getDataOriginWeather()

package handlers 

// as you see I'm using a pointer, assuming my problems would be solved. 
// for the most part this does everything I want, except that I'm also getting 
// records in my struct from before it's been processed by getDataOriginWeather()
// and getDataDestinationWeather()
var Data = &data.TravelItenaries

func (s *Server) getWeather(w http.ResponseWriter, r *http.Request) {
    ...
    // Here I'm simply trying to print the struct to the user after it's been processed
    // by data.getDataOriginWeather() and data.getDataDestinationWeather with the expected 
    // data. 
    fmt.Println(utils.PrettyPrint(Data))

}

While var Data = &data.TravelItenaries does contain "expected data", it also contains old records from before the functions.

How can I explicitly pass the struct after it's been processed in the data.Run() to handlers.getWeather() with the processed data. My use of a pointer here is providing mixed results.

Please let me know if you need to see more of my code.

CodePudding user response:

In the "run" function it seems you are:

  • not checking errors - I think you should.
  • working on a mutable global variable - your code will be cleaner and easier to test and debug if you do not.

How can I explicitly pass the struct after it's been processed in the data.Run() to handlers.getWeather() with the processed data. My use of a pointer here is providing mixed results.

Create a struct that contains what is needed to process a call. Implement http.Handler for that struct. Use that when you set up your server.

Example:

package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type TravelHandler struct {
    Data interface{} // the preloaded data you need to serve http
}

func (th TravelHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    err := json.NewEncoder(w).Encode(th.Data)

    if err != nil {
        http.Error(w, "could not encode data", 500)
    }
}

func createTravelHandler() *TravelHandler {
    // populate travel handler

    return &TravelHandler{}
}

func main() {
    th := createTravelHandler() // no mutable global, return the handler from a function (or pipe it through several functions)

    err := http.ListenAndServe("localhost:3000", th)

    log.Println(err)
}
  • Related