add base handler and refactor H
This commit is contained in:
parent
fb4d31afba
commit
c7024d7f4e
3
service_a/internal/app/app.go
Normal file
3
service_a/internal/app/app.go
Normal file
@ -0,0 +1,3 @@
|
||||
package app
|
||||
|
||||
type H map[string]any
|
||||
16
service_a/internal/domains/handlers.go
Normal file
16
service_a/internal/domains/handlers.go
Normal file
@ -0,0 +1,16 @@
|
||||
package domains
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
BaseHandler struct{}
|
||||
)
|
||||
|
||||
func (bh *BaseHandler) ToJSON(w http.ResponseWriter, statusCode int, data any) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(statusCode)
|
||||
json.NewEncoder(w).Encode(data)
|
||||
}
|
||||
@ -5,8 +5,6 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type H map[string]any
|
||||
|
||||
type MeteoData struct {
|
||||
Timestamp time.Time `csv:"fecha"`
|
||||
Location string `csv:"ciudad"`
|
||||
@ -29,18 +27,52 @@ type FileStats struct {
|
||||
FileChecksum string `json:"file_checksum"`
|
||||
}
|
||||
|
||||
type GetMeteoData struct {
|
||||
Location string
|
||||
From string
|
||||
To string
|
||||
Page int
|
||||
Limit int
|
||||
}
|
||||
|
||||
func (mt *GetMeteoData) Validate() error {
|
||||
if mt.Location == "" {
|
||||
return ErrMissingOrInvalidLocation
|
||||
}
|
||||
|
||||
if mt.From == "" {
|
||||
return ErrMissingOrInvalidFromDate
|
||||
}
|
||||
|
||||
if mt.To == "" {
|
||||
return ErrMissingOrInvalidToDate
|
||||
}
|
||||
|
||||
if _, err := time.Parse("2006-01-02", mt.From); err != nil {
|
||||
return ErrMissingOrInvalidFromDate
|
||||
}
|
||||
|
||||
if _, err := time.Parse("2006-01-02", mt.To); err != nil {
|
||||
return ErrMissingOrInvalidToDate
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
ErrCannotParseFile = errors.New("cannot parse file")
|
||||
ErrValidateRecord = errors.New("error validating record")
|
||||
ErrRecordNotValid = errors.New("record not valid")
|
||||
ErrReadingCSVHeader = errors.New("error reading CSV header")
|
||||
ErrReadingCSVRow = errors.New("error reading CSV row")
|
||||
ErrMissingOrInvalidDateField = errors.New("missing or invalid date field")
|
||||
ErrMissingOrInvalidCityField = errors.New("missing or invalid city field")
|
||||
ErrMissingOrInvalidMaxTemp = errors.New("missing or invalid max temp field")
|
||||
ErrMissingOrInvalidMinTemp = errors.New("missing or invalid min temp field")
|
||||
ErrMissingOrInvalidRainfall = errors.New("missing or invalid rainfall field")
|
||||
ErrMissingOrInvalidCloudiness = errors.New("missing or invalid cloudiness field")
|
||||
ErrMissingOrInvalidDate = errors.New("missing or invalid date")
|
||||
ErrMissingOrInvalidFromDate = errors.New("missing or invalid from date")
|
||||
ErrMissingOrInvalidToDate = errors.New("missing or invalid to date")
|
||||
ErrMissingOrInvalidLocation = errors.New("missing or invalid location")
|
||||
ErrMissingOrInvalidMaxTemp = errors.New("missing or invalid max temp")
|
||||
ErrMissingOrInvalidMinTemp = errors.New("missing or invalid min temp")
|
||||
ErrMissingOrInvalidRainfall = errors.New("missing or invalid rainfall")
|
||||
ErrMissingOrInvalidCloudiness = errors.New("missing or invalid cloudiness")
|
||||
ErrMaxTempOutOfRange = errors.New("max temp out of range (must be <= 60°C)")
|
||||
ErrMinTempOutOfRange = errors.New("min temp out of range (must be >= -20°C)")
|
||||
ErrRainfallOutOfRange = errors.New("rainfall out of range (must be 0-500 mm)")
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"servicea/internal/app"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -69,7 +70,7 @@ func (c *CSV) Parse(r io.Reader) ([]MeteoData, []RejectedMeteoData, error) {
|
||||
|
||||
rowValue := strings.Join(row, ";")
|
||||
|
||||
record := make(H)
|
||||
record := make(app.H)
|
||||
for i, value := range row {
|
||||
if i < len(header) {
|
||||
record[header[i]] = value
|
||||
@ -99,17 +100,17 @@ func (c *CSV) Parse(r io.Reader) ([]MeteoData, []RejectedMeteoData, error) {
|
||||
return meteoDataList, rejectedDataList, nil
|
||||
}
|
||||
|
||||
func normalize(record H) (*MeteoData, error) {
|
||||
func normalize(record app.H) (*MeteoData, error) {
|
||||
meteoData := &MeteoData{}
|
||||
|
||||
var err error
|
||||
|
||||
meteoData.Timestamp, err = parseDate(record, "Fecha", ErrMissingOrInvalidDateField)
|
||||
meteoData.Timestamp, err = parseDate(record, "Fecha", ErrMissingOrInvalidDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
meteoData.Location, err = parseString(record, "Ciudad", ErrMissingOrInvalidCityField)
|
||||
meteoData.Location, err = parseString(record, "Ciudad", ErrMissingOrInvalidLocation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -137,7 +138,7 @@ func normalize(record H) (*MeteoData, error) {
|
||||
return meteoData, nil
|
||||
}
|
||||
|
||||
func parseDate(record H, key string, errMissing error) (time.Time, error) {
|
||||
func parseDate(record app.H, key string, errMissing error) (time.Time, error) {
|
||||
if str, ok := record[key].(string); ok && str != "" {
|
||||
t, err := time.Parse("2006/01/02", str)
|
||||
if err != nil {
|
||||
@ -148,14 +149,14 @@ func parseDate(record H, key string, errMissing error) (time.Time, error) {
|
||||
return time.Time{}, errMissing
|
||||
}
|
||||
|
||||
func parseString(record H, key string, errMissing error) (string, error) {
|
||||
func parseString(record app.H, key string, errMissing error) (string, error) {
|
||||
if str, ok := record[key].(string); ok && str != "" {
|
||||
return str, nil
|
||||
}
|
||||
return "", errMissing
|
||||
}
|
||||
|
||||
func parseFloatField(record H, key string, errMissing error) (float32, error) {
|
||||
func parseFloatField(record app.H, key string, errMissing error) (float32, error) {
|
||||
if str, ok := record[key].(string); ok && str != "" {
|
||||
str = strings.Replace(str, ",", ".", 1)
|
||||
f, err := strconv.ParseFloat(str, 32)
|
||||
@ -167,7 +168,7 @@ func parseFloatField(record H, key string, errMissing error) (float32, error) {
|
||||
return 0, errMissing
|
||||
}
|
||||
|
||||
func parseIntField(record H, key string, errMissing error) (int, error) {
|
||||
func parseIntField(record app.H, key string, errMissing error) (int, error) {
|
||||
if str, ok := record[key].(string); ok && str != "" {
|
||||
str = strings.TrimSpace(str)
|
||||
i, err := strconv.Atoi(str)
|
||||
|
||||
@ -4,15 +4,18 @@ import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"servicea/internal/app"
|
||||
"servicea/internal/domains"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
domains.BaseHandler
|
||||
*Service
|
||||
}
|
||||
|
||||
@ -68,13 +71,48 @@ func (h *Handler) IngestCSV(w http.ResponseWriter, r *http.Request) {
|
||||
"elapsed_ms", fileStats.ElapsedMS,
|
||||
"file_checksum", fileStats.FileChecksum,
|
||||
)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(H{
|
||||
"stats": fileStats,
|
||||
})
|
||||
h.ToJSON(w, http.StatusOK, app.H{"stats": fileStats})
|
||||
}
|
||||
|
||||
func (h *Handler) IngestExcel(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "hello from excel")
|
||||
}
|
||||
|
||||
func (h *Handler) GetMeteoData(w http.ResponseWriter, r *http.Request) {
|
||||
queryParams := r.URL.Query()
|
||||
pageInt := int(1)
|
||||
limitInt := int(10)
|
||||
|
||||
page := queryParams.Get("page")
|
||||
limit := queryParams.Get("limit")
|
||||
|
||||
if page != "" {
|
||||
p, err := strconv.Atoi(page)
|
||||
if err == nil {
|
||||
pageInt = p
|
||||
}
|
||||
}
|
||||
|
||||
if limit != "" {
|
||||
l, err := strconv.Atoi(limit)
|
||||
if err == nil {
|
||||
limitInt = l
|
||||
} else {
|
||||
limitInt = 10
|
||||
}
|
||||
}
|
||||
|
||||
params := GetMeteoData{
|
||||
Location: queryParams.Get("city"),
|
||||
From: queryParams.Get("from"),
|
||||
To: queryParams.Get("to"),
|
||||
Page: pageInt,
|
||||
Limit: limitInt,
|
||||
}
|
||||
|
||||
if err := params.Validate(); err != nil {
|
||||
h.ToJSON(w, http.StatusBadRequest, app.H{"error": err.Error()})
|
||||
}
|
||||
|
||||
slog.Info("params", "params", params)
|
||||
}
|
||||
|
||||
@ -5,4 +5,6 @@ import "net/http"
|
||||
func RegisterRoutes(mux *http.ServeMux, handler *Handler) {
|
||||
mux.HandleFunc("POST /ingest/csv", handler.IngestCSV)
|
||||
mux.HandleFunc("POST /ingest/excel", handler.IngestExcel)
|
||||
|
||||
mux.HandleFunc("GET /data", handler.GetMeteoData)
|
||||
}
|
||||
|
||||
@ -48,3 +48,7 @@ func (s *Service) UpdateElapsedMS(ctx context.Context, batchID, elapsedMS int) e
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) GetMeteoData(params GetMeteoData) ([]MeteoData, error) {
|
||||
return []MeteoData{}, nil
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user