Skip to content

Commit

Permalink
fix. update README.md, add NodeID (for describe purposes). minor fixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
imperiuse committed Jul 3, 2022
1 parent 73277c9 commit ae1d01e
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 65 deletions.
Binary file modified .img/example_get.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .img/example_get_2_not_ready.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .img/example_get_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified .img/example_post.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .img/example_post_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,28 @@ From root of the repository:
2) Start monitoring

```curl --request POST --url http://localhost:4000/api/v1/monitoring?cur=btcusd&period=1m&freq=10s```
![img](./.img/example_post.png)
3) ![img](./.img/example_post_2.png)

3) Get results of monitoring
4) Get results of monitoring

```curl --request GET --url http://localhost:4000/api/v1/monitoring/1```

![img](./.img/example_get.png)
![img](./.img/example_get_2_not_ready.png)
![img](./.img/example_get_3.png)

#### Insomnia examples:
Optional:

see in folder -> `.insomnia`
4) Get result monitoring and delete monitoring record from db

![img](./.img/example_post.png)
![img](./.img/example_get.png)
```curl --request GET --url http://localhost:4000/api/v1/monitoring/1?delete=true```


#### Insomnia examples:

see in folder -> `.insomnia`

#### Docker Compose run

![img](./.img/result_running_service_in_docker_compose.png)
Expand All @@ -88,7 +96,7 @@ see in folder -> `.insomnia`

#### Env up

make dev_env_up
make dev_env_up

After that can run go app (in JetBrains IDE or VSCode) (do not forget set up .env file and add ENVs)

