Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor web server #77

Merged
merged 8 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 14 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,30 +73,25 @@ Before launching the proxy (notifier) service, it has to be configured so that i
correct environment variables.

The supported config variables are:
- `Port`: the port on which the http server listens on. Should be the same
as the port in the `ProxyUrl` described above.
- `Username`: the username used to authorize an observer. Can be left empty for `UseAuthorization = false`.
- `Password`: the password used to authorize an observer. Can be left empty for `UseAuthorization = false`.
- `Host`: the host and port on which the http server listens on. Should be the same port
as the one specified in the `ProxyUrl` described above.
- `Username`: the username used to authorize an observer. Can be left empty for `UseAuthorization = false` on observer connector.
- `Password`: the password used to authorize an observer. Can be left empty for `UseAuthorization = false` on observer connector.
- `CheckDuplicates`: if true, it will check (based on a locker service using redis) if the event have been already pushed to clients

The [config](https://github.com/multiversx/mx-chain-notifier-go/blob/main/cmd/notifier/config/config.toml) file:

If observer connector is set to use BasicAuth with `UseAuthorization = true`, `Username` and `Password` has to be
set here on events notifier, and `Auth` flag has to be enabled in
[`api.toml`](https://github.com/multiversx/mx-chain-notifier-go/blob/main/cmd/notifier/config/api.toml) config file for events path.
For example:
```toml
[ConnectorApi]
# The port on which the Hub listens for subscriptions
Port = "5000"

# Username is the username needed to authorize an observer to push data
Username = ""

# Password is the password needed to authorize an observer to push event data
Password = ""

# CheckDuplicates signals if the events received from observers have been already pushed to clients
# Requires a redis instance/cluster and should be used when multiple observers push from the same shard
CheckDuplicates = true
[APIPackages.events]
Routes = [
{ Name = "/push", Open = true, Auth = true },
{ Name = "/revert", Open = true, Auth = false },
```

The main config file can be found [here](https://github.com/multiversx/mx-chain-notifier-go/blob/main/cmd/notifier/config/config.toml).

After the configuration file is set up, the notifier instance can be
launched.

Expand Down
47 changes: 30 additions & 17 deletions api/gin/webServer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,25 @@ import (
"github.com/multiversx/mx-chain-notifier-go/config"
)

const (
defaultRestInterface = "localhost:5000"
)

var log = logger.GetOrCreate("api/gin")

// ArgsWebServerHandler holds the arguments needed to create a web server handler
type ArgsWebServerHandler struct {
Facade shared.FacadeHandler
Config config.ConnectorApiConfig
Type string
Facade shared.FacadeHandler
Configs config.Configs
}

// webServer is a wrapper for gin.Engine, holding additional components
type webServer struct {
sync.RWMutex
facade shared.FacadeHandler
httpServer shared.HTTPServerCloser
config config.ConnectorApiConfig
configs config.Configs
groups map[string]shared.GroupHandler
apiType string
wasTriggered bool
cancelFunc func()
}
Expand All @@ -47,8 +49,7 @@ func NewWebServerHandler(args ArgsWebServerHandler) (*webServer, error) {

return &webServer{
facade: args.Facade,
config: args.Config,
apiType: args.Type,
configs: args.Configs,
groups: make(map[string]shared.GroupHandler),
wasTriggered: false,
}, nil
Expand All @@ -58,13 +59,26 @@ func checkArgs(args ArgsWebServerHandler) error {
if check.IfNil(args.Facade) {
return apiErrors.ErrNilFacadeHandler
}
if args.Type == "" {
if args.Configs.Flags.APIType == "" {
return common.ErrInvalidAPIType
}

return nil
}

func (w *webServer) getWSAddr() string {
addr := w.configs.GeneralConfig.ConnectorApi.Host
if addr == "" {
return defaultRestInterface
}

if !strings.Contains(addr, ":") {
return fmt.Sprintf(":%s", addr)
}

return addr
}

// Run starts the server and the Hub as goroutines
// It returns an instance of http.Server
func (w *webServer) Run() error {
Expand All @@ -78,11 +92,6 @@ func (w *webServer) Run() error {
return nil
}

port := w.config.Port
if !strings.Contains(port, ":") {
port = fmt.Sprintf(":%s", port)
}

engine := gin.Default()
engine.Use(cors.Default())

Expand All @@ -93,8 +102,10 @@ func (w *webServer) Run() error {

w.registerRoutes(engine)

addr := w.getWSAddr()

server := &http.Server{
Addr: port,
Addr: addr,
Handler: engine,
}

Expand Down Expand Up @@ -125,7 +136,7 @@ func (w *webServer) createGroups() error {
}
groupsMap["status"] = statusGroup

if w.apiType == common.WSAPIType {
if w.configs.Flags.APIType == common.WSAPIType {
hubHandler, err := groups.NewHubGroup(w.facade)
if err != nil {
return err
Expand All @@ -141,8 +152,10 @@ func (w *webServer) createGroups() error {
func (w *webServer) registerRoutes(ginEngine *gin.Engine) {
for groupName, groupHandler := range w.groups {
log.Info("registering API group", "group name", groupName)
ginGroup := ginEngine.Group(fmt.Sprintf("/%s", groupName)).Use(groupHandler.GetAdditionalMiddlewares()...)
groupHandler.RegisterRoutes(ginGroup)

ginGroup := ginEngine.Group(fmt.Sprintf("/%s", groupName))

groupHandler.RegisterRoutes(ginGroup, w.configs.ApiRoutesConfig)
}
}

Expand Down
14 changes: 10 additions & 4 deletions api/gin/webServer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,16 @@ import (
func createMockArgsWebServerHandler() gin.ArgsWebServerHandler {
return gin.ArgsWebServerHandler{
Facade: &mocks.FacadeStub{},
Config: config.ConnectorApiConfig{
Port: "8080",
Configs: config.Configs{
GeneralConfig: config.GeneralConfig{
ConnectorApi: config.ConnectorApiConfig{
Host: "8080",
},
},
Flags: config.FlagsConfig{
APIType: "notifier",
},
},
Type: "notifier",
}
}

Expand All @@ -40,7 +46,7 @@ func TestNewWebServerHandler(t *testing.T) {
t.Parallel()

args := createMockArgsWebServerHandler()
args.Type = ""
args.Configs.Flags.APIType = ""

ws, err := gin.NewWebServerHandler(args)
require.True(t, check.IfNil(ws))
Expand Down
50 changes: 48 additions & 2 deletions api/groups/baseGroup.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,68 @@
package groups

import (
"strings"

"github.com/gin-gonic/gin"
logger "github.com/multiversx/mx-chain-logger-go"
"github.com/multiversx/mx-chain-notifier-go/api/shared"
"github.com/multiversx/mx-chain-notifier-go/config"
)

var log = logger.GetOrCreate("api/groups")

type baseGroup struct {
endpoints []*shared.EndpointHandlerData
endpoints []*shared.EndpointHandlerData
additionalMiddlewares []gin.HandlerFunc
}

// RegisterRoutes will register all the endpoints to the given web server
func (bg *baseGroup) RegisterRoutes(
ws gin.IRoutes,
ws *gin.RouterGroup,
apiConfig config.APIRoutesConfig,
) {
for _, handlerData := range bg.endpoints {
isOpen, isAuthEnabled := getEndpointStatus(ws, handlerData.Path, apiConfig)
if !isOpen {
log.Debug("endpoint is closed", "path", handlerData.Path)
continue
}

if isAuthEnabled {
ws.Use(bg.GetAdditionalMiddlewares()...)
bogdan-rosianu marked this conversation as resolved.
Show resolved Hide resolved
}

ws.Handle(handlerData.Method, handlerData.Path, handlerData.Handler)
}
}

// GetAdditionalMiddlewares return additional middlewares
bogdan-rosianu marked this conversation as resolved.
Show resolved Hide resolved
func (bg *baseGroup) GetAdditionalMiddlewares() []gin.HandlerFunc {
return bg.additionalMiddlewares
}

func getEndpointStatus(
ws *gin.RouterGroup,
path string,
apiConfig config.APIRoutesConfig,
) (bool, bool) {
basePath := ws.BasePath()

// ws.BasePath will return paths like /group
// so we need the last token after splitting by /
splitPath := strings.Split(basePath, "/")
basePath = splitPath[len(splitPath)-1]

group, ok := apiConfig.APIPackages[basePath]
if !ok {
return false, false
}

for _, route := range group.Routes {
if route.Name == path {
return route.Open, route.Auth
}
}

return false, false
}
7 changes: 4 additions & 3 deletions api/groups/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import (
"fmt"
"io"

"github.com/multiversx/mx-chain-notifier-go/api/shared"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/multiversx/mx-chain-notifier-go/api/shared"
"github.com/multiversx/mx-chain-notifier-go/config"
)

func startWebServer(group shared.GroupHandler, path string) *gin.Engine {
func startWebServer(group shared.GroupHandler, path string, apiConfig config.APIRoutesConfig) *gin.Engine {
ws := gin.New()
ws.Use(cors.Default())
routes := ws.Group(path)
group.RegisterRoutes(routes)
group.RegisterRoutes(routes, apiConfig)
return ws
}

Expand Down
15 changes: 5 additions & 10 deletions api/groups/eventsGroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ const (

type eventsGroup struct {
*baseGroup
facade EventsFacadeHandler
additionalMiddlewares []gin.HandlerFunc
facade EventsFacadeHandler
}

// NewEventsGroup registers handlers for the /events group
Expand All @@ -30,9 +29,10 @@ func NewEventsGroup(facade EventsFacadeHandler) (*eventsGroup, error) {
}

h := &eventsGroup{
baseGroup: &baseGroup{},
facade: facade,
additionalMiddlewares: make([]gin.HandlerFunc, 0),
facade: facade,
baseGroup: &baseGroup{
additionalMiddlewares: make([]gin.HandlerFunc, 0),
},
}

h.createMiddlewares()
Expand Down Expand Up @@ -60,11 +60,6 @@ func NewEventsGroup(facade EventsFacadeHandler) (*eventsGroup, error) {
return h, nil
}

// GetAdditionalMiddlewares return additional middlewares for this group
func (h *eventsGroup) GetAdditionalMiddlewares() []gin.HandlerFunc {
return h.additionalMiddlewares
}

func (h *eventsGroup) pushEvents(c *gin.Context) {
pushEventsRawData, err := c.GetRawData()
if err != nil {
Expand Down
Loading
Loading