feat: implement service

This commit is contained in:
Oarkflow
2025-08-07 21:04:11 +05:45
parent c324140ae7
commit 1128ae76b7
12 changed files with 1757 additions and 5 deletions

1
.gitignore vendored
View File

@@ -24,3 +24,4 @@ go.work
*.svg
.qodo
.history
services/examples/config

View File

@@ -67,6 +67,7 @@ func loginDAG() *dag.DAG {
renderHTML := handlers.NewRenderHTMLNode("render-html")
renderHTML.Payload.Data = map[string]any{
"schema_file": "login.json",
"template_file": "app/templates/basic.html",
}
flow.AddNode(dag.Page, "Login Form", "LoginForm", renderHTML, true)
flow.AddNode(dag.Function, "Validate Login", "ValidateLogin", &ValidateLoginNode{})
@@ -88,6 +89,7 @@ func main() {
renderHTML := handlers.NewRenderHTMLNode("render-html")
renderHTML.Payload.Data = map[string]any{
"schema_file": "schema.json",
"template_file": "app/templates/basic.html",
}
flow.AddDAGNode(dag.Page, "Check Login", "Login", loginDAG(), true)
flow.AddNode(dag.Page, "Contact Form", "ContactForm", renderHTML)

2
go.mod
View File

@@ -11,7 +11,7 @@ require (
github.com/oarkflow/expr v0.0.11
github.com/oarkflow/form v0.0.0-20241203111156-b1be5636af43
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/xid v1.2.8
golang.org/x/crypto v0.33.0

4
go.sum
View File

@@ -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/jet v0.0.4 h1:rs0nTzodye/9zhrSX7FlR80Gjaty6ei2Ln0pmaUrdwg=
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.21/go.mod h1:maoLmQZJ/8pF1MugtpVqzHJ59dH1Z7xFSNkhl9BQjYo=
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.79 h1:DxhtkBGG+pUu6cudSVw5g75FbKEQJkij5w7n5AEN00M=

439
services/dto.go Normal file
View 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
View 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)
}

View 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
View 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
View 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
View 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
View 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
View 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
}