Skip to content

Commit

Permalink
add encode json and validator helper func
Browse files Browse the repository at this point in the history
  • Loading branch information
The-Gleb committed Mar 12, 2024
1 parent b3ead84 commit 1c5590f
Show file tree
Hide file tree
Showing 15 changed files with 170 additions and 94 deletions.
28 changes: 16 additions & 12 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import (
"fmt"
"log"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"time"

_ "net/http/pprof"

Expand Down Expand Up @@ -38,13 +38,15 @@ func main() {
BuildVersion, BuildDate, BuildCommit,
)

if err := Run(); err != nil {
if err := Run(context.Background()); err != nil {
log.Fatal(err)
}

}

func Run() error {
func Run(ctx context.Context) error {
ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT)
defer cancel()

go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
Expand All @@ -66,7 +68,7 @@ func Run() error {
}

if config.DatabaseDSN != "" {
db, err := database.NewMetricDB(context.Background(), config.DatabaseDSN)
db, err := database.NewMetricDB(ctx, config.DatabaseDSN)
if err != nil {
return err
}
Expand Down Expand Up @@ -111,24 +113,26 @@ func Run() error {

var wg sync.WaitGroup

ctx, cancelCtx := context.WithCancel(context.Background())

wg.Add(1)
go func() {
defer wg.Done()
backupService.Run(ctx)
err := backupService.Run(ctx)
if err != nil {
cancel()
}
}()

wg.Add(1)
go func() {
defer wg.Done()
ServerShutdownSignal := make(chan os.Signal, 1)
signal.Notify(ServerShutdownSignal, syscall.SIGINT)
// ServerShutdownSignal := make(chan os.Signal, 1)
// signal.Notify(ServerShutdownSignal, syscall.SIGINT)

<-ServerShutdownSignal
<-ctx.Done()

cancelCtx()
err := s.Shutdown(context.Background())
ctxShutdown, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err := s.Shutdown(ctxShutdown)
if err != nil {
panic(err)
}
Expand Down
14 changes: 7 additions & 7 deletions internal/controller/http/v1/handler/get_metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package v1
import (
"context"
"errors"
"fmt"
"net/http"
"strconv"

Expand All @@ -16,7 +17,7 @@ const (
)

type GetMetricUsecase interface {
GetMetric(ctx context.Context, metrics entity.Metric) (entity.Metric, error)
GetMetric(ctx context.Context, metric entity.GetMetricDTO) (entity.Metric, error)
}

// getMetricHandler receives metric type, name in url params.
Expand Down Expand Up @@ -49,25 +50,24 @@ func (h *getMetricHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {

mType := chi.URLParam(r, "type")
mName := chi.URLParam(r, "name")
metric := entity.Metric{
dto := entity.GetMetricDTO{
MType: mType,
ID: mName,
}

if mType == "" || mName == "" {
http.Error(rw, "invalid request, metric type or metric name is empty", http.StatusBadRequest)
problems := dto.Valid()
if len(problems) > 0 {
http.Error(rw, fmt.Sprintf("invalid %T: %d problems", dto, len(problems)), http.StatusBadRequest)
return
}

metric, err := h.usecase.GetMetric(r.Context(), metric)
metric, err := h.usecase.GetMetric(r.Context(), dto)
if err != nil {

if errors.Is(err, repository.ErrNotFound) {
rw.WriteHeader(http.StatusNotFound)
http.Error(rw, err.Error(), http.StatusNotFound)
return
} else {
rw.WriteHeader(http.StatusBadRequest)
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}
Expand Down
18 changes: 5 additions & 13 deletions internal/controller/http/v1/handler/get_metric_json.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package v1

import (
"encoding/json"
"errors"
"fmt"
"net/http"

v1 "github.com/The-Gleb/go_metrics_and_alerting/internal/controller/http/v1"
"github.com/The-Gleb/go_metrics_and_alerting/internal/domain/entity"
"github.com/The-Gleb/go_metrics_and_alerting/internal/logger"
"github.com/The-Gleb/go_metrics_and_alerting/internal/repository"
Expand Down Expand Up @@ -44,19 +44,13 @@ func (h *getMetricJSONHandler) Middlewares(md ...func(http.Handler) http.Handler

func (h *getMetricJSONHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {

var metric entity.Metric
err := json.NewDecoder(r.Body).Decode(&metric)
dto, _, err := v1.DecodeValid[entity.GetMetricDTO](r)
if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}

if metric.ID == "" || metric.MType == "" {
http.Error(rw, "invalid request body,some fields are empty, but they shouldn`t", http.StatusBadRequest)
return
}

metric, err = h.usecase.GetMetric(r.Context(), metric)
metric, err := h.usecase.GetMetric(r.Context(), dto)
if err != nil {
err = fmt.Errorf("handlers.GetMetricJSON: %w", err)
logger.Log.Error(err)
Expand All @@ -72,12 +66,10 @@ func (h *getMetricJSONHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request
}
}

b, err := json.Marshal(metric)
err = v1.Encode(rw, r, 200, metric)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}

rw.Write(b)

}
19 changes: 5 additions & 14 deletions internal/controller/http/v1/handler/update_metric_json.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package v1

import (
"encoding/json"
"fmt"
"net/http"

v1 "github.com/The-Gleb/go_metrics_and_alerting/internal/controller/http/v1"
"github.com/The-Gleb/go_metrics_and_alerting/internal/domain/entity"
"github.com/go-chi/chi/v5"
)
Expand Down Expand Up @@ -41,32 +41,23 @@ func (h *updateMetricJSONHandler) Middlewares(md ...func(http.Handler) http.Hand

func (h *updateMetricJSONHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {

var metric entity.Metric
err := json.NewDecoder(r.Body).Decode(&metric)
dto, _, err := v1.DecodeValid[entity.Metric](r)
if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}

if metric.MType == "" || metric.ID == "" ||
(metric.Delta == nil && metric.Value == nil) {
http.Error(rw, "invalid request body,some fields are empty, but they shouldn`t", http.StatusBadRequest)
return
}

metric, err = h.usecase.UpdateMetric(r.Context(), metric)
metric, err := h.usecase.UpdateMetric(r.Context(), dto)
if err != nil {
err = fmt.Errorf("updateMetricJSONHandler: %w", err)
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}

b, err := json.Marshal(metric)
err = v1.Encode(rw, r, 200, metric)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}

rw.Write(b)

}
27 changes: 27 additions & 0 deletions internal/controller/http/v1/json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package http

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

func DecodeValid[T Validator](r *http.Request) (T, map[string]string, error) {
var v T
if err := json.NewDecoder(r.Body).Decode(&v); err != nil {
return v, nil, fmt.Errorf("decode json: %w", err)
}
if problems := v.Valid(); len(problems) > 0 {
return v, problems, fmt.Errorf("invalid %T: %d problems", v, len(problems))
}
return v, nil, nil
}

func Encode[T any](w http.ResponseWriter, r *http.Request, status int, v T) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
if err := json.NewEncoder(w).Encode(v); err != nil {
return fmt.Errorf("encode json: %w", err)
}
return nil
}
5 changes: 5 additions & 0 deletions internal/controller/http/v1/validatior.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package http

type Validator interface {
Valid() (problems map[string]string)
}
37 changes: 37 additions & 0 deletions internal/domain/entity/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,43 @@ type Metric struct {
Delta *int64 `json:"delta,omitempty"` // значение метрики в случае передачи counter
Value *float64 `json:"value,omitempty"` // значение метрики в случае передачи gauge
}

func (m Metric) Valid() map[string]string {
problems := make(map[string]string)
switch m.MType {
case "gauge":
if m.Value == nil {
problems["value"] = "should not be empty"
}
case "counter":
if m.Delta == nil {
problems["delta"] = "should not be empty"
}
default:
problems["type"] = "should not be empty"
}
if m.ID == "" {
problems["id"] = "should not be empty"
}
return problems
}

type GetMetricDTO struct {
MType string
ID string
}

func (dto GetMetricDTO) Valid() map[string]string {
problems := make(map[string]string)
if dto.MType == "" {
problems["type"] = "should not be empty"
}
if dto.ID == "" {
problems["id"] = "should not be empty"
}
return problems
}

type MetricSlices struct {
Gauge []Metric
Counter []Metric
Expand Down
10 changes: 5 additions & 5 deletions internal/domain/service/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,18 @@ func NewBackupService(ms MetricStorage, bs FileStorage, interval int, restore bo
}
}

func (service *backupService) Run(ctx context.Context) {
func (service *backupService) Run(ctx context.Context) error {

if service.restore {
err := service.LoadDataFromFile(ctx)
if err != nil {
logger.Log.Errorf("error in backupservice, stopping")
return
return err
}
}

if service.backupInterval <= 0 || service.backupStorage == nil {
return
return nil
}

saveTicker := time.NewTicker(time.Duration(service.backupInterval) * time.Second)
Expand All @@ -53,11 +53,11 @@ func (service *backupService) Run(ctx context.Context) {
case <-saveTicker.C:
err := service.StoreDataToFile(ctx)
if err != nil {
return
return err
}
case <-ctx.Done():
logger.Log.Debug("stop saving to file")
return
return nil
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions internal/domain/service/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ type MetricStorage interface {

UpdateMetricSet(ctx context.Context, metrics []entity.Metric) (int64, error)

GetGauge(ctx context.Context, metric entity.Metric) (entity.Metric, error)
GetCounter(ctx context.Context, metric entity.Metric) (entity.Metric, error)
GetGauge(ctx context.Context, metric entity.GetMetricDTO) (entity.Metric, error)
GetCounter(ctx context.Context, metric entity.GetMetricDTO) (entity.Metric, error)
GetAllMetrics(ctx context.Context) (entity.MetricSlices, error)

PingDB() error
Expand Down Expand Up @@ -78,19 +78,19 @@ func (service *metricService) UpdateMetricSet(ctx context.Context, metrics []ent

}

func (service *metricService) GetMetric(ctx context.Context, metric entity.Metric) (entity.Metric, error) {

func (service *metricService) GetMetric(ctx context.Context, dto entity.GetMetricDTO) (entity.Metric, error) {
var metric entity.Metric
var err error
switch metric.MType {
case "gauge":
err = retry.DefaultRetry(ctx, func(ctx context.Context) error {
metric, err = service.storage.GetGauge(ctx, metric)
metric, err = service.storage.GetGauge(ctx, dto)
return err
})

case "counter":
err = retry.DefaultRetry(ctx, func(ctx context.Context) error {
metric, err = service.storage.GetCounter(ctx, metric)
metric, err = service.storage.GetCounter(ctx, dto)
return err
})
default:
Expand Down
Loading

0 comments on commit 1c5590f

Please sign in to comment.