mirror of
https://github.com/oarkflow/mq.git
synced 2025-10-09 06:50:09 +08:00
feat: implement service
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -24,3 +24,4 @@ go.work
|
|||||||
*.svg
|
*.svg
|
||||||
.qodo
|
.qodo
|
||||||
.history
|
.history
|
||||||
|
services/examples/config
|
||||||
|
@@ -67,6 +67,7 @@ func loginDAG() *dag.DAG {
|
|||||||
renderHTML := handlers.NewRenderHTMLNode("render-html")
|
renderHTML := handlers.NewRenderHTMLNode("render-html")
|
||||||
renderHTML.Payload.Data = map[string]any{
|
renderHTML.Payload.Data = map[string]any{
|
||||||
"schema_file": "login.json",
|
"schema_file": "login.json",
|
||||||
|
"template_file": "app/templates/basic.html",
|
||||||
}
|
}
|
||||||
flow.AddNode(dag.Page, "Login Form", "LoginForm", renderHTML, true)
|
flow.AddNode(dag.Page, "Login Form", "LoginForm", renderHTML, true)
|
||||||
flow.AddNode(dag.Function, "Validate Login", "ValidateLogin", &ValidateLoginNode{})
|
flow.AddNode(dag.Function, "Validate Login", "ValidateLogin", &ValidateLoginNode{})
|
||||||
@@ -88,6 +89,7 @@ func main() {
|
|||||||
renderHTML := handlers.NewRenderHTMLNode("render-html")
|
renderHTML := handlers.NewRenderHTMLNode("render-html")
|
||||||
renderHTML.Payload.Data = map[string]any{
|
renderHTML.Payload.Data = map[string]any{
|
||||||
"schema_file": "schema.json",
|
"schema_file": "schema.json",
|
||||||
|
"template_file": "app/templates/basic.html",
|
||||||
}
|
}
|
||||||
flow.AddDAGNode(dag.Page, "Check Login", "Login", loginDAG(), true)
|
flow.AddDAGNode(dag.Page, "Check Login", "Login", loginDAG(), true)
|
||||||
flow.AddNode(dag.Page, "Contact Form", "ContactForm", renderHTML)
|
flow.AddNode(dag.Page, "Contact Form", "ContactForm", renderHTML)
|
||||||
|
2
go.mod
2
go.mod
@@ -11,7 +11,7 @@ require (
|
|||||||
github.com/oarkflow/expr v0.0.11
|
github.com/oarkflow/expr v0.0.11
|
||||||
github.com/oarkflow/form v0.0.0-20241203111156-b1be5636af43
|
github.com/oarkflow/form v0.0.0-20241203111156-b1be5636af43
|
||||||
github.com/oarkflow/jet v0.0.4
|
github.com/oarkflow/jet v0.0.4
|
||||||
github.com/oarkflow/json v0.0.21
|
github.com/oarkflow/json v0.0.28
|
||||||
github.com/oarkflow/log v1.0.79
|
github.com/oarkflow/log v1.0.79
|
||||||
github.com/oarkflow/xid v1.2.8
|
github.com/oarkflow/xid v1.2.8
|
||||||
golang.org/x/crypto v0.33.0
|
golang.org/x/crypto v0.33.0
|
||||||
|
4
go.sum
4
go.sum
@@ -40,8 +40,8 @@ github.com/oarkflow/form v0.0.0-20241203111156-b1be5636af43 h1:AjNCAnpzDi6BYVUfX
|
|||||||
github.com/oarkflow/form v0.0.0-20241203111156-b1be5636af43/go.mod h1:fYwqhq8Sig9y0cmgO6q6WN8SP/rrsi7h2Yyk+Ufrne8=
|
github.com/oarkflow/form v0.0.0-20241203111156-b1be5636af43/go.mod h1:fYwqhq8Sig9y0cmgO6q6WN8SP/rrsi7h2Yyk+Ufrne8=
|
||||||
github.com/oarkflow/jet v0.0.4 h1:rs0nTzodye/9zhrSX7FlR80Gjaty6ei2Ln0pmaUrdwg=
|
github.com/oarkflow/jet v0.0.4 h1:rs0nTzodye/9zhrSX7FlR80Gjaty6ei2Ln0pmaUrdwg=
|
||||||
github.com/oarkflow/jet v0.0.4/go.mod h1:YXIc47aYyx1xKpnmuz1Z9o88cxxa47r7X3lfUAxZ0Qg=
|
github.com/oarkflow/jet v0.0.4/go.mod h1:YXIc47aYyx1xKpnmuz1Z9o88cxxa47r7X3lfUAxZ0Qg=
|
||||||
github.com/oarkflow/json v0.0.21 h1:tBx4ufwC48UAd3fUCqLVH/dERpnZ85Dgw5/h7H2HMoM=
|
github.com/oarkflow/json v0.0.28 h1:pCt7yezRDJeSdSu2OZ6Aai0F4J9qCwmPWRsCmfaH8Ds=
|
||||||
github.com/oarkflow/json v0.0.21/go.mod h1:maoLmQZJ/8pF1MugtpVqzHJ59dH1Z7xFSNkhl9BQjYo=
|
github.com/oarkflow/json v0.0.28/go.mod h1:E6Mg4LoY1PHCntfAegZmECc6Ux24sBpXJAu2lwZUe74=
|
||||||
github.com/oarkflow/jsonschema v0.0.4 h1:n5Sb7WVb7NNQzn/ei9++4VPqKXCPJhhsHeTGJkIuwmM=
|
github.com/oarkflow/jsonschema v0.0.4 h1:n5Sb7WVb7NNQzn/ei9++4VPqKXCPJhhsHeTGJkIuwmM=
|
||||||
github.com/oarkflow/jsonschema v0.0.4/go.mod h1:AxNG3Nk7KZxnnjRJlHLmS1wE9brtARu5caTFuicCtnA=
|
github.com/oarkflow/jsonschema v0.0.4/go.mod h1:AxNG3Nk7KZxnnjRJlHLmS1wE9brtARu5caTFuicCtnA=
|
||||||
github.com/oarkflow/log v1.0.79 h1:DxhtkBGG+pUu6cudSVw5g75FbKEQJkij5w7n5AEN00M=
|
github.com/oarkflow/log v1.0.79 h1:DxhtkBGG+pUu6cudSVw5g75FbKEQJkij5w7n5AEN00M=
|
||||||
|
439
services/dto.go
Normal file
439
services/dto.go
Normal file
@@ -0,0 +1,439 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/oarkflow/json"
|
||||||
|
|
||||||
|
"github.com/oarkflow/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type structValueMap = map[string]reflect.Value
|
||||||
|
|
||||||
|
// Marker type for functions with no receiver
|
||||||
|
type nilRecvT struct{}
|
||||||
|
|
||||||
|
var nilRecvRfType = reflect.TypeOf(nilRecvT{})
|
||||||
|
var errorRfType = reflect.TypeOf((*error)(nil)).Elem()
|
||||||
|
var mapperPtrRfType = reflect.TypeOf((*Mapper)(nil))
|
||||||
|
|
||||||
|
type convertFuncClosure = func(reflect.Value, *Mapper) (reflect.Value, error)
|
||||||
|
type inspectFuncClosure = func(reflect.Value, reflect.Value, *Mapper) error
|
||||||
|
|
||||||
|
const structTag = "dto"
|
||||||
|
|
||||||
|
// NoValidMappingError indicates that no valid mapping was found
|
||||||
|
type NoValidMappingError struct {
|
||||||
|
ToType reflect.Type
|
||||||
|
FromType reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nvme NoValidMappingError) Error() string {
|
||||||
|
return fmt.Sprintf("No valid mapping found for %v from %v", nvme.ToType, nvme.FromType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mapper contains conversion and inspect functions
|
||||||
|
type Mapper struct {
|
||||||
|
// linear search might be faster than nested maps
|
||||||
|
convFunc map[reflect.Type]map[reflect.Type]convertFuncClosure
|
||||||
|
postFunc map[reflect.Type]map[reflect.Type][]inspectFuncClosure
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================================== utils =================================
|
||||||
|
|
||||||
|
// Collect all struct fields (including anonymous) into a structValueMap
|
||||||
|
func collectStructFields(rfValue reflect.Value, rfType reflect.Type, fields structValueMap) {
|
||||||
|
for i := 0; i < rfType.NumField(); i++ {
|
||||||
|
fieldValue := rfValue.Field(i)
|
||||||
|
fieldType := rfType.Field(i)
|
||||||
|
if tags, ok := fieldType.Tag.Lookup(structTag); ok {
|
||||||
|
if strings.Contains(tags, "ignore") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fieldType.Anonymous {
|
||||||
|
collectStructFields(fieldValue, fieldType.Type, fields)
|
||||||
|
} else {
|
||||||
|
fields[fieldType.Name] = fieldValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return reflect.Value with pointer removed (first layer only)
|
||||||
|
func reflectValueRemovePtr(v any) reflect.Value {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
if rv.Type().Kind() == reflect.Ptr {
|
||||||
|
return rv.Elem()
|
||||||
|
}
|
||||||
|
return rv
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps an error from a reflect value
|
||||||
|
// Panics if the value is non nill and not an error
|
||||||
|
func errorFromReflectValue(rv reflect.Value) error {
|
||||||
|
if rv.IsNil() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err, ok := rv.Interface().(error)
|
||||||
|
if !ok {
|
||||||
|
panic("Failed to map error from reflect.Value")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================================== Conversion and inspection functions ===
|
||||||
|
|
||||||
|
// Run inspect functions for (dst-src) pair
|
||||||
|
func (m *Mapper) runInspectFuncs(dstRv, srcRv reflect.Value) error {
|
||||||
|
toMap, ok := m.postFunc[dstRv.Type()]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, recvType := range []reflect.Type{srcRv.Type(), nilRecvRfType} {
|
||||||
|
funcs, ok := toMap[recvType]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, fun := range funcs {
|
||||||
|
if err := fun(dstRv.Addr(), srcRv, m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run convert function for (dst-src) pair
|
||||||
|
// Returns (error, true) if a valid function was found, (nil, false) otherwise
|
||||||
|
func (m *Mapper) runConvFuncs(dstRv, srcRv reflect.Value) (bool, error) {
|
||||||
|
toMap, ok := m.convFunc[srcRv.Type()]
|
||||||
|
if !ok {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if convertFunc, ok := toMap[dstRv.Type()]; ok {
|
||||||
|
val, err := convertFunc(srcRv, m)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
dstRv.Set(val)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasCustomFuncs returns true if the Mapper has custom functions defined
|
||||||
|
func (m *Mapper) HasCustomFuncs() bool {
|
||||||
|
return len(m.convFunc)+len(m.postFunc) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddConvFunc adds a conversion function to the Mapper
|
||||||
|
//
|
||||||
|
// Panics if f is not a valid conversion function
|
||||||
|
// Overwrites previous functions with the same type pair
|
||||||
|
func (m *Mapper) AddConvFunc(f any) {
|
||||||
|
rt := reflect.TypeOf(f)
|
||||||
|
|
||||||
|
// check basic argument invariant
|
||||||
|
if rt.NumOut() < 1 || rt.NumIn() < 1 {
|
||||||
|
panic("Bad conversion function")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if to inject mapper
|
||||||
|
takesMapper := false
|
||||||
|
if rt.NumIn() > 1 && rt.In(1) == mapperPtrRfType {
|
||||||
|
takesMapper = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if returns an error
|
||||||
|
returnsError := false
|
||||||
|
outType := rt.Out(0)
|
||||||
|
if rt.NumOut() > 1 && rt.Out(1).Implements(errorRfType) {
|
||||||
|
returnsError = true
|
||||||
|
}
|
||||||
|
|
||||||
|
inType := rt.In(0)
|
||||||
|
|
||||||
|
// create maps
|
||||||
|
if len(m.convFunc) == 0 {
|
||||||
|
m.convFunc = make(map[reflect.Type]map[reflect.Type]convertFuncClosure)
|
||||||
|
}
|
||||||
|
if len(m.convFunc[inType]) == 0 {
|
||||||
|
m.convFunc[inType] = make(map[reflect.Type]convertFuncClosure)
|
||||||
|
}
|
||||||
|
|
||||||
|
// register closure
|
||||||
|
m.convFunc[inType][outType] = func(from reflect.Value, m *Mapper) (reflect.Value, error) {
|
||||||
|
args := []reflect.Value{from}
|
||||||
|
if takesMapper {
|
||||||
|
args = append(args, reflect.ValueOf(m))
|
||||||
|
}
|
||||||
|
out := reflect.ValueOf(f).Call(args)
|
||||||
|
if returnsError {
|
||||||
|
return out[0], errorFromReflectValue(out[1])
|
||||||
|
}
|
||||||
|
return out[0], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddInspectFunc adds an inspection function to the Mapper
|
||||||
|
//
|
||||||
|
// Panics if f is not a valid inspection function
|
||||||
|
func (m *Mapper) AddInspectFunc(f any) {
|
||||||
|
ft := reflect.TypeOf(f)
|
||||||
|
inType := ft.In(0).Elem()
|
||||||
|
|
||||||
|
// check if takes from
|
||||||
|
fromType := nilRecvRfType
|
||||||
|
if ft.NumIn() > 1 {
|
||||||
|
fromType = ft.In(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if takes mapper
|
||||||
|
takesMapper := false
|
||||||
|
if ft.NumIn() > 2 && ft.In(2) == reflect.TypeOf(m) {
|
||||||
|
takesMapper = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if returns error
|
||||||
|
returnsError := false
|
||||||
|
if ft.NumOut() > 0 && ft.Out(0).Implements(errorRfType) {
|
||||||
|
returnsError = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// create map path
|
||||||
|
if len(m.postFunc) == 0 {
|
||||||
|
m.postFunc = make(map[reflect.Type]map[reflect.Type][]inspectFuncClosure)
|
||||||
|
}
|
||||||
|
if len(m.postFunc[inType]) == 0 {
|
||||||
|
m.postFunc[inType] = make(map[reflect.Type][]inspectFuncClosure)
|
||||||
|
}
|
||||||
|
|
||||||
|
// register closure
|
||||||
|
m.postFunc[inType][fromType] = append(m.postFunc[inType][fromType],
|
||||||
|
func(v1, v2 reflect.Value, m *Mapper) error {
|
||||||
|
args := []reflect.Value{v1}
|
||||||
|
if fromType != nilRecvRfType {
|
||||||
|
args = append(args, v2)
|
||||||
|
}
|
||||||
|
if takesMapper {
|
||||||
|
args = append(args, reflect.ValueOf(m))
|
||||||
|
}
|
||||||
|
|
||||||
|
out := reflect.ValueOf(f).Call(args)
|
||||||
|
if returnsError {
|
||||||
|
return errorFromReflectValue(out[0])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================================== Mapping functions =====================
|
||||||
|
|
||||||
|
// Map slices
|
||||||
|
// Panics if arguments are not slices
|
||||||
|
func (m *Mapper) mapSlice(toRv, fromRv reflect.Value) error {
|
||||||
|
toRv.Set(reflect.MakeSlice(toRv.Type(), fromRv.Len(), fromRv.Len()))
|
||||||
|
for i := 0; i < fromRv.Len(); i++ {
|
||||||
|
if err := m.mapValue(toRv.Index(i), fromRv.Index(i)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map maps
|
||||||
|
// Panics if arguments are not maps
|
||||||
|
func (m *Mapper) mapMap(dstRv, srcRv reflect.Value) error {
|
||||||
|
dstRv.Set(reflect.MakeMapWithSize(dstRv.Type(), srcRv.Len()))
|
||||||
|
// Map values
|
||||||
|
mapIt := srcRv.MapRange()
|
||||||
|
for mapIt.Next() {
|
||||||
|
toKey := reflect.New(dstRv.Type().Key()).Elem()
|
||||||
|
toValue := reflect.New(dstRv.Type().Elem()).Elem()
|
||||||
|
if err := m.mapValue(toKey, mapIt.Key()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := m.mapValue(toValue, mapIt.Value()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dstRv.SetMapIndex(toKey, toValue)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map structs
|
||||||
|
// Panics if arguments are not structs
|
||||||
|
func (m *Mapper) mapStructs(dstRv, srcRv reflect.Value) error {
|
||||||
|
toFields := make(structValueMap)
|
||||||
|
collectStructFields(dstRv, dstRv.Type(), toFields)
|
||||||
|
|
||||||
|
fromFields := make(structValueMap)
|
||||||
|
collectStructFields(srcRv, srcRv.Type(), fromFields)
|
||||||
|
|
||||||
|
for fieldName, toValue := range toFields {
|
||||||
|
fromValue, ok := fromFields[fieldName]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := m.mapValue(toValue, fromValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map map values to slice
|
||||||
|
// Panics if arguments are not slice and map accordingly
|
||||||
|
func (m *Mapper) mapMapToSlice(dstRv, srcRv reflect.Value) error {
|
||||||
|
dstRv.Set(reflect.MakeSlice(dstRv.Type(), srcRv.Len(), srcRv.Len()))
|
||||||
|
i := 0
|
||||||
|
mapIt := srcRv.MapRange()
|
||||||
|
for mapIt.Next() {
|
||||||
|
if err := m.mapValue(dstRv.Index(i), mapIt.Value()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map a map of slices to slice
|
||||||
|
// Panics of arguments are not a map of slices and a slice accordingly
|
||||||
|
func (m *Mapper) mapMapSlicesToSlice(dstRv, srcRv reflect.Value) error {
|
||||||
|
// calculate length
|
||||||
|
sumLen := 0
|
||||||
|
mapIt := srcRv.MapRange()
|
||||||
|
for mapIt.Next() {
|
||||||
|
sumLen += mapIt.Value().Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
dstRv.Set(reflect.MakeSlice(dstRv.Type(), sumLen, sumLen))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
mapIt = srcRv.MapRange()
|
||||||
|
for mapIt.Next() {
|
||||||
|
mapSlice := mapIt.Value()
|
||||||
|
for j := 0; j < mapSlice.Len(); i, j = i+1, j+1 {
|
||||||
|
if err := m.mapValue(dstRv.Index(i), mapSlice.Index(j)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to map any value
|
||||||
|
func (m *Mapper) mapValue(dstRv, srcRv reflect.Value) (returnError error) {
|
||||||
|
tk, fk := dstRv.Type().Kind(), srcRv.Type().Kind()
|
||||||
|
|
||||||
|
// Defer inspect functions
|
||||||
|
defer func() {
|
||||||
|
if returnError != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
returnError = m.runInspectFuncs(dstRv, srcRv)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 1. Check conversion functions
|
||||||
|
converted, err := m.runConvFuncs(dstRv, srcRv)
|
||||||
|
if converted {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Check direct assignment
|
||||||
|
if srcRv.Type().AssignableTo(dstRv.Type()) {
|
||||||
|
dstRv.Set(srcRv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Check conversion
|
||||||
|
if srcRv.Type().ConvertibleTo(dstRv.Type()) {
|
||||||
|
dstRv.Set(srcRv.Convert(dstRv.Type()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Handle pointers by dereferencing from
|
||||||
|
if fk == reflect.Ptr {
|
||||||
|
// Skip null pointers
|
||||||
|
if srcRv.IsNil() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m.mapValue(dstRv, srcRv.Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Handle pointers by dereferencing to
|
||||||
|
if tk == reflect.Ptr {
|
||||||
|
// Allocate new value if nil
|
||||||
|
if dstRv.IsNil() {
|
||||||
|
dstRv.Set(reflect.New(dstRv.Type().Elem()))
|
||||||
|
}
|
||||||
|
return m.mapValue(dstRv.Elem(), srcRv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Handle sructs
|
||||||
|
if tk == reflect.Struct && fk == reflect.Struct {
|
||||||
|
return m.mapStructs(dstRv, srcRv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Handle slices
|
||||||
|
if tk == reflect.Slice && fk == reflect.Slice {
|
||||||
|
return m.mapSlice(dstRv, srcRv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Handle maps
|
||||||
|
if tk == reflect.Map && fk == reflect.Map {
|
||||||
|
return m.mapMap(dstRv, srcRv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. Handle map to slice
|
||||||
|
if tk == reflect.Slice && fk == reflect.Map {
|
||||||
|
err := m.mapMapToSlice(dstRv, srcRv)
|
||||||
|
|
||||||
|
// 9. Handle map of slices to slice
|
||||||
|
mapElemK := srcRv.Type().Elem().Kind()
|
||||||
|
if errors.As(err, &NoValidMappingError{}) && mapElemK == reflect.Slice {
|
||||||
|
// dont propagate errors
|
||||||
|
if errFlatten := m.mapMapSlicesToSlice(dstRv, srcRv); errFlatten == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NoValidMappingError{
|
||||||
|
ToType: dstRv.Type(),
|
||||||
|
FromType: srcRv.Type(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================================== Public helpers ========================
|
||||||
|
|
||||||
|
// Map transfers values from src to dst
|
||||||
|
func (m *Mapper) Map(dst, src any) error {
|
||||||
|
return m.mapValue(reflectValueRemovePtr(dst), reflectValueRemovePtr(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map transfers values from src to dst
|
||||||
|
func Map(dst, src any) error {
|
||||||
|
m := Mapper{}
|
||||||
|
err := m.Map(dst, src)
|
||||||
|
if err != nil {
|
||||||
|
return MapJson(dst, src)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapJson(dst, src any) error {
|
||||||
|
bt, err := json.Marshal(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.Unmarshal(bt, dst)
|
||||||
|
}
|
13
services/examples/main.go
Normal file
13
services/examples/main.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/oarkflow/mq/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
loader := services.NewLoader("config")
|
||||||
|
loader.Load()
|
||||||
|
fmt.Println(loader.UserConfig)
|
||||||
|
}
|
60
services/generic_command.go
Normal file
60
services/generic_command.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/oarkflow/cli/contracts"
|
||||||
|
"github.com/oarkflow/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Flag struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
Usage string `json:"usage"`
|
||||||
|
Aliases []string `json:"aliases"`
|
||||||
|
Required bool `json:"required"`
|
||||||
|
}
|
||||||
|
type GenericCommand struct {
|
||||||
|
handler func(ctx contracts.Context) error
|
||||||
|
Command string `json:"signature"`
|
||||||
|
Desc string `json:"description"`
|
||||||
|
Handler Handler `json:"handler"`
|
||||||
|
HandlerKey string `json:"handler_key"`
|
||||||
|
Flags []Flag `json:"flags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature The name and signature of the console command.
|
||||||
|
func (receiver *GenericCommand) Signature() string {
|
||||||
|
return receiver.Command
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description The console command description.
|
||||||
|
func (receiver *GenericCommand) Description() string {
|
||||||
|
return receiver.Desc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend The console command extend.
|
||||||
|
func (receiver *GenericCommand) Extend() contracts.Extend {
|
||||||
|
var flags []contracts.Flag
|
||||||
|
for _, flag := range receiver.Flags {
|
||||||
|
flags = append(flags, contracts.Flag{
|
||||||
|
Name: flag.Name,
|
||||||
|
Aliases: flag.Aliases,
|
||||||
|
Usage: flag.Usage,
|
||||||
|
Required: flag.Required,
|
||||||
|
Value: flag.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return contracts.Extend{Flags: flags}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Execute the console command.
|
||||||
|
func (receiver *GenericCommand) Handle(ctx contracts.Context) error {
|
||||||
|
if receiver.handler == nil {
|
||||||
|
return errors.New("Handler not found")
|
||||||
|
}
|
||||||
|
return receiver.handler(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHandler Execute the console command.
|
||||||
|
func (receiver *GenericCommand) SetHandler(handler func(ctx contracts.Context) error) {
|
||||||
|
receiver.handler = handler
|
||||||
|
}
|
67
services/go.mod
Normal file
67
services/go.mod
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
module github.com/oarkflow/mq/services
|
||||||
|
|
||||||
|
go 1.24.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/oarkflow/cli v0.0.0-20250313133305-8d14a63c1883
|
||||||
|
github.com/oarkflow/errors v0.0.6
|
||||||
|
github.com/oarkflow/filters v0.0.36
|
||||||
|
github.com/oarkflow/jenv v0.0.2
|
||||||
|
github.com/oarkflow/json v0.0.28
|
||||||
|
github.com/oarkflow/jsonschema v0.0.4
|
||||||
|
github.com/oarkflow/log v1.0.83
|
||||||
|
github.com/oarkflow/metadata v0.0.78
|
||||||
|
github.com/oarkflow/mq v0.0.17
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
|
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||||
|
github.com/bytedance/gopkg v0.1.1 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
|
github.com/goccy/go-reflect v1.2.0 // indirect
|
||||||
|
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||||
|
github.com/gofiber/fiber/v2 v2.52.6 // indirect
|
||||||
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||||
|
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
|
github.com/gotnospirit/makeplural v0.0.0-20180622080156-a5f48d94d976 // indirect
|
||||||
|
github.com/gotnospirit/messageformat v0.0.0-20221001023931-dfe49f1eb092 // indirect
|
||||||
|
github.com/hetiansu5/urlquery v1.2.7 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.7.2 // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
|
github.com/kaptinlin/go-i18n v0.1.4 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
|
github.com/microsoft/go-mssqldb v1.8.0 // indirect
|
||||||
|
github.com/oarkflow/convert v0.0.5 // indirect
|
||||||
|
github.com/oarkflow/date v0.0.4 // indirect
|
||||||
|
github.com/oarkflow/dipper v0.0.6 // indirect
|
||||||
|
github.com/oarkflow/expr v0.0.11 // indirect
|
||||||
|
github.com/oarkflow/form v0.0.0-20241203111156-b1be5636af43 // indirect
|
||||||
|
github.com/oarkflow/jet v0.0.4 // indirect
|
||||||
|
github.com/oarkflow/protocol v0.0.16 // indirect
|
||||||
|
github.com/oarkflow/render v0.0.1 // indirect
|
||||||
|
github.com/oarkflow/squealx v0.0.36 // indirect
|
||||||
|
github.com/oarkflow/xid v1.2.8 // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||||
|
github.com/toorop/go-dkim v0.0.0-20240103092955-90b7d1423f92 // indirect
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
github.com/valyala/fasthttp v1.59.0 // indirect
|
||||||
|
github.com/xhit/go-simple-mail/v2 v2.16.0 // indirect
|
||||||
|
golang.org/x/crypto v0.33.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
||||||
|
golang.org/x/sync v0.14.0 // indirect
|
||||||
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
|
golang.org/x/text v0.25.0 // indirect
|
||||||
|
golang.org/x/time v0.11.0 // indirect
|
||||||
|
)
|
162
services/go.sum
Normal file
162
services/go.sum
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
|
||||||
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
|
||||||
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||||
|
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||||
|
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||||
|
github.com/bytedance/gopkg v0.1.1 h1:3azzgSkiaw79u24a+w9arfH8OfnQQ4MHUt9lJFREEaE=
|
||||||
|
github.com/bytedance/gopkg v0.1.1/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
|
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
|
||||||
|
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||||
|
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||||
|
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
|
github.com/goccy/go-reflect v1.2.0 h1:O0T8rZCuNmGXewnATuKYnkL0xm6o8UNOJZd/gOkb9ms=
|
||||||
|
github.com/goccy/go-reflect v1.2.0/go.mod h1:n0oYZn8VcV2CkWTxi8B9QjkCoq6GTtCEdfmR66YhFtE=
|
||||||
|
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||||
|
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||||
|
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
|
||||||
|
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
|
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||||
|
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/gotnospirit/makeplural v0.0.0-20180622080156-a5f48d94d976 h1:b70jEaX2iaJSPZULSUxKtm73LBfsCrMsIlYCUgNGSIs=
|
||||||
|
github.com/gotnospirit/makeplural v0.0.0-20180622080156-a5f48d94d976/go.mod h1:ZGQeOwybjD8lkCjIyJfqR5LD2wMVHJ31d6GdPxoTsWY=
|
||||||
|
github.com/gotnospirit/messageformat v0.0.0-20221001023931-dfe49f1eb092 h1:c7gcNWTSr1gtLp6PyYi3wzvFCEcHJ4YRobDgqmIgf7Q=
|
||||||
|
github.com/gotnospirit/messageformat v0.0.0-20221001023931-dfe49f1eb092/go.mod h1:ZZAN4fkkful3l1lpJwF8JbW41ZiG9TwJ2ZlqzQovBNU=
|
||||||
|
github.com/hetiansu5/urlquery v1.2.7 h1:jn0h+9pIRqUziSPnRdK/gJK8S5TCnk+HZZx5fRHf8K0=
|
||||||
|
github.com/hetiansu5/urlquery v1.2.7/go.mod h1:wFpZdTHRdwt7mk0EM/DdZEWtEN4xf8HJoH/BLXm/PG0=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
|
||||||
|
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
github.com/kaptinlin/go-i18n v0.1.4 h1:wCiwAn1LOcvymvWIVAM4m5dUAMiHunTdEubLDk4hTGs=
|
||||||
|
github.com/kaptinlin/go-i18n v0.1.4/go.mod h1:g1fn1GvTgT4CiLE8/fFE1hboHWJ6erivrDpiDtCcFKg=
|
||||||
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||||
|
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/microsoft/go-mssqldb v1.8.0 h1:7cyZ/AT7ycDsEoWPIXibd+aVKFtteUNhDGf3aobP+tw=
|
||||||
|
github.com/microsoft/go-mssqldb v1.8.0/go.mod h1:6znkekS3T2vp0waiMhen4GPU1BiAsrP+iXHcE7a7rFo=
|
||||||
|
github.com/oarkflow/cli v0.0.0-20250313133305-8d14a63c1883 h1:N0Mroo3lyZTcVUyPAAO40mhgVicrweifMNnOrXQxt+A=
|
||||||
|
github.com/oarkflow/cli v0.0.0-20250313133305-8d14a63c1883/go.mod h1:8APSp4HJyCyh7R17XHQYSNz69N7+73S1z+/N8xhC6vo=
|
||||||
|
github.com/oarkflow/convert v0.0.5 h1:5s5DlnZLSUweB+EDUjynj0Eput7AkGUEQUvs/j4CWmM=
|
||||||
|
github.com/oarkflow/convert v0.0.5/go.mod h1:/N8EEJiKUCtYS//+UfS3IX3ej7K1wIPgW/1nBhbeEXs=
|
||||||
|
github.com/oarkflow/date v0.0.4 h1:EwY/wiS3CqZNBx7b2x+3kkJwVNuGk+G0dls76kL/fhU=
|
||||||
|
github.com/oarkflow/date v0.0.4/go.mod h1:xQTFc6p6O5VX6J75ZrPJbelIFGca1ASmhpgirFqL8vM=
|
||||||
|
github.com/oarkflow/dipper v0.0.6 h1:E+ak9i4R1lxx0B04CjfG5DTLTmwuWA1nrdS6KIHdUxQ=
|
||||||
|
github.com/oarkflow/dipper v0.0.6/go.mod h1:bnXQ6465eP8WZ9U3M7R24zeBG3P6IU5SASuvpAyCD9w=
|
||||||
|
github.com/oarkflow/errors v0.0.6 h1:qTBzVblrX6bFbqYLfatsrZHMBPchOZiIE3pfVzh1+k8=
|
||||||
|
github.com/oarkflow/errors v0.0.6/go.mod h1:UETn0Q55PJ+YUbpR4QImIoBavd6QvJtyW/oeTT7ghZM=
|
||||||
|
github.com/oarkflow/expr v0.0.11 h1:H6h+dIUlU+xDlijMXKQCh7TdE6MGVoFPpZU7q/dziRI=
|
||||||
|
github.com/oarkflow/expr v0.0.11/go.mod h1:WgMZqP44h7SBwKyuGZwC15vj46lHtI0/QpKdEZpRVE4=
|
||||||
|
github.com/oarkflow/filters v0.0.36 h1:7jVfQ/CBOc9+KKa8IOsKjGvPMgNpfkObS7DQAKpcImQ=
|
||||||
|
github.com/oarkflow/filters v0.0.36/go.mod h1:aNd+dCtqa6kjhMJgzkMkT7oRE/JkwMpR5vq0dSsDHpY=
|
||||||
|
github.com/oarkflow/form v0.0.0-20241203111156-b1be5636af43 h1:AjNCAnpzDi6BYVUfXUUuIdWruRu4npSSTrR3eZ6Vppw=
|
||||||
|
github.com/oarkflow/form v0.0.0-20241203111156-b1be5636af43/go.mod h1:fYwqhq8Sig9y0cmgO6q6WN8SP/rrsi7h2Yyk+Ufrne8=
|
||||||
|
github.com/oarkflow/jenv v0.0.2 h1:NrzvRkauJ4UDXDiyY6TorNv1fcEMf5J8HZNCtVSr4zo=
|
||||||
|
github.com/oarkflow/jenv v0.0.2/go.mod h1:jJS3oDWYtc8TbLGRiRC4f60P0jzDXvPIvqS5yTiwvSg=
|
||||||
|
github.com/oarkflow/jet v0.0.4 h1:rs0nTzodye/9zhrSX7FlR80Gjaty6ei2Ln0pmaUrdwg=
|
||||||
|
github.com/oarkflow/jet v0.0.4/go.mod h1:YXIc47aYyx1xKpnmuz1Z9o88cxxa47r7X3lfUAxZ0Qg=
|
||||||
|
github.com/oarkflow/json v0.0.28 h1:pCt7yezRDJeSdSu2OZ6Aai0F4J9qCwmPWRsCmfaH8Ds=
|
||||||
|
github.com/oarkflow/json v0.0.28/go.mod h1:E6Mg4LoY1PHCntfAegZmECc6Ux24sBpXJAu2lwZUe74=
|
||||||
|
github.com/oarkflow/jsonschema v0.0.4 h1:n5Sb7WVb7NNQzn/ei9++4VPqKXCPJhhsHeTGJkIuwmM=
|
||||||
|
github.com/oarkflow/jsonschema v0.0.4/go.mod h1:AxNG3Nk7KZxnnjRJlHLmS1wE9brtARu5caTFuicCtnA=
|
||||||
|
github.com/oarkflow/log v1.0.83 h1:T/38wvjuNeVJ9PDo0wJDTnTUQZ5XeqlcvpbCItuFFJo=
|
||||||
|
github.com/oarkflow/log v1.0.83/go.mod h1:dMn57z9uq11Y264cx9c9Ac7ska9qM+EBhn4qf9CNlsM=
|
||||||
|
github.com/oarkflow/metadata v0.0.78 h1:ciKbtzQGXYvSlxaFYtDX1CocCkchHskreAldVIkHIMg=
|
||||||
|
github.com/oarkflow/metadata v0.0.78/go.mod h1:T6Bcsq2FVjrJYMJpMluQTw+/xkqUwax7m/qGHTDCyaw=
|
||||||
|
github.com/oarkflow/mq v0.0.17 h1:krNZW4Gi3CO90HYhAhsskVhNoObWhGjmsMLqcTuNjLQ=
|
||||||
|
github.com/oarkflow/mq v0.0.17/go.mod h1:nD3C1f4qniuGKl6pmp+BrzKcjYOZ8d+gmEUkDSOrG0Y=
|
||||||
|
github.com/oarkflow/protocol v0.0.16 h1:3qNn9gwoJOpdz+owyAmW4fNMpQplqHVIjzsWM4r0pcA=
|
||||||
|
github.com/oarkflow/protocol v0.0.16/go.mod h1:iKP/I+3/FIWlZ6OphAo8c60JO2qgwethOMR+NMsMI28=
|
||||||
|
github.com/oarkflow/render v0.0.1 h1:Caw74Yu8OE/tjCjurhbUkS0Fi9zE/mzVvQa1Cw7m7R4=
|
||||||
|
github.com/oarkflow/render v0.0.1/go.mod h1:nnRhxhKn9NCPtTfbsaLuyCt86Iv9hMbNPDFQoPucQYI=
|
||||||
|
github.com/oarkflow/squealx v0.0.36 h1:lEyvHOd+A2pxogyRvMdGAM8hw5sLY4SztdLcx+Rzer8=
|
||||||
|
github.com/oarkflow/squealx v0.0.36/go.mod h1:lXhv4tBTxRVAZfqft9En1V8LdEy42778AjjTiN04ctg=
|
||||||
|
github.com/oarkflow/xid v1.2.8 h1:uCIX61Binq2RPMsqImZM6pPGzoZTmRyD6jguxF9aAA0=
|
||||||
|
github.com/oarkflow/xid v1.2.8/go.mod h1:jG4YBh+swbjlWApGWDBYnsJEa7hi3CCpmuqhB3RAxVo=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||||
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns=
|
||||||
|
github.com/toorop/go-dkim v0.0.0-20240103092955-90b7d1423f92 h1:flbMkdl6HxQkLs6DDhH1UkcnFpNBOu70391STjMS0O4=
|
||||||
|
github.com/toorop/go-dkim v0.0.0-20240103092955-90b7d1423f92/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI=
|
||||||
|
github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU=
|
||||||
|
github.com/xhit/go-simple-mail/v2 v2.16.0 h1:ouGy/Ww4kuaqu2E2UrDw7SvLaziWTB60ICLkIkNVccA=
|
||||||
|
github.com/xhit/go-simple-mail/v2 v2.16.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98=
|
||||||
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
|
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||||
|
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||||
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
|
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||||
|
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||||
|
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||||
|
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||||
|
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
460
services/loader.go
Normal file
460
services/loader.go
Normal file
@@ -0,0 +1,460 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/oarkflow/filters"
|
||||||
|
"github.com/oarkflow/jenv"
|
||||||
|
"github.com/oarkflow/json"
|
||||||
|
"github.com/oarkflow/metadata"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Loader struct {
|
||||||
|
path string
|
||||||
|
configFile string
|
||||||
|
ParsedPath string
|
||||||
|
UserConfig *UserConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoader(path string, configFiles ...string) *Loader {
|
||||||
|
var configFile string
|
||||||
|
if len(configFiles) > 0 {
|
||||||
|
configFile = configFiles[0]
|
||||||
|
}
|
||||||
|
return &Loader{
|
||||||
|
path: path,
|
||||||
|
configFile: configFile,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader) Prefix() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader) Load() {
|
||||||
|
l.ParsedPath = l.prepareConfigPath()
|
||||||
|
cfg, err := l.loadConfig()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
l.UserConfig = cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader) prepareConfigPath() string {
|
||||||
|
path := l.path
|
||||||
|
if !filepath.IsAbs(path) {
|
||||||
|
b, err := filepath.Abs(path)
|
||||||
|
if err == nil {
|
||||||
|
path = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader) loadConfig() (*UserConfig, error) {
|
||||||
|
cfg := &UserConfig{}
|
||||||
|
configFile := l.configFile
|
||||||
|
if configFile != "" {
|
||||||
|
err := readFile(configFile, cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
initializeConfig(cfg)
|
||||||
|
}
|
||||||
|
if l.ParsedPath != "" {
|
||||||
|
err := readPath(l.ParsedPath, cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPath(path string, cfg *UserConfig) error {
|
||||||
|
readers := []func(string, *UserConfig) error{
|
||||||
|
readConfig, readCredentials, readConditions, readHandlers,
|
||||||
|
readModels, readApplicationRules, readCommands, readBackgroundTasks,
|
||||||
|
readWeb, readRenderer, readApis,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, read := range readers {
|
||||||
|
if err := read(path, cfg); err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readFile(path string, cfg any) error {
|
||||||
|
content, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return unmarshalConfig(content, path, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalConfig(data []byte, path string, cfg any) error {
|
||||||
|
ext := filepath.Ext(path)
|
||||||
|
switch ext {
|
||||||
|
case ".json":
|
||||||
|
return jenv.UnmarshalJSON(data, cfg)
|
||||||
|
case ".yaml", ".yml":
|
||||||
|
return jenv.UnmarshalYAML(data, cfg)
|
||||||
|
default:
|
||||||
|
return errors.New("unsupported file format. Only yaml and json supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeConfig(cfg *UserConfig) {
|
||||||
|
// Initialize Application Rules
|
||||||
|
for i, applicationRule := range cfg.Policy.ApplicationRules {
|
||||||
|
if applicationRule.Rule != nil {
|
||||||
|
applicationRule.BuildRuleFromRequest(cfg.GetCondition)
|
||||||
|
cfg.Policy.ApplicationRules[i] = applicationRule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Initialize Background Handlers
|
||||||
|
for i, command := range cfg.Policy.BackgroundHandlers {
|
||||||
|
if command.HandlerKey != "" {
|
||||||
|
if handler := cfg.GetHandler(command.HandlerKey); handler != nil {
|
||||||
|
command.Handler = *handler
|
||||||
|
cfg.Policy.BackgroundHandlers[i] = command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Initialize API Handlers
|
||||||
|
for i, api := range cfg.Policy.Web.Apis {
|
||||||
|
for j, route := range api.Routes {
|
||||||
|
if route.HandlerKey != "" {
|
||||||
|
if handler := cfg.GetHandler(route.HandlerKey); handler != nil {
|
||||||
|
route.Handler = *handler
|
||||||
|
api.Routes[j] = route
|
||||||
|
cfg.Policy.Web.Apis[i] = api
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to read either JSON or YAML config files
|
||||||
|
func readConfigFile[T any](path string, appendFn func(T)) error {
|
||||||
|
var out T
|
||||||
|
var err error
|
||||||
|
if content, readErr := os.ReadFile(path + ".json"); readErr == nil {
|
||||||
|
err = json.Unmarshal(content, &out)
|
||||||
|
} else if content, readErr := os.ReadFile(path + ".yaml"); readErr == nil {
|
||||||
|
err = yaml.Unmarshal(content, &out)
|
||||||
|
} else {
|
||||||
|
err = readErr // Set to the most recent read error if both files are missing
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
appendFn(out)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalContent[T any](content []byte, dataType string, appendFn func(T)) error {
|
||||||
|
var out T
|
||||||
|
var err error
|
||||||
|
switch dataType {
|
||||||
|
case "json":
|
||||||
|
err = jenv.UnmarshalJSON(content, &out)
|
||||||
|
case "yaml":
|
||||||
|
err = jenv.UnmarshalYAML(content, &out)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
appendFn(out)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to read either JSON or YAML config files
|
||||||
|
func readArrayConfigFile[T any](path string, appendFn func(T)) error {
|
||||||
|
var err error
|
||||||
|
var data []json.RawMessage
|
||||||
|
var dataType string
|
||||||
|
if content, readErr := os.ReadFile(path + ".json"); readErr == nil {
|
||||||
|
dataType = "json"
|
||||||
|
err = json.Unmarshal(content, &data)
|
||||||
|
} else if content, readErr := os.ReadFile(path + ".yaml"); readErr == nil {
|
||||||
|
dataType = "yaml"
|
||||||
|
err = yaml.Unmarshal(content, &data)
|
||||||
|
} else {
|
||||||
|
err = readErr // Set to the most recent read error if both files are missing
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, d := range data {
|
||||||
|
err = unmarshalContent(d, dataType, appendFn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample read function with YAML/JSON support
|
||||||
|
func readConditions(path string, cfg *UserConfig) error {
|
||||||
|
path = filepath.Join(path, "policies", "conditions")
|
||||||
|
return readConfigFile(path, func(data []*filters.Filter) {
|
||||||
|
cfg.Policy.Conditions = append(cfg.Policy.Conditions, data...)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func readApplicationRules(path string, cfg *UserConfig) error {
|
||||||
|
modelsPath := filepath.Join(path, "policies", "application_rules")
|
||||||
|
entries, err := os.ReadDir(modelsPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() && isSupportedExt(filepath.Ext(entry.Name())) {
|
||||||
|
file := filepath.Join(modelsPath, entry.Name())
|
||||||
|
var applicationRule *filters.ApplicationRule
|
||||||
|
content, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = unmarshalConfig(content, file, &applicationRule)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if applicationRule.Rule != nil {
|
||||||
|
applicationRule.BuildRuleFromRequest(cfg.GetCondition)
|
||||||
|
cfg.Policy.ApplicationRules = append(cfg.Policy.ApplicationRules, applicationRule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCommands(path string, cfg *UserConfig) error {
|
||||||
|
modelsPath := filepath.Join(path, "policies", "commands")
|
||||||
|
entries, err := os.ReadDir(modelsPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() && isSupportedExt(filepath.Ext(entry.Name())) {
|
||||||
|
file := filepath.Join(modelsPath, entry.Name())
|
||||||
|
var command GenericCommand
|
||||||
|
content, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = unmarshalConfig(content, file, &command)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if command.HandlerKey != "" {
|
||||||
|
if handler := cfg.GetHandler(command.HandlerKey); handler != nil {
|
||||||
|
command.Handler = *handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cfg.Policy.Commands = append(cfg.Policy.Commands, &command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readDatabases(path string, cfg *UserConfig) error {
|
||||||
|
path = filepath.Join(path, "credentials", "databases")
|
||||||
|
return readArrayConfigFile(path, func(data metadata.Config) {
|
||||||
|
cfg.Core.Credentials.Databases = append(cfg.Core.Credentials.Databases, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func readStorages(path string, cfg *UserConfig) error {
|
||||||
|
path = filepath.Join(path, "credentials", "storages")
|
||||||
|
return readArrayConfigFile(path, func(data Storage) {
|
||||||
|
cfg.Core.Credentials.Storages = append(cfg.Core.Credentials.Storages, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCaches(path string, cfg *UserConfig) error {
|
||||||
|
path = filepath.Join(path, "credentials", "caches")
|
||||||
|
return readArrayConfigFile(path, func(data Cache) {
|
||||||
|
cfg.Core.Credentials.Caches = append(cfg.Core.Credentials.Caches, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCredentials(path string, cfg *UserConfig) error {
|
||||||
|
if err := readDatabases(path, cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := readStorages(path, cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return readCaches(path, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readConfig(path string, cfg *UserConfig) error {
|
||||||
|
path = filepath.Join(path, "conf")
|
||||||
|
return readConfigFile(path, func(coreData Core) {
|
||||||
|
if cfg.Core.Enums == nil {
|
||||||
|
cfg.Core.Enums = make(map[string]map[string]any)
|
||||||
|
}
|
||||||
|
for key, val := range coreData.Enums {
|
||||||
|
cfg.Core.Enums[key] = val
|
||||||
|
}
|
||||||
|
if cfg.Core.Consts == nil {
|
||||||
|
cfg.Core.Consts = make(map[string]any)
|
||||||
|
}
|
||||||
|
for key, val := range coreData.Consts {
|
||||||
|
cfg.Core.Consts[key] = val
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func readModels(path string, cfg *UserConfig) error {
|
||||||
|
modelsPath := filepath.Join(path, "policies", "models")
|
||||||
|
entries, err := os.ReadDir(modelsPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() && isSupportedExt(filepath.Ext(entry.Name())) {
|
||||||
|
file := filepath.Join(modelsPath, entry.Name())
|
||||||
|
var model Model
|
||||||
|
content, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = unmarshalConfig(content, file, &model)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fileName := strings.TrimSuffix(entry.Name(), filepath.Ext(entry.Name()))
|
||||||
|
if model.Name == "" {
|
||||||
|
model.Name = fileName
|
||||||
|
}
|
||||||
|
if cfg.GetModel(model.Name) == nil {
|
||||||
|
cfg.Policy.Models = append(cfg.Policy.Models, model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHandlers(path string, cfg *UserConfig) error {
|
||||||
|
modelsPath := filepath.Join(path, "policies", "handlers")
|
||||||
|
entries, err := os.ReadDir(modelsPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() && isSupportedExt(filepath.Ext(entry.Name())) {
|
||||||
|
file := filepath.Join(modelsPath, entry.Name())
|
||||||
|
var handler Handler
|
||||||
|
content, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = unmarshalConfig(content, file, &handler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfg.Policy.Handlers = append(cfg.Policy.Handlers, handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readApis(path string, cfg *UserConfig) error {
|
||||||
|
modelsPath := filepath.Join(path, "policies", "apis")
|
||||||
|
entries, err := os.ReadDir(modelsPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() && isSupportedExt(filepath.Ext(entry.Name())) {
|
||||||
|
file := filepath.Join(modelsPath, entry.Name())
|
||||||
|
var api Api
|
||||||
|
content, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = unmarshalConfig(content, file, &api)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i, route := range api.Routes {
|
||||||
|
if route.HandlerKey != "" {
|
||||||
|
if handler := cfg.GetHandler(route.HandlerKey); handler != nil {
|
||||||
|
route.Handler = *handler
|
||||||
|
api.Routes[i] = route
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cfg.Policy.Web.Apis = append(cfg.Policy.Web.Apis, api)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readBackgroundTasks(path string, cfg *UserConfig) error {
|
||||||
|
modelsPath := filepath.Join(path, "policies", "background")
|
||||||
|
entries, err := os.ReadDir(modelsPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() && isSupportedExt(filepath.Ext(entry.Name())) {
|
||||||
|
file := filepath.Join(modelsPath, entry.Name())
|
||||||
|
var background BackgroundHandler
|
||||||
|
content, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = unmarshalConfig(content, file, &background)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfg.Policy.BackgroundHandlers = append(cfg.Policy.BackgroundHandlers, &background)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readRenderer(path string, cfg *UserConfig) error {
|
||||||
|
modelsPath := filepath.Join(path, "policies", "renderer")
|
||||||
|
entries, err := os.ReadDir(modelsPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() && isSupportedExt(filepath.Ext(entry.Name())) {
|
||||||
|
file := filepath.Join(modelsPath, entry.Name())
|
||||||
|
var background RenderConfig
|
||||||
|
content, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = unmarshalConfig(content, file, &background)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfg.Policy.Web.Render = append(cfg.Policy.Web.Render, &background)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readWeb(path string, cfg *UserConfig) error {
|
||||||
|
path = filepath.Join(path, "policies", "web")
|
||||||
|
return readConfigFile(path, func(data Web) {
|
||||||
|
cfg.Policy.Web = data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSupportedExt(ext string) bool {
|
||||||
|
return slices.Contains([]string{".json", ".yaml"}, ext)
|
||||||
|
}
|
118
services/setup.go
Normal file
118
services/setup.go
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/oarkflow/filters"
|
||||||
|
"github.com/oarkflow/log"
|
||||||
|
"github.com/oarkflow/mq"
|
||||||
|
"github.com/oarkflow/mq/dag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupHandler(handler Handler, brokerAddr string, async ...bool) *dag.DAG {
|
||||||
|
syncMode := true
|
||||||
|
if len(async) > 0 {
|
||||||
|
syncMode = async[0]
|
||||||
|
}
|
||||||
|
key := fmt.Sprintf(`%s-%v`, handler.Key, syncMode)
|
||||||
|
existingDAG := dag.GetDAG(key)
|
||||||
|
if existingDAG != nil {
|
||||||
|
return existingDAG
|
||||||
|
}
|
||||||
|
flow := dag.NewDAG(handler.Name, handler.Key, nil, mq.WithSyncMode(syncMode), mq.WithBrokerURL(brokerAddr))
|
||||||
|
for _, node := range handler.Nodes {
|
||||||
|
err := prepareNode(flow, node)
|
||||||
|
if err != nil {
|
||||||
|
flow.Error = err
|
||||||
|
return flow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, edge := range handler.Edges {
|
||||||
|
if edge.Label == "" {
|
||||||
|
edge.Label = fmt.Sprintf("edge-%s", edge.Source)
|
||||||
|
}
|
||||||
|
flow.AddEdge(dag.Simple, edge.Label, edge.Source, edge.Target...)
|
||||||
|
if flow.Error != nil {
|
||||||
|
return flow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, edge := range handler.Loops {
|
||||||
|
if edge.Label == "" {
|
||||||
|
edge.Label = fmt.Sprintf("loop-%s", edge.Source)
|
||||||
|
}
|
||||||
|
flow.AddEdge(dag.Iterator, edge.Label, edge.Source, edge.Target...)
|
||||||
|
}
|
||||||
|
err := flow.Validate()
|
||||||
|
if err != nil {
|
||||||
|
flow.Error = err
|
||||||
|
}
|
||||||
|
dag.AddDAG(key, flow)
|
||||||
|
return flow
|
||||||
|
}
|
||||||
|
|
||||||
|
type Filter struct {
|
||||||
|
Filter *filters.Filter `json:"condition"`
|
||||||
|
Node string `json:"node"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareNode(flow *dag.DAG, node Node) error {
|
||||||
|
newHandler := dag.GetHandler(node.Node)
|
||||||
|
if newHandler == nil {
|
||||||
|
return errors.New("Handler not found " + node.Node)
|
||||||
|
}
|
||||||
|
nodeHandler := newHandler(node.ID)
|
||||||
|
providers := mapProviders(node.Data.Providers)
|
||||||
|
switch nodeHandler := nodeHandler.(type) {
|
||||||
|
case dag.ConditionProcessor:
|
||||||
|
nodeHandler.SetConfig(dag.Payload{
|
||||||
|
Mapping: node.Data.Mapping,
|
||||||
|
Data: node.Data.AdditionalData,
|
||||||
|
GeneratedFields: node.Data.GeneratedFields,
|
||||||
|
Providers: providers,
|
||||||
|
})
|
||||||
|
if s, ok := node.Data.AdditionalData["conditions"]; ok {
|
||||||
|
var fil map[string]*Filter
|
||||||
|
err := Map(&fil, s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
condition := make(map[string]string)
|
||||||
|
conditions := make(map[string]dag.Condition)
|
||||||
|
for key, cond := range fil {
|
||||||
|
condition[key] = cond.Node
|
||||||
|
conditions[key] = cond.Filter
|
||||||
|
}
|
||||||
|
flow.AddCondition(node.ID, condition)
|
||||||
|
nodeHandler.SetConditions(conditions)
|
||||||
|
}
|
||||||
|
case dag.Processor:
|
||||||
|
nodeHandler.SetConfig(dag.Payload{
|
||||||
|
Mapping: node.Data.Mapping,
|
||||||
|
Data: node.Data.AdditionalData,
|
||||||
|
GeneratedFields: node.Data.GeneratedFields,
|
||||||
|
Providers: providers,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
var nodeType dag.NodeType
|
||||||
|
if nodeHandler.GetType() == "Function" {
|
||||||
|
nodeType = dag.Function
|
||||||
|
} else if nodeHandler.GetType() == "Page" {
|
||||||
|
nodeType = dag.Page
|
||||||
|
}
|
||||||
|
if node.Name == "" {
|
||||||
|
node.Name = node.ID
|
||||||
|
}
|
||||||
|
flow.AddNode(nodeType, node.Name, node.ID, nodeHandler, node.FirstNode)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapProviders(dataProviders interface{}) []dag.Provider {
|
||||||
|
var providers []dag.Provider
|
||||||
|
err := Map(&providers, dataProviders)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("Unable to map providers")
|
||||||
|
}
|
||||||
|
return providers
|
||||||
|
}
|
430
services/user_config.go
Normal file
430
services/user_config.go
Normal file
@@ -0,0 +1,430 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/oarkflow/json"
|
||||||
|
v2 "github.com/oarkflow/jsonschema"
|
||||||
|
|
||||||
|
"github.com/oarkflow/filters"
|
||||||
|
|
||||||
|
"github.com/oarkflow/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Storage struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Key string `json:"key" yaml:"key"`
|
||||||
|
Driver string `json:"driver" yaml:"driver"`
|
||||||
|
Database string `json:"database" yaml:"database"`
|
||||||
|
Host string `json:"host" yaml:"host"`
|
||||||
|
Username string `json:"username" yaml:"username"`
|
||||||
|
Password string `json:"password" yaml:"password"`
|
||||||
|
Port int `json:"port" yaml:"port"`
|
||||||
|
IndexCache int `json:"index_cache" yaml:"index_cache"`
|
||||||
|
BlockCache int `json:"block_cache" yaml:"block_cache"`
|
||||||
|
InMemory bool `json:"in_memory" yaml:"in_memory"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Key string `json:"key" yaml:"key"`
|
||||||
|
Driver string `json:"driver" yaml:"driver"`
|
||||||
|
Database string `json:"database" yaml:"database"`
|
||||||
|
Host string `json:"host" yaml:"host"`
|
||||||
|
Username string `json:"username" yaml:"username"`
|
||||||
|
Password string `json:"password" yaml:"password"`
|
||||||
|
Prefix string `json:"prefix" yaml:"prefix"`
|
||||||
|
Port int `json:"port" yaml:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Credentials struct {
|
||||||
|
Databases []metadata.Config `json:"databases" yaml:"databases"`
|
||||||
|
Storages []Storage `json:"storages" yaml:"storages"`
|
||||||
|
Caches []Cache `json:"caches" yaml:"caches"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Core struct {
|
||||||
|
Consts map[string]any `json:"consts" yaml:"consts"`
|
||||||
|
Enums map[string]map[string]any `json:"enums" yaml:"enums"`
|
||||||
|
Credentials Credentials `json:"credentials" yaml:"credentials"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Operation struct {
|
||||||
|
Role string `json:"role" yaml:"role"`
|
||||||
|
Actions []string `json:"actions" yaml:"actions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Constraint struct {
|
||||||
|
Indices []metadata.Indices `json:"indices" yaml:"indices"`
|
||||||
|
ForeignKeys []metadata.ForeignKey `json:"foreign" yaml:"foreign"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query struct {
|
||||||
|
File string `json:"file" yaml:"file"`
|
||||||
|
String string `json:"string" yaml:"string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Model struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
OldName string `json:"old_name" yaml:"old_name"`
|
||||||
|
Key string `json:"key" yaml:"key"`
|
||||||
|
Title string `json:"title" yaml:"title"`
|
||||||
|
Database string `json:"database" yaml:"database"`
|
||||||
|
ModelType string `json:"model_type" yaml:"model_type"`
|
||||||
|
StoreFields []string `json:"store_fields" yaml:"store_fields"`
|
||||||
|
IndexFields []string `json:"index_fields" yaml:"index_fields"`
|
||||||
|
Query Query `json:"query" yaml:"query"`
|
||||||
|
Constraints Constraint `json:"constraints" yaml:"constraints"`
|
||||||
|
Fields []metadata.Field `json:"fields" yaml:"fields"`
|
||||||
|
Operations []Operation `json:"operations" yaml:"operations"`
|
||||||
|
Fulltext bool `json:"fulltext" yaml:"fulltext"`
|
||||||
|
Files map[string]bool `json:"files" yaml:"files"`
|
||||||
|
SortField string `json:"sort_field" yaml:"sort_field"`
|
||||||
|
SortOrder string `json:"sort_order" yaml:"sort_order"`
|
||||||
|
RestApi bool `json:"rest_api" yaml:"rest_api"`
|
||||||
|
Update bool `json:"update" yaml:"update"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Property struct {
|
||||||
|
Properties map[string]Property `json:"properties,omitempty" yaml:"properties,omitempty"`
|
||||||
|
Items *RequestSchema `json:"items,omitempty" yaml:"items,omitempty"`
|
||||||
|
Type any `json:"type,omitempty" yaml:"type,omitempty"`
|
||||||
|
Default any `json:"default" yaml:"default"`
|
||||||
|
In string `json:"in,omitempty" yaml:"in,omitempty"`
|
||||||
|
AdditionalProperties bool `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Property) UnmarshalJSON(data []byte) error {
|
||||||
|
type T Property
|
||||||
|
var pr T
|
||||||
|
err := json.Unmarshal(data, &pr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.Properties = pr.Properties
|
||||||
|
p.Items = pr.Items
|
||||||
|
p.In = pr.In
|
||||||
|
p.AdditionalProperties = pr.AdditionalProperties
|
||||||
|
switch pr.Type.(type) {
|
||||||
|
case string:
|
||||||
|
p.Type = pr.Type.(string)
|
||||||
|
case []any:
|
||||||
|
var v []string
|
||||||
|
for _, i := range pr.Type.([]any) {
|
||||||
|
v = append(v, i.(string))
|
||||||
|
}
|
||||||
|
p.Type = v
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Provider struct {
|
||||||
|
Mapping map[string]any `json:"mapping,omitempty" yaml:"mapping,omitempty"`
|
||||||
|
UpdateMapping map[string]any `json:"update_mapping,omitempty" yaml:"update_mapping,omitempty"`
|
||||||
|
InsertMapping map[string]any `json:"insert_mapping,omitempty" yaml:"insert_mapping,omitempty"`
|
||||||
|
Defaults map[string]any `json:"defaults,omitempty" yaml:"defaults,omitempty"`
|
||||||
|
ProviderType string `json:"provider_type,omitempty" yaml:"provider_type,omitempty"`
|
||||||
|
Database string `json:"database,omitempty" yaml:"database,omitempty"`
|
||||||
|
Source string `json:"source,omitempty" yaml:"source,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Data struct {
|
||||||
|
Mapping map[string]string `json:"mapping,omitempty" yaml:"mapping,omitempty"`
|
||||||
|
AdditionalData map[string]any `json:"additional_data,omitempty" yaml:"additional_data,omitempty"`
|
||||||
|
GeneratedFields []string `json:"generated_fields,omitempty" yaml:"generated_fields,omitempty"`
|
||||||
|
Providers []Provider `json:"providers,omitempty" yaml:"providers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Node struct {
|
||||||
|
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||||
|
ID string `json:"id" yaml:"id"`
|
||||||
|
Node string `json:"node" yaml:"node"`
|
||||||
|
Data Data `json:"data" yaml:"data"`
|
||||||
|
FirstNode bool `json:"first_node" yaml:"first_node"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Loop struct {
|
||||||
|
Label string `json:"label" yaml:"label"`
|
||||||
|
Source string `json:"source" yaml:"source"`
|
||||||
|
Target []string `json:"target" yaml:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Edge struct {
|
||||||
|
Label string `json:"label" yaml:"label"`
|
||||||
|
Source string `json:"source" yaml:"source"`
|
||||||
|
Target []string `json:"target" yaml:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Branch struct {
|
||||||
|
ConditionalNodes map[string]string `json:"conditional_nodes" yaml:"conditional_nodes"`
|
||||||
|
Key string `json:"key" yaml:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Key string `json:"key" yaml:"key"`
|
||||||
|
Nodes []Node `json:"nodes,omitempty" yaml:"nodes,omitempty"`
|
||||||
|
Edges []Edge `json:"edges,omitempty" yaml:"edges,omitempty"`
|
||||||
|
Branches []Branch `json:"branches,omitempty" yaml:"branches,omitempty"`
|
||||||
|
Loops []Loop `json:"loops,omitempty" yaml:"loops,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestSchema struct {
|
||||||
|
Properties map[string]Property `json:"properties,omitempty" yaml:"properties,omitempty"`
|
||||||
|
Items *RequestSchema `json:"items,omitempty" yaml:"items,omitempty"`
|
||||||
|
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||||
|
MaxLength int `json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
|
||||||
|
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
|
||||||
|
PrimaryKeys []string `json:"primaryKeys,omitempty" yaml:"primaryKeys,omitempty"`
|
||||||
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
|
AdditionalProperties bool `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RestrictedFields struct {
|
||||||
|
Fields []string `json:"fields" yaml:"fields"`
|
||||||
|
Roles []string `json:"roles" yaml:"roles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Route struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Description string `json:"description" yaml:"description"`
|
||||||
|
Uri string `json:"route_uri" yaml:"route_uri"`
|
||||||
|
HandlerKey string `json:"handler_key" yaml:"handler_key"`
|
||||||
|
Method string `json:"route_method" yaml:"route_method"`
|
||||||
|
Schema []byte `json:"schema" yaml:"schema"`
|
||||||
|
schema *v2.Schema
|
||||||
|
Rules map[string]string `json:"rules" yaml:"rules"`
|
||||||
|
CustomRules []string `json:"custom_rules" yaml:"custom_rules"`
|
||||||
|
Model string `json:"model" yaml:"model"`
|
||||||
|
Handler Handler `json:"handler" yaml:"handler"`
|
||||||
|
Middlewares []Middleware `json:"middlewares" yaml:"middlewares"`
|
||||||
|
Operation string `json:"operation" yaml:"operation"`
|
||||||
|
RestrictedFields RestrictedFields `json:"restricted_fields" yaml:"restricted_fields"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) GetSchema() *v2.Schema {
|
||||||
|
if r.schema != nil {
|
||||||
|
return r.schema
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) SetSchema(schema *v2.Schema) {
|
||||||
|
r.schema = schema
|
||||||
|
}
|
||||||
|
|
||||||
|
type Schedule struct {
|
||||||
|
Enable bool `json:"enable" yaml:"enable"`
|
||||||
|
Interval string `json:"interval" yaml:"interval"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BackgroundHandler struct {
|
||||||
|
Queue string `json:"queue" yaml:"queue"`
|
||||||
|
HandlerKey string `json:"handler_key" yaml:"handler_key"`
|
||||||
|
Payload json.RawMessage `json:"payload" yaml:"payload"`
|
||||||
|
Handler Handler `json:"handler" yaml:"handler"`
|
||||||
|
Schedule *Schedule `json:"schedule" yaml:"schedule"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Api struct {
|
||||||
|
Prefix string `json:"prefix" yaml:"prefix"`
|
||||||
|
Middlewares []Middleware `json:"middlewares" yaml:"middlewares"`
|
||||||
|
Routes []*Route `json:"routes" yaml:"routes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AllowedStatus struct {
|
||||||
|
Source string `json:"source" yaml:"source"`
|
||||||
|
Target []string `yaml:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Transition struct {
|
||||||
|
Source string `json:"source" yaml:"source"`
|
||||||
|
Target string `json:"target" yaml:"target"`
|
||||||
|
Filters map[string]any `json:"filters" yaml:"filters"`
|
||||||
|
Validators []map[string]any `json:"validators" yaml:"validators"`
|
||||||
|
Triggers []map[string]any `json:"triggers" yaml:"triggers"`
|
||||||
|
Actions []map[string]any `json:"actions" yaml:"actions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlowPipeline struct {
|
||||||
|
ID string `json:"id" yaml:"id"`
|
||||||
|
Statuses []string `json:"statuses" yaml:"statuses"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Flow struct {
|
||||||
|
ID string `json:"id" yaml:"id"`
|
||||||
|
Service string `json:"service" yaml:"service"`
|
||||||
|
Entity string `json:"entity" yaml:"entity"`
|
||||||
|
Model string `json:"model" yaml:"model"`
|
||||||
|
StatusField string `json:"status_field" yaml:"status_field"`
|
||||||
|
AggregateBy []string `json:"aggregate_by" yaml:"aggregate_by"`
|
||||||
|
Statuses []string `json:"statuses" yaml:"statuses"`
|
||||||
|
AllowedStatuses []AllowedStatus `json:"allowed_statuses" yaml:"allowed_statuses"`
|
||||||
|
Transitions []Transition `json:"transitions" yaml:"transitions"`
|
||||||
|
Pipelines []FlowPipeline `json:"pipelines" yaml:"pipelines"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flow) GetPipeline(key string) *FlowPipeline {
|
||||||
|
for _, pipeline := range f.Pipelines {
|
||||||
|
if pipeline.ID == key {
|
||||||
|
return &pipeline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Middleware struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Options json.RawMessage `json:"options" yaml:"options"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Static struct {
|
||||||
|
Dir string `json:"dir" yaml:"dir"`
|
||||||
|
Prefix string `json:"prefix" yaml:"prefix"`
|
||||||
|
Options struct {
|
||||||
|
ByteRange bool `json:"byte_range" yaml:"byte_range"`
|
||||||
|
Compress bool `json:"compress" yaml:"compress"`
|
||||||
|
Browse bool `json:"browse" yaml:"browse"`
|
||||||
|
IndexFile string `json:"index_file" yaml:"index_file"`
|
||||||
|
} `json:"options" yaml:"options"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RenderConfig struct {
|
||||||
|
ID string `json:"id" yaml:"id"`
|
||||||
|
Prefix string `json:"prefix" yaml:"prefix"`
|
||||||
|
Root string `json:"root" yaml:"root"`
|
||||||
|
Index string `json:"index" yaml:"index"`
|
||||||
|
UseIndex bool `json:"use_index" yaml:"use_index"`
|
||||||
|
Compress bool `json:"compress" yaml:"compress"`
|
||||||
|
Extension string `json:"extension" yaml:"extension"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Web struct {
|
||||||
|
Prefix string `json:"prefix" yaml:"prefix"`
|
||||||
|
Static *Static `json:"static" yaml:"static"`
|
||||||
|
Render []*RenderConfig `json:"render" yaml:"render"`
|
||||||
|
Middlewares []Middleware `json:"middlewares" yaml:"middlewares"`
|
||||||
|
Apis []Api `json:"apis" yaml:"apis"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Policy struct {
|
||||||
|
Web Web `json:"web" yaml:"web"`
|
||||||
|
Models []Model `json:"models" yaml:"models"`
|
||||||
|
BackgroundHandlers []*BackgroundHandler `json:"background_handlers" yaml:"background_handlers"`
|
||||||
|
Commands []*GenericCommand `json:"commands"`
|
||||||
|
Conditions []*filters.Filter `json:"conditions" yaml:"conditions"`
|
||||||
|
ApplicationRules []*filters.ApplicationRule `json:"application_rules" yaml:"application_rules"`
|
||||||
|
Handlers []Handler `json:"handlers" yaml:"handlers"`
|
||||||
|
Flows []Flow `json:"flows" yaml:"flows"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserConfig struct {
|
||||||
|
Core Core `json:"core" yaml:"core"`
|
||||||
|
Policy Policy `json:"policies" yaml:"policies"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserConfig) GetModel(source string) *Model {
|
||||||
|
for _, model := range c.Policy.Models {
|
||||||
|
if model.Name == source {
|
||||||
|
return &model
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserConfig) GetRenderConfig(source string) *RenderConfig {
|
||||||
|
for _, model := range c.Policy.Web.Render {
|
||||||
|
if model.ID == source {
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserConfig) GetEntities() (entities []string) {
|
||||||
|
for _, model := range c.Policy.Models {
|
||||||
|
entities = append(entities, model.Name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserConfig) GetDatabase(db string) *metadata.Config {
|
||||||
|
for _, database := range c.Core.Credentials.Databases {
|
||||||
|
if database.Key == db {
|
||||||
|
return &database
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserConfig) GetHandler(handlerName string) *Handler {
|
||||||
|
for _, handler := range c.Policy.Handlers {
|
||||||
|
if handler.Key == handlerName {
|
||||||
|
return &handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserConfig) GetRoute(name string) *Route {
|
||||||
|
for _, routes := range c.Policy.Web.Apis {
|
||||||
|
for _, route := range routes.Routes {
|
||||||
|
if route.Name == name {
|
||||||
|
return route
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserConfig) GetHandlerList() (handlers []string) {
|
||||||
|
for _, handler := range c.Policy.Handlers {
|
||||||
|
handlers = append(handlers, handler.Key)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserConfig) GetSourceDatabase(source string, db ...string) *metadata.Config {
|
||||||
|
if len(db) > 0 && db[0] != "" {
|
||||||
|
for _, database := range c.Core.Credentials.Databases {
|
||||||
|
if database.Key == db[0] {
|
||||||
|
return &database
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
model := c.GetModel(source)
|
||||||
|
if model == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, database := range c.Core.Credentials.Databases {
|
||||||
|
if database.Key == model.Database {
|
||||||
|
return &database
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserConfig) GetCondition(key string) *filters.Filter {
|
||||||
|
for _, condition := range c.Policy.Conditions {
|
||||||
|
if condition.Key == key {
|
||||||
|
return condition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserConfig) GetApplicationRule(key string) *filters.ApplicationRule {
|
||||||
|
for _, applicationRule := range c.Policy.ApplicationRules {
|
||||||
|
if applicationRule.Key == key {
|
||||||
|
return applicationRule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserConfig) GetFlow(key string) *Flow {
|
||||||
|
for _, flow := range c.Policy.Flows {
|
||||||
|
if flow.ID == key {
|
||||||
|
return &flow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
Reference in New Issue
Block a user