mirror of
https://github.com/elyby/chrly.git
synced 2025-05-31 14:11:51 +05:30
Remove dispatcher and eventsubscribers modules, remove statsd integration, remove event bus usage. Overall cleanup before otel integration
This commit is contained in:
4
go.mod
4
go.mod
@@ -2,11 +2,8 @@ module ely.by/chrly
|
|||||||
|
|
||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
replace github.com/asaskevich/EventBus v0.0.0-20200330115301-33b3bc6a7ddc => github.com/erickskrauch/EventBus v0.0.0-20200330115301-33b3bc6a7ddc
|
|
||||||
|
|
||||||
// Main dependencies
|
// Main dependencies
|
||||||
require (
|
require (
|
||||||
github.com/asaskevich/EventBus v0.0.0-20200330115301-33b3bc6a7ddc
|
|
||||||
github.com/brunomvsouza/singleflight v0.4.0
|
github.com/brunomvsouza/singleflight v0.4.0
|
||||||
github.com/defval/di v1.12.0
|
github.com/defval/di v1.12.0
|
||||||
github.com/etherlabsio/healthcheck/v2 v2.0.0
|
github.com/etherlabsio/healthcheck/v2 v2.0.0
|
||||||
@@ -42,7 +39,6 @@ require (
|
|||||||
github.com/leodido/go-urn v1.2.4 // indirect
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/mono83/udpwriter v1.0.2 // indirect
|
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
|
6
go.sum
6
go.sum
@@ -9,8 +9,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
|||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/defval/di v1.12.0 h1:xXm7BMX2+Nr0Yyu55DeJl/rmfCA7CQX89f4AGE0zA6U=
|
github.com/defval/di v1.12.0 h1:xXm7BMX2+Nr0Yyu55DeJl/rmfCA7CQX89f4AGE0zA6U=
|
||||||
github.com/defval/di v1.12.0/go.mod h1:PhVbOxQOvU7oawTOJXXTvqOJp1Dvsjs5PuzMw9gGl0I=
|
github.com/defval/di v1.12.0/go.mod h1:PhVbOxQOvU7oawTOJXXTvqOJp1Dvsjs5PuzMw9gGl0I=
|
||||||
github.com/erickskrauch/EventBus v0.0.0-20200330115301-33b3bc6a7ddc h1:kz3f5uMA1LxfRvJjZmMYG7Zu2rddTfJy6QZofz2YoGQ=
|
|
||||||
github.com/erickskrauch/EventBus v0.0.0-20200330115301-33b3bc6a7ddc/go.mod h1:RHSo3YFV/SbOGyFR36RKWaXPy3g9nKAmn6ebNLpbco4=
|
|
||||||
github.com/etherlabsio/healthcheck/v2 v2.0.0 h1:oKq8cbpwM/yNGPXf2Sff6MIjVUjx/pGYFydWzeK2MpA=
|
github.com/etherlabsio/healthcheck/v2 v2.0.0 h1:oKq8cbpwM/yNGPXf2Sff6MIjVUjx/pGYFydWzeK2MpA=
|
||||||
github.com/etherlabsio/healthcheck/v2 v2.0.0/go.mod h1:huNVOjKzu6FI1eaO1CGD3ZjhrmPWf5Obu/pzpI6/wog=
|
github.com/etherlabsio/healthcheck/v2 v2.0.0/go.mod h1:huNVOjKzu6FI1eaO1CGD3ZjhrmPWf5Obu/pzpI6/wog=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
@@ -59,8 +57,6 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
|
|||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mono83/slf v0.0.0-20170919161409-79153e9636db h1:tlz4fTklh5mttoq5M+0yEc5Lap8W/02A2HCXCJn5iz0=
|
github.com/mono83/slf v0.0.0-20170919161409-79153e9636db h1:tlz4fTklh5mttoq5M+0yEc5Lap8W/02A2HCXCJn5iz0=
|
||||||
github.com/mono83/slf v0.0.0-20170919161409-79153e9636db/go.mod h1:MfF+zNMZz+5IGY9h8jpFaGLyGoJ2ZPri2FmUVftBoUU=
|
github.com/mono83/slf v0.0.0-20170919161409-79153e9636db/go.mod h1:MfF+zNMZz+5IGY9h8jpFaGLyGoJ2ZPri2FmUVftBoUU=
|
||||||
github.com/mono83/udpwriter v1.0.2 h1:JiQ/N646oZoJA1G0FOMvn2teMt6SdL1KwNH2mszOlQs=
|
|
||||||
github.com/mono83/udpwriter v1.0.2/go.mod h1:mTDiyLtA0tXoxckkV9T4NUkJTgSQIuO8pAUKx/dSRkQ=
|
|
||||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
||||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||||
@@ -92,7 +88,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
|
|||||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
@@ -126,6 +121,5 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
@@ -215,8 +215,8 @@ func storeMojangUuid(ctx context.Context, conn radix.Conn, username string, uuid
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Redis) Ping() error {
|
func (r *Redis) Ping(ctx context.Context) error {
|
||||||
return r.client.Do(r.context, radix.Cmd(nil, "PING"))
|
return r.client.Do(ctx, radix.Cmd(nil, "PING"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeUuid(uuid string) string {
|
func normalizeUuid(uuid string) string {
|
||||||
|
@@ -294,6 +294,6 @@ func (s *redisTestSuite) TestStoreUuid() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *redisTestSuite) TestPing() {
|
func (s *redisTestSuite) TestPing() {
|
||||||
err := s.Redis.Ping()
|
err := s.Redis.Ping(context.Background())
|
||||||
s.Require().Nil(err)
|
s.Require().Nil(err)
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,7 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var config = di.Options(
|
var configDiOptions = di.Options(
|
||||||
di.Provide(newConfig),
|
di.Provide(newConfig),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -5,21 +5,18 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/defval/di"
|
"github.com/defval/di"
|
||||||
|
"github.com/etherlabsio/healthcheck/v2"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
db2 "ely.by/chrly/internal/db"
|
"ely.by/chrly/internal/db"
|
||||||
"ely.by/chrly/internal/db/redis"
|
"ely.by/chrly/internal/db/redis"
|
||||||
es "ely.by/chrly/internal/eventsubscribers"
|
|
||||||
"ely.by/chrly/internal/mojang"
|
"ely.by/chrly/internal/mojang"
|
||||||
"ely.by/chrly/internal/profiles"
|
"ely.by/chrly/internal/profiles"
|
||||||
)
|
)
|
||||||
|
|
||||||
// v4 had the idea that it would be possible to separate backends for storing skins and capes.
|
|
||||||
// But in v5 the storage will be unified, so this is just temporary constructors before large reworking.
|
|
||||||
//
|
|
||||||
// Since there are no options for selecting target backends,
|
// Since there are no options for selecting target backends,
|
||||||
// all constants in this case point to static specific implementations.
|
// all constants in this case point to static specific implementations.
|
||||||
var db = di.Options(
|
var dbDeOptions = di.Options(
|
||||||
di.Provide(newRedis,
|
di.Provide(newRedis,
|
||||||
di.As(new(profiles.ProfilesRepository)),
|
di.As(new(profiles.ProfilesRepository)),
|
||||||
di.As(new(profiles.ProfilesFinder)),
|
di.As(new(profiles.ProfilesFinder)),
|
||||||
@@ -34,7 +31,7 @@ func newRedis(container *di.Container, config *viper.Viper) (*redis.Redis, error
|
|||||||
|
|
||||||
conn, err := redis.New(
|
conn, err := redis.New(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
db2.NewZlibEncoder(&db2.JsonSerializer{}),
|
db.NewZlibEncoder(&db.JsonSerializer{}),
|
||||||
fmt.Sprintf("%s:%d", config.GetString("storage.redis.host"), config.GetInt("storage.redis.port")),
|
fmt.Sprintf("%s:%d", config.GetString("storage.redis.host"), config.GetInt("storage.redis.port")),
|
||||||
config.GetInt("storage.redis.poolSize"),
|
config.GetInt("storage.redis.poolSize"),
|
||||||
)
|
)
|
||||||
@@ -45,7 +42,7 @@ func newRedis(container *di.Container, config *viper.Viper) (*redis.Redis, error
|
|||||||
if err := container.Provide(func() *namedHealthChecker {
|
if err := container.Provide(func() *namedHealthChecker {
|
||||||
return &namedHealthChecker{
|
return &namedHealthChecker{
|
||||||
Name: "redis",
|
Name: "redis",
|
||||||
Checker: es.DatabaseChecker(conn),
|
Checker: healthcheck.CheckerFunc(conn.Ping),
|
||||||
}
|
}
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@@ -4,14 +4,13 @@ import "github.com/defval/di"
|
|||||||
|
|
||||||
func New() (*di.Container, error) {
|
func New() (*di.Container, error) {
|
||||||
return di.New(
|
return di.New(
|
||||||
config,
|
configDiOptions,
|
||||||
dispatcher,
|
loggerDiOptions,
|
||||||
logger,
|
dbDeOptions,
|
||||||
db,
|
mojangDiOptions,
|
||||||
mojangTextures,
|
handlersDiOptions,
|
||||||
handlers,
|
profilesDiOptions,
|
||||||
profilesDi,
|
serverDiOptions,
|
||||||
server,
|
|
||||||
securityDiOptions,
|
securityDiOptions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -1,34 +0,0 @@
|
|||||||
package di
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/defval/di"
|
|
||||||
"github.com/mono83/slf"
|
|
||||||
|
|
||||||
d "ely.by/chrly/internal/dispatcher"
|
|
||||||
"ely.by/chrly/internal/eventsubscribers"
|
|
||||||
"ely.by/chrly/internal/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
var dispatcher = di.Options(
|
|
||||||
di.Provide(newDispatcher,
|
|
||||||
di.As(new(d.Emitter)),
|
|
||||||
di.As(new(d.Subscriber)),
|
|
||||||
di.As(new(http.Emitter)),
|
|
||||||
di.As(new(eventsubscribers.Subscriber)),
|
|
||||||
),
|
|
||||||
di.Invoke(enableEventsHandlers),
|
|
||||||
)
|
|
||||||
|
|
||||||
func newDispatcher() d.Dispatcher {
|
|
||||||
return d.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
func enableEventsHandlers(
|
|
||||||
dispatcher d.Subscriber,
|
|
||||||
logger slf.Logger,
|
|
||||||
statsReporter slf.StatsReporter,
|
|
||||||
) {
|
|
||||||
// TODO: use idea from https://github.com/defval/di/issues/10#issuecomment-615869852
|
|
||||||
(&eventsubscribers.Logger{Logger: logger}).ConfigureWithDispatcher(dispatcher)
|
|
||||||
(&eventsubscribers.StatsReporter{StatsReporter: statsReporter}).ConfigureWithDispatcher(dispatcher)
|
|
||||||
}
|
|
@@ -1,8 +1,8 @@
|
|||||||
package di
|
package di
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/defval/di"
|
"github.com/defval/di"
|
||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
. "ely.by/chrly/internal/http"
|
. "ely.by/chrly/internal/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
var handlers = di.Options(
|
var handlersDiOptions = di.Options(
|
||||||
di.Provide(newHandlerFactory, di.As(new(http.Handler))),
|
di.Provide(newHandlerFactory, di.As(new(http.Handler))),
|
||||||
di.Provide(newSkinsystemHandler, di.WithName("skinsystem")),
|
di.Provide(newSkinsystemHandler, di.WithName("skinsystem")),
|
||||||
di.Provide(newApiHandler, di.WithName("api")),
|
di.Provide(newApiHandler, di.WithName("api")),
|
||||||
@@ -22,7 +22,6 @@ var handlers = di.Options(
|
|||||||
func newHandlerFactory(
|
func newHandlerFactory(
|
||||||
container *di.Container,
|
container *di.Container,
|
||||||
config *viper.Viper,
|
config *viper.Viper,
|
||||||
emitter Emitter,
|
|
||||||
) (*mux.Router, error) {
|
) (*mux.Router, error) {
|
||||||
enabledModules := config.GetStringSlice("modules")
|
enabledModules := config.GetStringSlice("modules")
|
||||||
|
|
||||||
@@ -31,7 +30,7 @@ func newHandlerFactory(
|
|||||||
// if you set an empty prefix. Since the main application should be mounted at the root prefix,
|
// if you set an empty prefix. Since the main application should be mounted at the root prefix,
|
||||||
// we use it as the base router
|
// we use it as the base router
|
||||||
var router *mux.Router
|
var router *mux.Router
|
||||||
if hasValue(enabledModules, "skinsystem") {
|
if slices.Contains(enabledModules, "skinsystem") {
|
||||||
if err := container.Resolve(&router, di.Name("skinsystem")); err != nil {
|
if err := container.Resolve(&router, di.Name("skinsystem")); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -40,13 +39,13 @@ func newHandlerFactory(
|
|||||||
}
|
}
|
||||||
|
|
||||||
router.StrictSlash(true)
|
router.StrictSlash(true)
|
||||||
requestEventsMiddleware := CreateRequestEventsMiddleware(emitter, "skinsystem")
|
requestEventsMiddleware := CreateRequestEventsMiddleware()
|
||||||
router.Use(requestEventsMiddleware)
|
router.Use(requestEventsMiddleware)
|
||||||
// NotFoundHandler doesn't call for registered middlewares, so we must wrap it manually.
|
// NotFoundHandler doesn't call for registered middlewares, so we must wrap it manually.
|
||||||
// See https://github.com/gorilla/mux/issues/416#issuecomment-600079279
|
// See https://github.com/gorilla/mux/issues/416#issuecomment-600079279
|
||||||
router.NotFoundHandler = requestEventsMiddleware(http.HandlerFunc(NotFoundHandler))
|
router.NotFoundHandler = requestEventsMiddleware(http.HandlerFunc(NotFoundHandler))
|
||||||
|
|
||||||
if hasValue(enabledModules, "api") {
|
if slices.Contains(enabledModules, "api") {
|
||||||
var apiRouter *mux.Router
|
var apiRouter *mux.Router
|
||||||
if err := container.Resolve(&apiRouter, di.Name("api")); err != nil {
|
if err := container.Resolve(&apiRouter, di.Name("api")); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -62,11 +61,6 @@ func newHandlerFactory(
|
|||||||
mount(router, "/api", apiRouter)
|
mount(router, "/api", apiRouter)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := container.Invoke(enableReporters)
|
|
||||||
if err != nil && !errors.Is(err, di.ErrTypeNotExists) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve health checkers last, because all the services required by the application
|
// Resolve health checkers last, because all the services required by the application
|
||||||
// must first be initialized and each of them can publish its own checkers
|
// must first be initialized and each of them can publish its own checkers
|
||||||
var healthCheckers []*namedHealthChecker
|
var healthCheckers []*namedHealthChecker
|
||||||
@@ -108,16 +102,6 @@ func newApiHandler(profilesManager ProfilesManager) *mux.Router {
|
|||||||
}).Handler()
|
}).Handler()
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasValue(slice []string, needle string) bool {
|
|
||||||
for _, value := range slice {
|
|
||||||
if value == needle {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func mount(router *mux.Router, path string, handler http.Handler) {
|
func mount(router *mux.Router, path string, handler http.Handler) {
|
||||||
router.PathPrefix(path).Handler(
|
router.PathPrefix(path).Handler(
|
||||||
http.StripPrefix(
|
http.StripPrefix(
|
||||||
|
@@ -1,26 +1,21 @@
|
|||||||
package di
|
package di
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/defval/di"
|
"github.com/defval/di"
|
||||||
"github.com/getsentry/raven-go"
|
"github.com/getsentry/raven-go"
|
||||||
"github.com/mono83/slf"
|
"github.com/mono83/slf"
|
||||||
"github.com/mono83/slf/rays"
|
"github.com/mono83/slf/rays"
|
||||||
"github.com/mono83/slf/recievers/sentry"
|
"github.com/mono83/slf/recievers/sentry"
|
||||||
"github.com/mono83/slf/recievers/statsd"
|
|
||||||
"github.com/mono83/slf/recievers/writer"
|
"github.com/mono83/slf/recievers/writer"
|
||||||
"github.com/mono83/slf/wd"
|
"github.com/mono83/slf/wd"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"ely.by/chrly/internal/eventsubscribers"
|
|
||||||
"ely.by/chrly/internal/version"
|
"ely.by/chrly/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logger = di.Options(
|
var loggerDiOptions = di.Options(
|
||||||
di.Provide(newLogger),
|
di.Provide(newLogger),
|
||||||
di.Provide(newSentry),
|
di.Provide(newSentry),
|
||||||
di.Provide(newStatsReporter),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type loggerParams struct {
|
type loggerParams struct {
|
||||||
@@ -71,34 +66,3 @@ func newSentry(config *viper.Viper) (*raven.Client, error) {
|
|||||||
|
|
||||||
return ravenClient, nil
|
return ravenClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStatsReporter(config *viper.Viper) (slf.StatsReporter, error) {
|
|
||||||
dispatcher := &slf.Dispatcher{}
|
|
||||||
|
|
||||||
statsdAddr := config.GetString("statsd.addr")
|
|
||||||
if statsdAddr != "" {
|
|
||||||
hostname, err := os.Hostname()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
statsdReceiver, err := statsd.NewReceiver(statsd.Config{
|
|
||||||
Address: statsdAddr,
|
|
||||||
Prefix: "ely.skinsystem." + hostname + ".app.",
|
|
||||||
FlushEvery: 1,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatcher.AddReceiver(statsdReceiver)
|
|
||||||
}
|
|
||||||
|
|
||||||
return wd.Custom("", "", dispatcher), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func enableReporters(reporter slf.StatsReporter, factories []eventsubscribers.Reporter) {
|
|
||||||
for _, factory := range factories {
|
|
||||||
factory.Enable(reporter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -12,7 +12,7 @@ import (
|
|||||||
"ely.by/chrly/internal/profiles"
|
"ely.by/chrly/internal/profiles"
|
||||||
)
|
)
|
||||||
|
|
||||||
var mojangTextures = di.Options(
|
var mojangDiOptions = di.Options(
|
||||||
di.Provide(newMojangApi),
|
di.Provide(newMojangApi),
|
||||||
di.Provide(newMojangTexturesProviderFactory),
|
di.Provide(newMojangTexturesProviderFactory),
|
||||||
di.Provide(newMojangTexturesProvider),
|
di.Provide(newMojangTexturesProvider),
|
@@ -7,7 +7,7 @@ import (
|
|||||||
"ely.by/chrly/internal/profiles"
|
"ely.by/chrly/internal/profiles"
|
||||||
)
|
)
|
||||||
|
|
||||||
var profilesDi = di.Options(
|
var profilesDiOptions = di.Options(
|
||||||
di.Provide(newProfilesManager, di.As(new(ProfilesManager))),
|
di.Provide(newProfilesManager, di.As(new(ProfilesManager))),
|
||||||
di.Provide(newProfilesProvider, di.As(new(ProfilesProvider))),
|
di.Provide(newProfilesProvider, di.As(new(ProfilesProvider))),
|
||||||
)
|
)
|
||||||
|
@@ -15,7 +15,7 @@ import (
|
|||||||
"ely.by/chrly/internal/security"
|
"ely.by/chrly/internal/security"
|
||||||
)
|
)
|
||||||
|
|
||||||
var server = di.Options(
|
var serverDiOptions = di.Options(
|
||||||
di.Provide(newAuthenticator, di.As(new(Authenticator))),
|
di.Provide(newAuthenticator, di.As(new(Authenticator))),
|
||||||
di.Provide(newServer),
|
di.Provide(newServer),
|
||||||
)
|
)
|
||||||
|
@@ -1,34 +0,0 @@
|
|||||||
package dispatcher
|
|
||||||
|
|
||||||
import "github.com/asaskevich/EventBus"
|
|
||||||
|
|
||||||
type Subscriber interface {
|
|
||||||
Subscribe(topic string, fn interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Emitter interface {
|
|
||||||
Emit(topic string, args ...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Dispatcher interface {
|
|
||||||
Subscriber
|
|
||||||
Emitter
|
|
||||||
}
|
|
||||||
|
|
||||||
type localEventDispatcher struct {
|
|
||||||
bus EventBus.Bus
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *localEventDispatcher) Subscribe(topic string, fn interface{}) {
|
|
||||||
_ = d.bus.Subscribe(topic, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *localEventDispatcher) Emit(topic string, args ...interface{}) {
|
|
||||||
d.bus.Publish(topic, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func New() Dispatcher {
|
|
||||||
return &localEventDispatcher{
|
|
||||||
bus: EventBus.New(),
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,60 +0,0 @@
|
|||||||
package eventsubscribers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/etherlabsio/healthcheck/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Pingable interface {
|
|
||||||
Ping() error
|
|
||||||
}
|
|
||||||
|
|
||||||
func DatabaseChecker(connection Pingable) healthcheck.CheckerFunc {
|
|
||||||
return func(ctx context.Context) error {
|
|
||||||
done := make(chan error)
|
|
||||||
go func() {
|
|
||||||
done <- connection.Ping()
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return errors.New("check timeout")
|
|
||||||
case err := <-done:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type expiringErrHolder struct {
|
|
||||||
D time.Duration
|
|
||||||
err error
|
|
||||||
l sync.Mutex
|
|
||||||
t *time.Timer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *expiringErrHolder) Get() error {
|
|
||||||
h.l.Lock()
|
|
||||||
defer h.l.Unlock()
|
|
||||||
|
|
||||||
return h.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *expiringErrHolder) Set(err error) {
|
|
||||||
h.l.Lock()
|
|
||||||
defer h.l.Unlock()
|
|
||||||
if h.t != nil {
|
|
||||||
h.t.Stop()
|
|
||||||
h.t = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
h.err = err
|
|
||||||
if err != nil {
|
|
||||||
h.t = time.AfterFunc(h.D, func() {
|
|
||||||
h.Set(nil)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,50 +0,0 @@
|
|||||||
package eventsubscribers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
type pingableMock struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pingableMock) Ping() error {
|
|
||||||
args := p.Called()
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDatabaseChecker(t *testing.T) {
|
|
||||||
t.Run("no error", func(t *testing.T) {
|
|
||||||
p := &pingableMock{}
|
|
||||||
p.On("Ping").Return(nil)
|
|
||||||
checker := DatabaseChecker(p)
|
|
||||||
assert.Nil(t, checker(context.Background()))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with error", func(t *testing.T) {
|
|
||||||
err := errors.New("mock error")
|
|
||||||
p := &pingableMock{}
|
|
||||||
p.On("Ping").Return(err)
|
|
||||||
checker := DatabaseChecker(p)
|
|
||||||
assert.Equal(t, err, checker(context.Background()))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("context timeout", func(t *testing.T) {
|
|
||||||
p := &pingableMock{}
|
|
||||||
waitChan := make(chan time.Time, 1)
|
|
||||||
p.On("Ping").WaitUntil(waitChan).Return(nil)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 0)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
checker := DatabaseChecker(p)
|
|
||||||
assert.Errorf(t, checker(ctx), "check timeout")
|
|
||||||
close(waitChan)
|
|
||||||
})
|
|
||||||
}
|
|
@@ -1,41 +0,0 @@
|
|||||||
package eventsubscribers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/mono83/slf"
|
|
||||||
"github.com/mono83/slf/wd"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Logger struct {
|
|
||||||
slf.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) ConfigureWithDispatcher(d Subscriber) {
|
|
||||||
d.Subscribe("skinsystem:after_request", l.handleAfterSkinsystemRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) handleAfterSkinsystemRequest(req *http.Request, statusCode int) {
|
|
||||||
path := req.URL.Path
|
|
||||||
if req.URL.RawQuery != "" {
|
|
||||||
path += "?" + req.URL.RawQuery
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Info(
|
|
||||||
":ip - - \":method :path\" :statusCode - \":userAgent\" \":forwardedIp\"",
|
|
||||||
wd.StringParam("ip", trimPort(req.RemoteAddr)),
|
|
||||||
wd.StringParam("method", req.Method),
|
|
||||||
wd.StringParam("path", path),
|
|
||||||
wd.IntParam("statusCode", statusCode),
|
|
||||||
wd.StringParam("userAgent", req.UserAgent()),
|
|
||||||
wd.StringParam("forwardedIp", req.Header.Get("X-Forwarded-For")),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func trimPort(ip string) string {
|
|
||||||
// Don't care about possible -1 result because RemoteAddr will always contain ip and port
|
|
||||||
cutTo := strings.LastIndexByte(ip, ':')
|
|
||||||
|
|
||||||
return ip[0:cutTo]
|
|
||||||
}
|
|
@@ -1,159 +0,0 @@
|
|||||||
package eventsubscribers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/mono83/slf"
|
|
||||||
"github.com/mono83/slf/params"
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
|
|
||||||
"ely.by/chrly/internal/dispatcher"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LoggerMock struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
func prepareLoggerArgs(message string, params []slf.Param) []interface{} {
|
|
||||||
args := []interface{}{message}
|
|
||||||
for _, v := range params {
|
|
||||||
args = append(args, v.(interface{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LoggerMock) Trace(message string, params ...slf.Param) {
|
|
||||||
l.Called(prepareLoggerArgs(message, params)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LoggerMock) Debug(message string, params ...slf.Param) {
|
|
||||||
l.Called(prepareLoggerArgs(message, params)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LoggerMock) Info(message string, params ...slf.Param) {
|
|
||||||
l.Called(prepareLoggerArgs(message, params)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LoggerMock) Warning(message string, params ...slf.Param) {
|
|
||||||
l.Called(prepareLoggerArgs(message, params)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LoggerMock) Error(message string, params ...slf.Param) {
|
|
||||||
l.Called(prepareLoggerArgs(message, params)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LoggerMock) Alert(message string, params ...slf.Param) {
|
|
||||||
l.Called(prepareLoggerArgs(message, params)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LoggerMock) Emergency(message string, params ...slf.Param) {
|
|
||||||
l.Called(prepareLoggerArgs(message, params)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
type LoggerTestCase struct {
|
|
||||||
Events [][]interface{}
|
|
||||||
ExpectedCalls [][]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var loggerTestCases = map[string]*LoggerTestCase{
|
|
||||||
"should log each request to the skinsystem": {
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:after_request",
|
|
||||||
(func() *http.Request {
|
|
||||||
req := httptest.NewRequest("GET", "http://localhost/skins/username.png", nil)
|
|
||||||
req.Header.Add("User-Agent", "Test user agent")
|
|
||||||
|
|
||||||
return req
|
|
||||||
})(),
|
|
||||||
201,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"Info",
|
|
||||||
":ip - - \":method :path\" :statusCode - \":userAgent\" \":forwardedIp\"",
|
|
||||||
mock.MatchedBy(func(strParam params.String) bool {
|
|
||||||
return strParam.Key == "ip" && strParam.Value == "192.0.2.1"
|
|
||||||
}),
|
|
||||||
mock.MatchedBy(func(strParam params.String) bool {
|
|
||||||
return strParam.Key == "method" && strParam.Value == "GET"
|
|
||||||
}),
|
|
||||||
mock.MatchedBy(func(strParam params.String) bool {
|
|
||||||
return strParam.Key == "path" && strParam.Value == "/skins/username.png"
|
|
||||||
}),
|
|
||||||
mock.MatchedBy(func(strParam params.Int) bool {
|
|
||||||
return strParam.Key == "statusCode" && strParam.Value == 201
|
|
||||||
}),
|
|
||||||
mock.MatchedBy(func(strParam params.String) bool {
|
|
||||||
return strParam.Key == "userAgent" && strParam.Value == "Test user agent"
|
|
||||||
}),
|
|
||||||
mock.MatchedBy(func(strParam params.String) bool {
|
|
||||||
return strParam.Key == "forwardedIp" && strParam.Value == ""
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"should log each request to the skinsystem 2": {
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:after_request",
|
|
||||||
(func() *http.Request {
|
|
||||||
req := httptest.NewRequest("GET", "http://localhost/skins/username.png?authlib=1.5.2", nil)
|
|
||||||
req.Header.Add("User-Agent", "Test user agent")
|
|
||||||
req.Header.Add("X-Forwarded-For", "1.2.3.4")
|
|
||||||
|
|
||||||
return req
|
|
||||||
})(),
|
|
||||||
201,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"Info",
|
|
||||||
":ip - - \":method :path\" :statusCode - \":userAgent\" \":forwardedIp\"",
|
|
||||||
mock.Anything, // Already tested
|
|
||||||
mock.Anything, // Already tested
|
|
||||||
mock.MatchedBy(func(strParam params.String) bool {
|
|
||||||
return strParam.Key == "path" && strParam.Value == "/skins/username.png?authlib=1.5.2"
|
|
||||||
}),
|
|
||||||
mock.Anything, // Already tested
|
|
||||||
mock.Anything, // Already tested
|
|
||||||
mock.MatchedBy(func(strParam params.String) bool {
|
|
||||||
return strParam.Key == "forwardedIp" && strParam.Value == "1.2.3.4"
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLogger(t *testing.T) {
|
|
||||||
for name, c := range loggerTestCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
loggerMock := &LoggerMock{}
|
|
||||||
if c.ExpectedCalls != nil {
|
|
||||||
for _, c := range c.ExpectedCalls {
|
|
||||||
topicName, _ := c[0].(string)
|
|
||||||
loggerMock.On(topicName, c[1:]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reporter := &Logger{
|
|
||||||
Logger: loggerMock,
|
|
||||||
}
|
|
||||||
|
|
||||||
d := dispatcher.New()
|
|
||||||
reporter.ConfigureWithDispatcher(d)
|
|
||||||
for _, args := range c.Events {
|
|
||||||
eventName, _ := args[0].(string)
|
|
||||||
d.Emit(eventName, args[1:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.ExpectedCalls != nil {
|
|
||||||
for _, c := range c.ExpectedCalls {
|
|
||||||
topicName, _ := c[0].(string)
|
|
||||||
loggerMock.AssertCalled(t, topicName, c[1:]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,116 +0,0 @@
|
|||||||
package eventsubscribers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/mono83/slf"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StatsReporter struct {
|
|
||||||
slf.StatsReporter
|
|
||||||
Prefix string
|
|
||||||
|
|
||||||
timersMap map[string]time.Time
|
|
||||||
timersMutex sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
type Reporter interface {
|
|
||||||
Enable(reporter slf.StatsReporter)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReporterFunc func(reporter slf.StatsReporter)
|
|
||||||
|
|
||||||
func (f ReporterFunc) Enable(reporter slf.StatsReporter) {
|
|
||||||
f(reporter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: rework all reporters in the same style as it was there: https://github.com/elyby/chrly/blob/1543e98b/di/db.go#L48-L52
|
|
||||||
func (s *StatsReporter) ConfigureWithDispatcher(d Subscriber) {
|
|
||||||
s.timersMap = make(map[string]time.Time)
|
|
||||||
|
|
||||||
// Per request events
|
|
||||||
d.Subscribe("skinsystem:before_request", s.handleBeforeRequest)
|
|
||||||
d.Subscribe("skinsystem:after_request", s.handleAfterRequest)
|
|
||||||
|
|
||||||
// Authentication events
|
|
||||||
d.Subscribe("authenticator:success", s.incCounterHandler("authentication.challenge")) // TODO: legacy, remove in v5
|
|
||||||
d.Subscribe("authenticator:success", s.incCounterHandler("authentication.success"))
|
|
||||||
d.Subscribe("authentication:error", s.incCounterHandler("authentication.challenge")) // TODO: legacy, remove in v5
|
|
||||||
d.Subscribe("authentication:error", s.incCounterHandler("authentication.failed"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StatsReporter) handleBeforeRequest(req *http.Request) {
|
|
||||||
var key string
|
|
||||||
m := req.Method
|
|
||||||
p := req.URL.Path
|
|
||||||
if p == "/skins" {
|
|
||||||
key = "skins.get_request"
|
|
||||||
} else if strings.HasPrefix(p, "/skins/") {
|
|
||||||
key = "skins.request"
|
|
||||||
} else if p == "/cloaks" {
|
|
||||||
key = "capes.get_request"
|
|
||||||
} else if strings.HasPrefix(p, "/cloaks/") {
|
|
||||||
key = "capes.request"
|
|
||||||
} else if strings.HasPrefix(p, "/textures/signed/") {
|
|
||||||
key = "signed_textures.request"
|
|
||||||
} else if strings.HasPrefix(p, "/textures/") {
|
|
||||||
key = "textures.request"
|
|
||||||
} else if strings.HasPrefix(p, "/profile/") {
|
|
||||||
key = "profiles.request"
|
|
||||||
} else if m == http.MethodPost && p == "/api/skins" {
|
|
||||||
key = "api.skins.post.request"
|
|
||||||
} else if m == http.MethodDelete && strings.HasPrefix(p, "/api/skins/") {
|
|
||||||
key = "api.skins.delete.request"
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.IncCounter(key, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StatsReporter) handleAfterRequest(req *http.Request, code int) {
|
|
||||||
var key string
|
|
||||||
m := req.Method
|
|
||||||
p := req.URL.Path
|
|
||||||
if m == http.MethodPost && p == "/api/skins" && code == http.StatusCreated {
|
|
||||||
key = "api.skins.post.success"
|
|
||||||
} else if m == http.MethodPost && p == "/api/skins" && code == http.StatusBadRequest {
|
|
||||||
key = "api.skins.post.validation_failed"
|
|
||||||
} else if m == http.MethodDelete && strings.HasPrefix(p, "/api/skins/") && code == http.StatusNoContent {
|
|
||||||
key = "api.skins.delete.success"
|
|
||||||
} else if m == http.MethodDelete && strings.HasPrefix(p, "/api/skins/") && code == http.StatusNotFound {
|
|
||||||
key = "api.skins.delete.not_found"
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.IncCounter(key, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StatsReporter) incCounterHandler(name string) func(...interface{}) {
|
|
||||||
return func(...interface{}) {
|
|
||||||
s.IncCounter(name, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StatsReporter) startTimeRecording(timeKey string) {
|
|
||||||
s.timersMutex.Lock()
|
|
||||||
defer s.timersMutex.Unlock()
|
|
||||||
s.timersMap[timeKey] = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StatsReporter) finalizeTimeRecording(timeKey string, statName string) {
|
|
||||||
s.timersMutex.Lock()
|
|
||||||
defer s.timersMutex.Unlock()
|
|
||||||
startedAt, ok := s.timersMap[timeKey]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(s.timersMap, timeKey)
|
|
||||||
|
|
||||||
s.RecordTimer(statName, time.Since(startedAt))
|
|
||||||
}
|
|
@@ -1,240 +0,0 @@
|
|||||||
package eventsubscribers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/mono83/slf"
|
|
||||||
|
|
||||||
"ely.by/chrly/internal/dispatcher"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
func prepareStatsReporterArgs(name string, value interface{}, params []slf.Param) []interface{} {
|
|
||||||
args := []interface{}{name, value}
|
|
||||||
for _, v := range params {
|
|
||||||
args = append(args, v.(interface{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
||||||
type StatsReporterMock struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *StatsReporterMock) IncCounter(name string, value int64, params ...slf.Param) {
|
|
||||||
r.Called(prepareStatsReporterArgs(name, value, params)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *StatsReporterMock) UpdateGauge(name string, value int64, params ...slf.Param) {
|
|
||||||
r.Called(prepareStatsReporterArgs(name, value, params)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *StatsReporterMock) RecordTimer(name string, duration time.Duration, params ...slf.Param) {
|
|
||||||
r.Called(prepareStatsReporterArgs(name, duration, params)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *StatsReporterMock) Timer(name string, params ...slf.Param) slf.Timer {
|
|
||||||
return slf.NewTimer(name, params, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
type StatsReporterTestCase struct {
|
|
||||||
Events [][]interface{}
|
|
||||||
ExpectedCalls [][]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var statsReporterTestCases = []*StatsReporterTestCase{
|
|
||||||
// Before request
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/skins/username", nil)},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "skins.request", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/skins?name=username", nil)},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "skins.get_request", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/cloaks/username", nil)},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "capes.request", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/cloaks?name=username", nil)},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "capes.get_request", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/textures/username", nil)},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "textures.request", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/textures/signed/username", nil)},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "signed_textures.request", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/profile/username", nil)},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "profiles.request", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:before_request", httptest.NewRequest("POST", "http://localhost/api/skins", nil)},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "api.skins.post.request", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:before_request", httptest.NewRequest("DELETE", "http://localhost/api/skins/username", nil)},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "api.skins.delete.request", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:before_request", httptest.NewRequest("DELETE", "http://localhost/api/skins/id:1", nil)},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "api.skins.delete.request", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/unknown", nil)},
|
|
||||||
},
|
|
||||||
ExpectedCalls: nil,
|
|
||||||
},
|
|
||||||
// After request
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:after_request", httptest.NewRequest("POST", "http://localhost/api/skins", nil), 201},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "api.skins.post.success", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:after_request", httptest.NewRequest("POST", "http://localhost/api/skins", nil), 400},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "api.skins.post.validation_failed", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:after_request", httptest.NewRequest("DELETE", "http://localhost/api/skins/username", nil), 204},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "api.skins.delete.success", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:after_request", httptest.NewRequest("DELETE", "http://localhost/api/skins/username", nil), 404},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "api.skins.delete.not_found", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:after_request", httptest.NewRequest("DELETE", "http://localhost/api/skins/id:1", nil), 204},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "api.skins.delete.success", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:after_request", httptest.NewRequest("DELETE", "http://localhost/api/skins/id:1", nil), 404},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "api.skins.delete.not_found", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"skinsystem:after_request", httptest.NewRequest("DELETE", "http://localhost/unknown", nil), 404},
|
|
||||||
},
|
|
||||||
ExpectedCalls: nil,
|
|
||||||
},
|
|
||||||
// Authenticator
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"authenticator:success"},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "authentication.challenge", int64(1)},
|
|
||||||
{"IncCounter", "authentication.success", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Events: [][]interface{}{
|
|
||||||
{"authentication:error", errors.New("error")},
|
|
||||||
},
|
|
||||||
ExpectedCalls: [][]interface{}{
|
|
||||||
{"IncCounter", "authentication.challenge", int64(1)},
|
|
||||||
{"IncCounter", "authentication.failed", int64(1)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStatsReporter(t *testing.T) {
|
|
||||||
for _, c := range statsReporterTestCases {
|
|
||||||
t.Run("handle events", func(t *testing.T) {
|
|
||||||
statsReporterMock := &StatsReporterMock{}
|
|
||||||
if c.ExpectedCalls != nil {
|
|
||||||
for _, c := range c.ExpectedCalls {
|
|
||||||
topicName, _ := c[0].(string)
|
|
||||||
statsReporterMock.On(topicName, c[1:]...).Once()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reporter := &StatsReporter{
|
|
||||||
StatsReporter: statsReporterMock,
|
|
||||||
Prefix: "mock_prefix",
|
|
||||||
}
|
|
||||||
|
|
||||||
d := dispatcher.New()
|
|
||||||
reporter.ConfigureWithDispatcher(d)
|
|
||||||
for _, e := range c.Events {
|
|
||||||
eventName, _ := e[0].(string)
|
|
||||||
d.Emit(eventName, e[1:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
statsReporterMock.AssertExpectations(t)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,7 +0,0 @@
|
|||||||
package eventsubscribers
|
|
||||||
|
|
||||||
import "ely.by/chrly/internal/dispatcher"
|
|
||||||
|
|
||||||
type Subscriber interface {
|
|
||||||
dispatcher.Subscriber
|
|
||||||
}
|
|
@@ -7,23 +7,17 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/mono83/slf"
|
"github.com/mono83/slf"
|
||||||
"github.com/mono83/slf/wd"
|
"github.com/mono83/slf/wd"
|
||||||
|
|
||||||
"ely.by/chrly/internal/dispatcher"
|
"ely.by/chrly/internal/version"
|
||||||
v "ely.by/chrly/internal/version"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Emitter interface {
|
|
||||||
dispatcher.Emitter
|
|
||||||
}
|
|
||||||
|
|
||||||
func StartServer(server *http.Server, logger slf.Logger) {
|
func StartServer(server *http.Server, logger slf.Logger) {
|
||||||
logger.Debug("Chrly :v (:c)", wd.StringParam("v", v.Version()), wd.StringParam("c", v.Commit()))
|
logger.Debug("Chrly :v (:c)", wd.StringParam("v", version.Version()), wd.StringParam("c", version.Commit()))
|
||||||
|
|
||||||
done := make(chan bool, 1)
|
done := make(chan bool, 1)
|
||||||
go func() {
|
go func() {
|
||||||
@@ -62,21 +56,14 @@ func (lrw *loggingResponseWriter) WriteHeader(code int) {
|
|||||||
lrw.ResponseWriter.WriteHeader(code)
|
lrw.ResponseWriter.WriteHeader(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateRequestEventsMiddleware(emitter Emitter, prefix string) mux.MiddlewareFunc {
|
func CreateRequestEventsMiddleware() mux.MiddlewareFunc {
|
||||||
beforeTopic := strings.Join([]string{prefix, "before_request"}, ":")
|
|
||||||
afterTopic := strings.Join([]string{prefix, "after_request"}, ":")
|
|
||||||
|
|
||||||
return func(handler http.Handler) http.Handler {
|
return func(handler http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
emitter.Emit(beforeTopic, req)
|
|
||||||
|
|
||||||
lrw := &loggingResponseWriter{
|
lrw := &loggingResponseWriter{
|
||||||
ResponseWriter: resp,
|
ResponseWriter: resp,
|
||||||
statusCode: http.StatusOK,
|
statusCode: http.StatusOK,
|
||||||
}
|
}
|
||||||
handler.ServeHTTP(lrw, req)
|
handler.ServeHTTP(lrw, req)
|
||||||
|
|
||||||
emitter.Emit(afterTopic, req, lrw.statusCode)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,24 +11,12 @@ import (
|
|||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
)
|
)
|
||||||
|
|
||||||
type emitterMock struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *emitterMock) Emit(name string, args ...interface{}) {
|
|
||||||
e.Called(append([]interface{}{name}, args...)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateRequestEventsMiddleware(t *testing.T) {
|
func TestCreateRequestEventsMiddleware(t *testing.T) {
|
||||||
req := httptest.NewRequest("GET", "http://example.com", nil)
|
req := httptest.NewRequest("GET", "http://example.com", nil)
|
||||||
resp := httptest.NewRecorder()
|
resp := httptest.NewRecorder()
|
||||||
|
|
||||||
emitter := &emitterMock{}
|
|
||||||
emitter.On("Emit", "test_prefix:before_request", req)
|
|
||||||
emitter.On("Emit", "test_prefix:after_request", req, 400)
|
|
||||||
|
|
||||||
isHandlerCalled := false
|
isHandlerCalled := false
|
||||||
middlewareFunc := CreateRequestEventsMiddleware(emitter, "test_prefix")
|
middlewareFunc := CreateRequestEventsMiddleware()
|
||||||
middlewareFunc.Middleware(http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
middlewareFunc.Middleware(http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
resp.WriteHeader(400)
|
resp.WriteHeader(400)
|
||||||
isHandlerCalled = true
|
isHandlerCalled = true
|
||||||
@@ -37,8 +25,6 @@ func TestCreateRequestEventsMiddleware(t *testing.T) {
|
|||||||
if !isHandlerCalled {
|
if !isHandlerCalled {
|
||||||
t.Fatal("Handler isn't called from the middleware")
|
t.Fatal("Handler isn't called from the middleware")
|
||||||
}
|
}
|
||||||
|
|
||||||
emitter.AssertExpectations(t)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type authCheckerMock struct {
|
type authCheckerMock struct {
|
||||||
|
Reference in New Issue
Block a user