Expand All @@ -97,11 +105,11 @@ After that can run go app (in JetBrains IDE or VSCode) (do not forget set up .en

#### Remove old container or volumes

make docker_clean_all
make docker_clean_all

#### Rebuild prod app container

make rebuild
make rebuild

Made by Arseny Sazanov 2022

Expand Down
12 changes: 6 additions & 6 deletions configs/default.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
logger:
level: info
encoding: json
color: false
encoding: console
color: true
outputs:
- stdout
tags:
Expand Down Expand Up @@ -32,7 +32,7 @@ services:
port: 5432
database: pm
maxTryConnect: 3
timeoutTryConnect: 5s
timeoutTryConnect: "5s"
options:
maxLifeTime: 600
maxIdleConn: 10
Expand All @@ -41,10 +41,10 @@ services:
controllers:
general:
monitor:
timeoutConsulLeaderCheck: "1s" # TODO define lose 1 second
timeoutConsulLeaderCheck: "1s" # TODO define that we can lose 1 second (need discuss!)

master:
scanner:
timeoutOneTaskProcess: "2s" # TODO define max timeout for one task
intervalPeriodicScan: "1s" # TODO define max frequency for price scanner
timeoutOneTaskProcess: "2s" # TODO define max timeout for one task (need discuss!)
intervalPeriodicScan: "1s" # TODO define max frequency for price scanner (need discuss!)
cntWorkers: 1
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func New(appName string, envName env.Var, configPath string, nodeName string) (C
applyEnvOnConfig(&cfg, appName)

cfg.Servers.HTTP.Name += "_" + nodeName
cfg.Servers.HTTP.NodeID = nodeName

// show config for debug purposes
// nolint forbidigo // exception of rule )
Expand Down
78 changes: 37 additions & 41 deletions internal/servers/http/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/imperiuse/price_monitor/internal/helper"
"github.com/imperiuse/price_monitor/internal/logger/field"
mw "github.com/imperiuse/price_monitor/internal/servers/http/middlerware"
"github.com/imperiuse/price_monitor/internal/servers/http/util"
"github.com/imperiuse/price_monitor/internal/services/storage"
"github.com/imperiuse/price_monitor/internal/services/storage/model"
)
Expand All @@ -23,12 +22,13 @@ const defaultLimit = 10000
type (
FormGetMonitoring struct {
ID int64 `uri:"id" binding:"required,min=1,max=9223372036854775807"`
Delete bool `form:"delete" binding:"omitempty"`
Cursor uint64 `form:"cursor" binding:"omitempty,min=0,max=18446744073709551615"`
Limit uint64 `form:"limit" binding:"omitempty,min=1,max=10000"`
}

FormPostMonitoring struct {
// TODO ALSO CAN BE LIKE HERE
// TODO ALSO CAN BE LIKE HERE (I decide to go straightforward, simple and utility)
//FromTime time.Time `form:"from" binding:"required" time_format:"2006-01-02T15:04:05" time_utc:"0"` // time.RFC3339
//ToTime time.Time `form:"to" binding:"required" time_format:"2006-01-02T15:04:05" time_utc:"0"` // time.RFC3339

Expand All @@ -52,8 +52,8 @@ type (
// @Produce json
// @Success 200
// @Router /health [get]
func Health(c *gin.Context) {
c.String(http.StatusOK, "")
func (s *Server) Health(c *gin.Context) {
c.String(http.StatusOK, `{"NodeID": %s}`, s.config.NodeID)
}

// Readiness godoc
Expand All @@ -65,8 +65,8 @@ func Health(c *gin.Context) {
// @Produce json
// @Success 200
// @Router /ready [get]
func Readiness(c *gin.Context) {
c.String(http.StatusOK, "ready")
func (s *Server) Readiness(c *gin.Context) {
c.String(http.StatusOK, `{"NodeID": %s}`, s.config.NodeID)
}

// GetMonitoring godoc
Expand All @@ -75,6 +75,7 @@ func Readiness(c *gin.Context) {
// @Id GetMonitoring
// @Tags Server API
// @Param id path int true "id of road controller"
// @Param delete query bool false "delete monitoring after"
// @Param cursor query int false "cursor for cursor pagination"
// @Param limit query int false "limit for limit pagination"// todo https://uxdesign.cc/why-facebook-says-cursor-pagination-is-the-greatest-d6b98d86b6c0
// @Accept json
Expand All @@ -93,14 +94,14 @@ func (s *Server) GetMonitoring(c *gin.Context) { // TODO toooo-looong func need
f := FormGetMonitoring{}
if err := c.ShouldBindUri(&f); err != nil {
s.log.Error("can not parse params (uri)", field.ID(f.ID), field.Any("form", f), field.Error(err))
util.SendErrorJSON(c, http.StatusBadRequest, "can not parse params (uri)", err)
s.SendErrorJSON(c, http.StatusBadRequest, "can not parse params (uri)", err)

return
}

if err := c.ShouldBindQuery(&f); err != nil {
s.log.Error("can not parse params (query)", field.ID(f.ID), field.Any("form", f), field.Error(err))
util.SendErrorJSON(c, http.StatusBadRequest, "can not parse params (query)", err)
s.SendErrorJSON(c, http.StatusBadRequest, "can not parse params (query)", err)

return
}
Expand All @@ -114,36 +115,36 @@ func (s *Server) GetMonitoring(c *gin.Context) { // TODO toooo-looong func need
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
s.log.Debug("not found monitoring obj with id", field.ID(f.ID))
util.SendErrorJSON(c, http.StatusNotFound, "no monitoring obj with that id", nil)
s.SendErrorJSON(c, http.StatusNotFound, "no monitoring obj with that id", nil)

return
}

s.log.Error("can not get data for monitoring from db", field.ID(f.ID), field.Any("form", f), field.Error(err))
util.SendErrorJSON(c, http.StatusInternalServerError, "can not get data for monitoring from db", err)
s.SendErrorJSON(c, http.StatusInternalServerError, "can not get data for monitoring from db", err)

return
}

if time.Now().UTC().Before(m.ExpiredAt) {
s.log.Debug("monitoring has not finished yet", field.Any("form", f), field.ID(f.ID))
util.SendErrorJSON(c, http.StatusAccepted, "monitoring has not finished yet", nil)
s.SendErrorJSON(c, http.StatusAccepted, "monitoring has not finished yet", nil)

return
}

curCode, err := s.getCurrencyCodeById(ctx, m.CurrencyID)
if err != nil {
s.log.Debug("not found currency code by code id", field.Any("m", m), field.ID(f.ID))
util.SendErrorJSON(c, http.StatusInternalServerError, "not found currency code by code id", nil)
s.SendErrorJSON(c, http.StatusInternalServerError, "not found currency code by code id", nil)

return
}

freq, err := time.ParseDuration(m.Frequency)
if err != nil {
s.log.Error("bad value for frequency from db", field.ID(f.ID), field.Any("m", m), field.Error(err))
util.SendErrorJSON(c, http.StatusInternalServerError, "bad value for frequency from db ", err)
s.SendErrorJSON(c, http.StatusInternalServerError, "bad value for frequency from db ", err)

return
}
Expand All @@ -162,7 +163,7 @@ func (s *Server) GetMonitoring(c *gin.Context) { // TODO toooo-looong func need
if err != nil {
s.log.Error("can not get prices data for monitoring",
field.ID(f.ID), field.Any("form", f), field.Error(err))
util.SendErrorJSON(c, http.StatusInternalServerError, "can not get prices data for monitoring", err)
s.SendErrorJSON(c, http.StatusInternalServerError, "can not get prices data for monitoring", err)

return
}
Expand All @@ -174,21 +175,20 @@ func (s *Server) GetMonitoring(c *gin.Context) { // TODO toooo-looong func need
prices = applyFreqFilter(prices, freq)

// TODO optional we can delete monitoring with that ID (auto clean table, good idea imho)
//_, err = s.storage.Connector().Repo(m).Delete(ctx, f.ID)
//if err != nil {
// s.log.Error("can not deleter monitoring", field.ID(f.ID), field.Any("form", f), field.Error(err))
//}

util.SendJSON(c, http.StatusOK, util.HTTPGoodResponse{
Time: time.Now().UTC().Unix(),
UUID: helper.FromContextGetUUID(ctx),
Status: "Ok",
Description: "Result of monitoring (time in UTC)",
H: gin.H{
if f.Delete {
_, err = s.storage.Connector().Repo(m).Delete(ctx, f.ID)
if err != nil {
s.log.Error("can not deleter monitoring", field.ID(f.ID), field.Any("form", f), field.Error(err))
}
}

s.SendJSON(c, http.StatusOK, "Result of monitoring (time in UTC)",
gin.H{
"MonitoringID": f.ID,
"StartAt": m.StartedAt,
"FinishedAt": m.ExpiredAt,
"Prices": convertToResponsePrices(prices),
},
})
})
}

func (s *Server) getCurrencyCodeById(ctx context.Context, id model.Identity) (string, error) {
Expand Down Expand Up @@ -263,30 +263,30 @@ func (s *Server) PostMonitoring(c *gin.Context) { // TODO toooo-looong func nee
periodDuration, err := time.ParseDuration(f.Period)
if err != nil {
s.log.Error("bad value for period", field.Any("form", f), field.Error(err))
util.SendErrorJSON(c, http.StatusBadRequest, "bad value for period", err)
s.SendErrorJSON(c, http.StatusBadRequest, "bad value for period", err)

return
}

freqDur, err := time.ParseDuration(f.Frequency)
if err != nil {
s.log.Error("bad value for frequncy", field.Any("form", f), field.Error(err))
util.SendErrorJSON(c, http.StatusBadRequest, "bad value for frequency", err)
s.SendErrorJSON(c, http.StatusBadRequest, "bad value for frequency", err)

return
}

if f.Period == "" || f.Frequency == "" || f.Currency == "" {
s.log.Error("bad value fin form", field.Any("form", f), field.Error(err))
util.SendErrorJSON(c, http.StatusBadRequest, "bad values in form", err)
s.SendErrorJSON(c, http.StatusBadRequest, "bad values in form", err)

return
}

// TODO need clarify this, I add my constraints instead
if periodDuration > time.Hour*24 || freqDur < time.Second {
s.log.Error("period to much or freq too low", field.Any("form", f), field.Error(err))
util.SendErrorJSON(c, http.StatusBadRequest, "freqDur", err)
s.SendErrorJSON(c, http.StatusBadRequest, "freqDur", err)

return
}
Expand All @@ -299,28 +299,24 @@ func (s *Server) PostMonitoring(c *gin.Context) { // TODO toooo-looong func nee
m.CurrencyID, err = s.getCurrencyIdByCurrencyCode(ctx, f.Currency)
if err != nil {
s.log.Error("can not get data currency data from db", field.Any("form", f), field.Error(err))
util.SendErrorJSON(c, http.StatusInternalServerError, "can not get data currency data from db", err)
s.SendErrorJSON(c, http.StatusBadRequest, "can not get data currency data from db."+
" probably you try to start monitoring unsupported currency", err)

return
}

id, err := s.storage.Connector().Repo(m).Create(ctx, m)
if err != nil {
s.log.Error("can not create new monitor obj", field.Any("m", m), field.Error(err))
util.SendErrorJSON(c, http.StatusInternalServerError, "can not create new monitor obj", err)
s.SendErrorJSON(c, http.StatusInternalServerError, "can not create new monitor obj", err)

return
}

util.SendJSON(c, http.StatusOK, util.HTTPGoodResponse{
Time: time.Now().UTC().Unix(),
UUID: helper.FromContextGetUUID(ctx),
Status: "Ok",
Description: "Successfully created new monitoring",
H: gin.H{
s.SendJSON(c, http.StatusOK, "Successfully created new monitoring",
gin.H{
"MonitoringID": id,
},
})
})

}

Expand Down
5 changes: 3 additions & 2 deletions internal/servers/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type (
// Config - config struct.
Config struct {
Name string
NodeID string
Address string
DomainName string `yaml:"domainName"`
AllowOrigin string `yaml:"allowOrigin"`
Expand Down Expand Up @@ -117,8 +118,8 @@ func New(

// Server's check handlers
// todo metrics middleware -> mw.MetricsRPC("health", Health))
e.GET("/health", Health)
e.GET("/ready", Readiness)
e.GET("/health", s.Health)
e.GET("/ready", s.Readiness)

// Add a ginzap middleware, which:
// - Logs all requests, like a combined access and error log.
Expand Down
Loading

0 comments on commit ae1d01e

Please sign in to comment.