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)
}