refactor foundry lib structure; add transport classes for db,http,websocket; add action interface instead of multiple function that accepts action classes; add changing foundry status on websocket responses
This commit is contained in:
9
internal/foundry/actions/errors.go
Normal file
9
internal/foundry/actions/errors.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package actions
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrActionNotFound = errors.New("Action doesn't found")
|
||||
ErrObjTypeNotMatch = errors.New("Provided value type didn't match obj field type")
|
||||
ErrObjNotPointer = errors.New("Passed obj is not pointer")
|
||||
)
|
||||
69
internal/foundry/actions/model.go
Normal file
69
internal/foundry/actions/model.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
|
||||
)
|
||||
|
||||
const (
|
||||
WsSessionType = "session"
|
||||
WsProgressType = "progress"
|
||||
WsUserActivityType = "userActivity"
|
||||
WsShutdownType = "shutdown"
|
||||
)
|
||||
|
||||
type WsActions interface {
|
||||
Action(tr *transport.FoundryTransport, status *types.FoundryStatus) error
|
||||
}
|
||||
|
||||
var WsMsgActions = map[string]func() WsActions{
|
||||
"session": func() WsActions { return &WsSessionMsg{} },
|
||||
"progress": func() WsActions { return &WsProgressMsg{} },
|
||||
// "userActivity": WsUserActivityCursor{},
|
||||
"shutdown": func() WsActions { return &WsShutdownMsg{} },
|
||||
}
|
||||
|
||||
func GetWsMsgAction(dataType string) WsActions {
|
||||
if factory, ok := WsMsgActions[dataType]; ok {
|
||||
return factory()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SplitToTypeAndData(dataJson []byte) (WsActions, error) {
|
||||
var rawItems []json.RawMessage
|
||||
err := json.Unmarshal(dataJson, &rawItems)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var dataType string
|
||||
err = json.Unmarshal(rawItems[0], &dataType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dataType == WsUserActivityType {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var data map[string]any
|
||||
err = json.Unmarshal(rawItems[1], &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
actionDataPtr := GetWsMsgAction(dataType)
|
||||
if actionDataPtr == nil {
|
||||
return nil, ErrActionNotFound
|
||||
}
|
||||
|
||||
err = FillStruct(data, actionDataPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return actionDataPtr, nil
|
||||
}
|
||||
37
internal/foundry/actions/progress.go
Normal file
37
internal/foundry/actions/progress.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
|
||||
)
|
||||
|
||||
type WsProgressMsg struct {
|
||||
Id string `json:"id"`
|
||||
Message string `json:"message"`
|
||||
Pct float64 `json:"pct"`
|
||||
HasChanged bool `json:"hasChanged,omitempty"`
|
||||
Act string `json:"action"`
|
||||
Step string `json:"step"`
|
||||
}
|
||||
|
||||
func (msg WsProgressMsg) Action(tr *transport.FoundryTransport, status *types.FoundryStatus) error {
|
||||
if !msg.HasChanged {
|
||||
tr.Logger.Debug("World has been launched", "world", msg.Id)
|
||||
select {
|
||||
case <-tr.ExchangeChan.ProgressMsg[msg.Id]:
|
||||
tr.Logger.Warn("Channel for the world is closed", "world", msg.Id)
|
||||
default:
|
||||
close(tr.ExchangeChan.ProgressMsg[msg.Id])
|
||||
}
|
||||
|
||||
if status.IsActive != true {
|
||||
newStatus, err := tr.Http.GetStatus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
status.Update(newStatus)
|
||||
tr.Logger.Info("Status has been changed", "status", status)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
26
internal/foundry/actions/session.go
Normal file
26
internal/foundry/actions/session.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
|
||||
)
|
||||
|
||||
type WsSessionMsg struct {
|
||||
SessionId string `json:"sessionId"`
|
||||
UserId *string `json:"userId,omitempty"`
|
||||
}
|
||||
|
||||
func (msg WsSessionMsg) Action(tr *transport.FoundryTransport, status *types.FoundryStatus) error {
|
||||
if status.IsActive {
|
||||
tr.Logger.Warn("World is started. Please, return to setup page to fully initialize database")
|
||||
return nil
|
||||
}
|
||||
|
||||
if tr.IsDbInit {
|
||||
return nil
|
||||
}
|
||||
tr.IsDbInit = true
|
||||
|
||||
go tr.FillDBWithFoundryData()
|
||||
return nil
|
||||
}
|
||||
30
internal/foundry/actions/shutdown.go
Normal file
30
internal/foundry/actions/shutdown.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
|
||||
)
|
||||
|
||||
type WsShutdownMsg struct {
|
||||
World string `json:"world"`
|
||||
UserId *string `json:"userId,omitempty"`
|
||||
}
|
||||
|
||||
func (msg WsShutdownMsg) Action(tr *transport.FoundryTransport, status *types.FoundryStatus) error {
|
||||
if status.IsActive != false {
|
||||
newStatus, err := tr.Http.GetStatus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
status.Update(newStatus)
|
||||
tr.Logger.Info("Status has been changed", "status", status)
|
||||
}
|
||||
|
||||
if tr.IsDbInit {
|
||||
return nil
|
||||
}
|
||||
tr.IsDbInit = true
|
||||
|
||||
go tr.FillDBWithFoundryData()
|
||||
return nil
|
||||
}
|
||||
20
internal/foundry/actions/user_activity.go
Normal file
20
internal/foundry/actions/user_activity.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
|
||||
)
|
||||
|
||||
type WsUserActivityMsg struct {
|
||||
Cursor WsUserActivityCursor `json:"cursor"`
|
||||
}
|
||||
|
||||
type WsUserActivityCursor struct {
|
||||
X int `json:"x"`
|
||||
Y int `json:"y"`
|
||||
}
|
||||
|
||||
func (msg WsUserActivityMsg) Action(tr *transport.FoundryTransport, status *types.FoundryStatus) error {
|
||||
tr.Logger.Debug("WsUserActivityMsg")
|
||||
return nil
|
||||
}
|
||||
87
internal/foundry/actions/utils.go
Normal file
87
internal/foundry/actions/utils.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func findByTag(obj any, tag string) (int, error) {
|
||||
structType := reflect.TypeOf(obj).Elem()
|
||||
for i := 0; i < structType.NumField(); i++ {
|
||||
fieldTags := structType.Field(i).Tag.Get("json")
|
||||
if tag == strings.Split(fieldTags, ",")[0] {
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
return -1, fmt.Errorf("No such field: %s in obj", tag)
|
||||
}
|
||||
|
||||
func setField(obj any, name string, value any) error {
|
||||
structValue := reflect.ValueOf(obj).Elem()
|
||||
idx, err := findByTag(obj, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
structFieldValue := structValue.Field(idx)
|
||||
|
||||
if !structFieldValue.IsValid() {
|
||||
return fmt.Errorf("No such field: %s in obj", name)
|
||||
}
|
||||
|
||||
if !structFieldValue.CanSet() {
|
||||
return fmt.Errorf("Cannot set %s field value", name)
|
||||
}
|
||||
|
||||
structFieldType := structFieldValue.Type()
|
||||
val := reflect.ValueOf(value)
|
||||
if structFieldType != val.Type() {
|
||||
return ErrObjTypeNotMatch
|
||||
}
|
||||
|
||||
if value == nil {
|
||||
structFieldValue.Set(reflect.Zero(structFieldType))
|
||||
} else {
|
||||
structFieldValue.Set(val)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isPointer(obj any) bool {
|
||||
iv := reflect.ValueOf(obj)
|
||||
if !iv.IsValid() {
|
||||
return false
|
||||
}
|
||||
|
||||
return iv.Kind() == reflect.Pointer
|
||||
}
|
||||
|
||||
func FillStruct(m map[string]any, obj any) error {
|
||||
if !isPointer(obj) {
|
||||
return ErrObjNotPointer
|
||||
}
|
||||
|
||||
for k, v := range m {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
err := setField(obj, k, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Dereference(input any) any {
|
||||
v := reflect.ValueOf(input)
|
||||
|
||||
// Check if the value is a pointer
|
||||
if v.Kind() == reflect.Pointer {
|
||||
// v.Elem() returns the value the pointer points to
|
||||
return v.Elem().Interface()
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
Reference in New Issue
Block a user