mirror of
https://github.com/semaphoreui/semaphore.git
synced 2024-11-23 12:30:41 +01:00
164 lines
4.1 KiB
Go
164 lines
4.1 KiB
Go
package db
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/ansible-semaphore/semaphore/util"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
func ConvertFlatToNested(flatMap map[string]string) map[string]interface{} {
|
|
nestedMap := make(map[string]interface{})
|
|
|
|
for key, value := range flatMap {
|
|
parts := strings.Split(key, ".")
|
|
currentMap := nestedMap
|
|
|
|
for i, part := range parts {
|
|
if i == len(parts)-1 {
|
|
currentMap[part] = value
|
|
} else {
|
|
if _, exists := currentMap[part]; !exists {
|
|
currentMap[part] = make(map[string]interface{})
|
|
}
|
|
currentMap = currentMap[part].(map[string]interface{})
|
|
}
|
|
}
|
|
}
|
|
|
|
return nestedMap
|
|
}
|
|
|
|
func AssignMapToStruct[P *S, S any](m map[string]interface{}, s P) error {
|
|
v := reflect.ValueOf(s).Elem()
|
|
return assignMapToStructRecursive(m, v)
|
|
}
|
|
|
|
func cloneStruct(origValue reflect.Value) reflect.Value {
|
|
// Create a new instance of the same type as the original struct
|
|
cloneValue := reflect.New(origValue.Type()).Elem()
|
|
|
|
// Iterate over the fields of the struct
|
|
for i := 0; i < origValue.NumField(); i++ {
|
|
// Get the field value
|
|
fieldValue := origValue.Field(i)
|
|
// Set the field value in the clone
|
|
cloneValue.Field(i).Set(fieldValue)
|
|
}
|
|
|
|
// Return the cloned struct
|
|
return cloneValue
|
|
}
|
|
|
|
func assignMapToStructRecursive(m map[string]interface{}, structValue reflect.Value) error {
|
|
structType := structValue.Type()
|
|
|
|
for i := 0; i < structType.NumField(); i++ {
|
|
field := structType.Field(i)
|
|
jsonTag := field.Tag.Get("json")
|
|
if jsonTag == "" {
|
|
jsonTag = field.Name
|
|
} else {
|
|
jsonTag = strings.Split(jsonTag, ",")[0]
|
|
}
|
|
|
|
if value, ok := m[jsonTag]; ok {
|
|
fieldValue := structValue.FieldByName(field.Name)
|
|
if fieldValue.CanSet() {
|
|
val := reflect.ValueOf(value)
|
|
|
|
switch fieldValue.Kind() {
|
|
case reflect.Struct:
|
|
|
|
if val.Kind() != reflect.Map {
|
|
return fmt.Errorf("expected map for nested struct field %s but got %T", field.Name, value)
|
|
}
|
|
|
|
mapValue, ok := value.(map[string]interface{})
|
|
if !ok {
|
|
return fmt.Errorf("cannot assign value of type %T to field %s of type %s", value, field.Name, field.Type)
|
|
}
|
|
err := assignMapToStructRecursive(mapValue, fieldValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case reflect.Map:
|
|
// Handle map
|
|
if val.Kind() != reflect.Map {
|
|
return fmt.Errorf("expected map for field %s but got %T", field.Name, value)
|
|
}
|
|
|
|
for _, key := range val.MapKeys() {
|
|
mapElemValue := val.MapIndex(key)
|
|
mapElemType := fieldValue.Type().Elem()
|
|
|
|
srcVal := fieldValue.MapIndex(key)
|
|
var mapElem reflect.Value
|
|
if srcVal.IsValid() {
|
|
mapElem = cloneStruct(srcVal)
|
|
} else {
|
|
mapElem = reflect.New(mapElemType).Elem()
|
|
}
|
|
|
|
if mapElemType.Kind() == reflect.Struct {
|
|
if err := assignMapToStructRecursive(mapElemValue.Interface().(map[string]interface{}), mapElem); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if mapElemValue.Type().ConvertibleTo(mapElemType) {
|
|
mapElem.Set(mapElemValue.Convert(mapElemType))
|
|
} else {
|
|
newVal, converted := util.CastValueToKind(mapElemValue.Interface(), mapElemType.Kind())
|
|
if !converted {
|
|
return fmt.Errorf("cannot assign value of type %s to map element of type %s",
|
|
mapElemValue.Type(), mapElemType)
|
|
}
|
|
|
|
mapElem.Set(reflect.ValueOf(newVal))
|
|
}
|
|
|
|
}
|
|
|
|
fieldValue.SetMapIndex(key, mapElem)
|
|
}
|
|
|
|
default:
|
|
// Handle simple types
|
|
if val.Type().ConvertibleTo(fieldValue.Type()) {
|
|
fieldValue.Set(val.Convert(fieldValue.Type()))
|
|
} else {
|
|
|
|
newVal, converted := util.CastValueToKind(val.Interface(), fieldValue.Type().Kind())
|
|
if !converted {
|
|
return fmt.Errorf("cannot assign value of type %s to map element of type %s",
|
|
val.Type(), val)
|
|
}
|
|
|
|
fieldValue.Set(reflect.ValueOf(newVal))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func FillConfigFromDB(store Store) (err error) {
|
|
|
|
opts, err := store.GetOptions(RetrieveQueryParams{})
|
|
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
options := ConvertFlatToNested(opts)
|
|
|
|
if options["apps"] == nil {
|
|
options["apps"] = make(map[string]interface{})
|
|
}
|
|
|
|
err = AssignMapToStruct(options, util.Config)
|
|
|
|
return
|
|
}
|