Skip to content
Merged
1 change: 1 addition & 0 deletions cmd/agent/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package main
27 changes: 26 additions & 1 deletion cmd/server/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
// Server for metrics collection and alerting service
package main

func main() {}
import (
"log"

"github.com/srg-bnd/observator/internal/server"
"github.com/srg-bnd/observator/internal/storage"
)

type App struct {
storage *storage.MemStorage
server *server.Server
}

func NewApp() *App {
storage := storage.NewMemStorage()
return &App{
storage: storage,
server: server.NewServer(storage),
}
}

func main() {
if err := NewApp().server.Start(); err != nil {
log.Fatal(err)
}
}
1 change: 1 addition & 0 deletions cmd/server/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package main
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
module github.com/srg-bnd/observator

go 1.22

require github.com/stretchr/testify v1.10.0

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Empty file added internal/agent/.keep
Empty file.
91 changes: 91 additions & 0 deletions internal/server/handlers/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Handlers for server
package handlers

import (
"errors"
"net/http"
"slices"
"strconv"

"github.com/srg-bnd/observator/internal/server/models"
"github.com/srg-bnd/observator/internal/server/services"
"github.com/srg-bnd/observator/internal/storage"
)

type Handler struct {
service *services.Service
}

func NewHandler(storage storage.Repositories) *Handler {
return &Handler{
service: services.NewService(storage),
}
}

/* UpdateMetricHandler */

func (h *Handler) UpdateMetricHandler(w http.ResponseWriter, r *http.Request) {
metric, err := h.parseAndValidateMetric(r)
if err != nil {
h.handleError(w, err)
return
}

h.processMetric(w, metric)
}

func (h *Handler) parseAndValidateMetric(r *http.Request) (*models.Metric, error) {
metric := models.Metric{}

// Check type
if !slices.Contains([]string{"counter", "gauge"}, r.PathValue("metricType")) {
return nil, errors.New("typeError")
}
metric.Type = r.PathValue("metricType")

// Check name
if r.PathValue("metricName") == "" {
return nil, errors.New("nameError")
}
metric.Name = r.PathValue("metricName")

// Check value
switch metric.Type {
case "counter":
value, err := strconv.ParseInt(r.PathValue("metricValue"), 10, 64)
if err != nil {
return nil, errors.New("valueError")
}

metric.SetCounter(value)
case "gauge":
value, err := strconv.ParseFloat(r.PathValue("metricValue"), 64)
if err != nil {
return nil, errors.New("valueError")
}

metric.SetGauge(value)
}

return &metric, nil
}

func (h *Handler) processMetric(w http.ResponseWriter, metric *models.Metric) {
data := h.service.UpdateMetricService(metric)

if data.Ok {
w.Header().Set("content-type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(http.StatusInternalServerError)
}
}

func (h *Handler) handleError(w http.ResponseWriter, err error) {
switch err.Error() {
case "typeError", "valueError":
w.WriteHeader(http.StatusBadRequest)
case "nameError":
w.WriteHeader(http.StatusNotFound)
}
}
30 changes: 30 additions & 0 deletions internal/server/handlers/handlers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package handlers

import (
"testing"

"github.com/srg-bnd/observator/internal/storage"
"github.com/stretchr/testify/assert"
)

func TestNewHandler(t *testing.T) {
storage := storage.NewMemStorage()
handler := NewHandler(storage)
assert.IsType(t, handler, &Handler{})
}

func TestUpdateMetricHandler(t *testing.T) {
t.Logf("TODO")
}

func TestParseAndValidateMetric(t *testing.T) {
t.Logf("TODO")
}

func TestProcessMetric(t *testing.T) {
t.Logf("TODO")
}

func TestHandleError(t *testing.T) {
t.Logf("TODO")
}
24 changes: 24 additions & 0 deletions internal/server/middleware/middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Middleware for server
package middleware

import "net/http"

type Middleware func(http.Handler) http.Handler

func Chain(h http.Handler, middlewares ...Middleware) http.Handler {
for _, middleware := range middlewares {
h = middleware(h)
}
return h
}

func CheckMethodPost(next http.Handler) http.Handler {
return http.HandlerFunc(func(resWriter http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPost {
http.Error(resWriter, "Method not allowed", http.StatusMethodNotAllowed)
return
}

next.ServeHTTP(resWriter, req)
})
}
11 changes: 11 additions & 0 deletions internal/server/middleware/middleware_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package middleware

import "testing"

func TestChain(t *testing.T) {
t.Logf("TODO")
}

func TestCheckMethodPost(t *testing.T) {
t.Logf("TODO")
}
30 changes: 30 additions & 0 deletions internal/server/models/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package models

/* Metric */

type Metric struct {
Type string
Name string
counterValue int64
gaugeValue float64
}

func NewMetric() *Metric {
return &Metric{}
}

func (m *Metric) SetCounter(value int64) {
m.counterValue = value
}

func (m *Metric) GetCounter() int64 {
return m.counterValue
}

func (m *Metric) SetGauge(value float64) {
m.gaugeValue = value
}

func (m *Metric) GetGauge() float64 {
return m.gaugeValue
}
40 changes: 40 additions & 0 deletions internal/server/models/models_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package models

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestNewMetric(t *testing.T) {
metric := NewMetric()
assert.IsType(t, metric, &Metric{})
}

func TestSetCounter(t *testing.T) {
metric := NewMetric()
metric.SetCounter(1)

assert.Equal(t, metric.counterValue, int64(1))
}

func TestGetCounter(t *testing.T) {
metric := NewMetric()
metric.SetCounter(1)

assert.Equal(t, metric.GetCounter(), int64(1))
}

func TestSetGauge(t *testing.T) {
metric := NewMetric()
metric.SetGauge(1)

assert.Equal(t, metric.gaugeValue, float64(1))
}

func TestGetGauge(t *testing.T) {
metric := NewMetric()
metric.SetGauge(1)

assert.Equal(t, metric.GetGauge(), float64(1))
}
48 changes: 48 additions & 0 deletions internal/server/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package server

import (
"net/http"

"github.com/srg-bnd/observator/internal/server/handlers"
"github.com/srg-bnd/observator/internal/server/middleware"
"github.com/srg-bnd/observator/internal/storage"
)

const (
defaultHost = `:8080`
)

type Server struct {
handler *handlers.Handler
}

func NewServer(storage storage.Repositories) *Server {
return &Server{
handler: handlers.NewHandler(storage),
}
}

// Init server dependencies before startup
func (server *Server) Start() error {
host, err := getHost()
if err != nil {
return err
}

mux := http.NewServeMux()
mux.Handle(
`/update/{metricType}/{metricName}/{metricValue}`,
middleware.Chain(
http.HandlerFunc(server.handler.UpdateMetricHandler),
middleware.CheckMethodPost,
))

http.ListenAndServe(host, mux)

return nil
}

// Selects the server host
func getHost() (string, error) {
return defaultHost, nil
}
26 changes: 26 additions & 0 deletions internal/server/server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package server

import (
"testing"

"github.com/srg-bnd/observator/internal/storage"
"github.com/stretchr/testify/assert"
)

func TestStart(t *testing.T) {
t.Logf("TODO")
}

func TestNewServer(t *testing.T) {
server := NewServer(storage.NewMemStorage())
assert.IsType(t, server, &Server{})
}

func TestGetHost(t *testing.T) {
got, _ := getHost()
want := ":8080"

if got != want {
t.Errorf(`Incorrect 'getHost()' method behavior, got "%v", want "%v"`, got, want)
}
}
Loading