package core import ( "bytes" "encoding/json" "errors" "hash/fnv" "io" "log" "net/http" "reflect" "strconv" "strings" "time" geohash "github.com/mmcloughlin/geohash" ) var CLOUDRACK_DATEFORMAT string = "20060102" func Hash(s string) uint32 { h := fnv.New32a() h.Write([]byte(s)) return h.Sum32() } func Chunk(array []interface{}, chunkSize int) [][]interface{} { var divided [][]interface{} for i := 0; i < len(array); i += chunkSize { end := i + chunkSize if end > len(array) { end = len(array) } divided = append(divided, array[i:end]) } return divided } //Buld a double array of integer from a max value and a chunkSize func ChunkArray(size int64, chunkSize int64) [][]interface{} { toChunk := make([]interface{}, 0, 0) for i := int64(0); i < size; i++ { toChunk = append(toChunk, i) } return Chunk(toChunk, int(chunkSize)) } //Generats a usinque ID based timestamp func GeneratUniqueId() string { return strconv.FormatInt(int64(Hash(time.Now().Format("20060102150405"))), 10) } /********** *Geo Hasshing **************/ func GeoHash(lat, lng float64) string { return geohash.Encode(lat, lng) } func GeoHashWithPrecision(lat, lng, radius float64) string { prec := builCaracterLength(radius) return geohash.EncodeWithPrecision(lat, lng, prec) } //geohasg precision: https://en.wikipedia.org/wiki/Geohash //1 ±2500 //2 ±630 //3 ±78 //4 ±20 //5 ±2.4 //6 ±0.61 //7 ±0.076 //8 ±0.019 func builCaracterLength(radius float64) uint { precisions := []float64{2500, 630, 78, 20, 2.4, 0.61, 0.076, 0.019} ind := 0 if radius >= precisions[ind] { return uint(ind + 1) } ind = ind + 1 for ind < len(precisions)-2 { if radius >= precisions[ind+1] && radius < precisions[ind] { return uint(ind + 1) } ind = ind + 1 } log.Printf("[CORE][UTILS] Geohash precision for %f KM is %v \n", radius, ind) return uint(ind) } func GeterateDateRange(startDate string, endDate string) []string { start, _ := time.Parse(CLOUDRACK_DATEFORMAT, startDate) end, _ := time.Parse(CLOUDRACK_DATEFORMAT, endDate) diff := int(end.Sub(start).Hours() / 24.0) log.Printf("[CORE][UTILS] Diff between %v and %v => %+v \n", start, end, diff) dates := make([]string, 0, 0) for i := 0; i < diff; i++ { t := start.AddDate(0, 0, i) dates = append(dates, t.Format(CLOUDRACK_DATEFORMAT)) } log.Printf("[CORE][UTILS] Generated date range from %s to %s => %+v \n", startDate, endDate, dates) return dates } /**************** * HTTP WRAPPERS ******************/ type HttpService struct { Endpoint string } type RestOptions struct { SubEndpoint string Headers map[string]string } func HttpInit(endpoint string) HttpService { log.Printf("[CORE][HTTP] initializing HTTP client with endpoint %v\n", endpoint) return HttpService{Endpoint: endpoint} } func processResponse(resp *http.Response) (*http.Response, error) { if resp.StatusCode >= 400 { return resp, errors.New(resp.Status) } return resp, nil } func (s HttpService) HttpPut(id string, object interface{}) (map[string]interface{}, error) { endpoint := s.Endpoint if id != "" { endpoint = endpoint + "/" + id } log.Printf("[CORE][HTTP] PUT %v\n", endpoint) log.Printf("[CORE][HTTP] Body: %+v\n", object) var resp *http.Response bytesRepresentation, err := json.Marshal(object) if err != nil { log.Fatalln(err) } client := &http.Client{} req, err := http.NewRequest(http.MethodPut, endpoint, bytes.NewBuffer(bytesRepresentation)) resp, err = client.Do(req) log.Printf("[CORE][HTTP] PUT RESPONSE %v\n", resp) if err == nil { resp, err = processResponse(resp) } var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) log.Printf("[CORE][HTTP] PUT RESPONSE DECODED %v\n", result) return result, err } func (s HttpService) HttpPost(object interface{}, tpl CloudrackObject, options RestOptions) (interface{}, error) { endpoint := s.Endpoint if options.SubEndpoint != "" { endpoint = endpoint + "/" + options.SubEndpoint } log.Printf("[CORE][HTTP] POST %v\n", endpoint) log.Printf("[CORE][HTTP] Body: %+v\n", object) var resp *http.Response bytesRepresentation, err := json.Marshal(object) if err != nil { log.Fatalln(err) } client := &http.Client{} req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBuffer(bytesRepresentation)) req.Header.Add("Content-Type", "application/json") for headerName, headerValue := range options.Headers { req.Header.Add(headerName, headerValue) } resp, err = client.Do(req) log.Printf("[CORE][HTTP] POST RESPONSE %v\n", resp) if err == nil && resp != nil { resp, err = processResponse(resp) res, _ := ParseBody(resp.Body, tpl) log.Printf("[CORE][HTTP] POST RESPONSE DECODED %v\n", res) return res, err } return tpl, err } func (s HttpService) HttpGet(params map[string]string, tpl CloudrackObject, options RestOptions) (interface{}, error) { endpoint := s.Endpoint if options.SubEndpoint != "" { endpoint = endpoint + "/" + options.SubEndpoint } log.Printf("[CORE][HTTP] GET %v\n", s.Endpoint) var resp *http.Response client := &http.Client{} req, err := http.NewRequest(http.MethodGet, endpoint, nil) if err != nil { log.Fatalln(err) } //Adding params q := req.URL.Query() for key, val := range params { q.Add(key, val) } req.URL.RawQuery = q.Encode() //Workaround: uncode comma to support comma separated params req.URL.RawQuery = strings.ReplaceAll(req.URL.RawQuery, "%2C", ",") log.Printf("[CORE][HTTP] GET REQUEST %v\n", req) resp, err = client.Do(req) log.Printf("[CORE][HTTP] GET RESPONSE %v\n", resp) if err == nil { resp, err = processResponse(resp) //json.NewDecoder(resp.Body).Decode(&res) res, _ := ParseBody(resp.Body, tpl) log.Printf("[CORE][HTTP] GET RESPONSE DECODED %v\n", res) return res, err } return tpl, err } func ParseBody(body io.ReadCloser, p CloudrackObject) (interface{}, error) { log.Printf("[CORE][HTTP] parsing json body into %v", reflect.TypeOf(p)) dec := json.NewDecoder(body) var err error = nil for { //if err = dec.Decode(&p); err == io.EOF { if err, p = p.Decode(*dec); err == io.EOF { break } else if err != nil { break } } if err == io.EOF { err = nil } return p, err } //type of object that is manageable by a micro service type CloudrackObject interface { //ParseBody(body io.ReadCloser) (interface{}, error) Decode(dec json.Decoder) (error, CloudrackObject) }