diff --git a/cmd/rest/rest.go b/cmd/rest/rest.go
index 2007606..c179e7d 100644
--- a/cmd/rest/rest.go
+++ b/cmd/rest/rest.go
@@ -27,7 +27,7 @@ import (
func main() {
err := cmd.Rest()
if err != nil {
- fmt.Println(err)
+ fmt.Print(err)
os.Exit(1)
}
os.Exit(0)
diff --git a/go.mod b/go.mod
index f8588d4..062f1c3 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
cuelang.org/go v0.5.0
github.com/dave/jennifer v1.6.1
github.com/gin-gonic/gin v1.9.1
+ github.com/go-playground/validator/v10 v10.14.0
github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.25.5
)
@@ -20,7 +21,6 @@ require (
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
- github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
diff --git a/internal/gencode/gencode.go b/internal/gencode/gencode.go
index 6e27791..333e0fc 100644
--- a/internal/gencode/gencode.go
+++ b/internal/gencode/gencode.go
@@ -55,7 +55,7 @@ func Exec(root string, rootOutput string) error {
return nil
}
-func gen(fhub model.Fhub) ([]byte, error) {
+func gen(fhub model.FHub) ([]byte, error) {
f := jen.NewFile("main")
f.PackagePrefix = "pkg"
diff --git a/internal/gencode/gencode_test.go b/internal/gencode/gencode_test.go
index 43214aa..e57fdea 100644
--- a/internal/gencode/gencode_test.go
+++ b/internal/gencode/gencode_test.go
@@ -11,7 +11,7 @@ import (
func Test_gen(t *testing.T) {
t.Run("not use initialize", func(t *testing.T) {
- fhub := model.Fhub{
+ fhub := model.FHub{
Packages: map[string]model.Package{
"test": {
Import: "fhub.dev/test",
@@ -69,7 +69,7 @@ func (f *functions) function_test(input map[string]any) map[string]any {
})
t.Run("use initialize", func(t *testing.T) {
- fhub := model.Fhub{
+ fhub := model.FHub{
Packages: map[string]model.Package{
"test": {
Import: "fhub.dev/test",
diff --git a/model/fhub.go b/model/fhub.go
index bdc49f9..8931637 100644
--- a/model/fhub.go
+++ b/model/fhub.go
@@ -17,13 +17,145 @@
package model
-type Fhub struct {
- Name string
- Version string
- SpecVersion string
+type FHub struct {
+ // Function namespace
+ Name string `validate:"required"`
+ // Function version
+ Version string `validate:"required"`
+ // FHub schema version
+ SpecVersion string `validate:"required"`
Constants map[string]string
Env []string
Import []string
- Packages map[string]Package
- Functions map[string]Function
+ Packages map[string]Package `validate:"min=1,dive"`
+ Functions map[string]Function `validate:"min=1,dive"`
+}
+
+func (in *FHub) DeepCopy() (out *FHub) {
+ out = new(FHub)
+ *out = *in
+
+ out.Env = make([]string, len(in.Env))
+ copy(out.Env, in.Env)
+
+ out.Import = make([]string, len(in.Import))
+ copy(out.Import, in.Env)
+
+ out.Constants = make(map[string]string, len(in.Constants))
+ for key, constant := range in.Constants {
+ out.Constants[key] = constant
+ }
+
+ out.Functions = make(map[string]Function, len(in.Functions))
+ for key, function := range in.Functions {
+ out.Functions[key] = function
+ }
+
+ out.Packages = make(map[string]Package, len(in.Packages))
+ for key, pkg := range in.Packages {
+ out.Packages[key] = pkg
+ }
+
+ return
+}
+
+type Package struct {
+ Import string `validate:"required"`
+ Launch string
+ Build Build
+ Serving Serving
+}
+
+func (p *Package) HasLaunch() bool {
+ return p.Launch != ""
+}
+
+func (in *Package) DeepCopy() (out *Package) {
+ *out = *in
+ return
+}
+
+type Serving struct {
+ Http *Http
+ Grpc *Grpc
+}
+
+func (in *Serving) DeepCopy() (out *Serving) {
+ out = new(Serving)
+ *out = *in
+
+ if out.Http != nil {
+ in, out := &in.Http, &out.Http
+ *out = new(Http)
+ **out = **in
+ }
+ if out.Grpc != nil {
+ in, out := &in.Grpc, &out.Grpc
+ *out = new(Grpc)
+ **out = **in
+ }
+ return
+}
+
+type Http struct {
+ Url string `validate:"required"`
+}
+
+func (in *Http) DeepCopy() (out *Http) {
+ out = new(Http)
+ *out = *in
+ return
+}
+
+type Grpc struct {
+ Proto string `validate:"required"`
+}
+
+func (in *Grpc) DeepCopy() (out *Grpc) {
+ out = new(Grpc)
+ *out = *in
+ return
+}
+
+type Build struct {
+ Local *Local `validate:"required_without=Container"`
+ Container *Container `validate:"required_without=Local"`
+}
+
+func (in *Build) DeepCopy() (out *Build) {
+ out = new(Build)
+ *out = *in
+ if out.Local != nil {
+ in, out := &in.Local, &out.Local
+ *out = new(Local)
+ **out = **in
+ }
+ if out.Container != nil {
+ in, out := &in.Container, &out.Container
+ *out = new(Container)
+ **out = **in
+ }
+ return out
+}
+
+type Local struct {
+ Source string `validate:"required"`
+}
+
+func (in *Local) DeepCopy() (out *Local) {
+ out = new(Local)
+ *out = *in
+ return
+}
+
+type Container struct {
+ Image string `validate:"required_without=ContainerFile"`
+ ContainerFile string `validate:"required_without=Image"`
+ Source string `validate:"required"`
+}
+
+func (in *Container) DeepCopy() (out *Container) {
+ out = new(Container)
+ *out = *in
+ return
}
diff --git a/model/function.go b/model/function.go
index 7cd59c4..a6c1993 100644
--- a/model/function.go
+++ b/model/function.go
@@ -30,13 +30,13 @@ type Function struct {
inputValue cue.Value `fhub:"input" fhub-unmarshal:"true"`
outputValue cue.Value `fhub:"output" fhub-unmarshal:"true"`
- Package string
- Launch string
+ Package string `validate:"required"`
+ Launch string `validate:"required"`
- InputsLabel []string
- InputsType []string
- OutputsLabel []string
- OutputsType []string
+ InputsLabel []string `validate:"min=1"`
+ InputsType []string `validate:"min=1"`
+ OutputsLabel []string `validate:"min=1"`
+ OutputsType []string `validate:"min=1"`
}
func (f *Function) Unmarshal(field string, value cue.Value) (err error) {
diff --git a/model/package.go b/model/package.go
deleted file mode 100644
index f64ea94..0000000
--- a/model/package.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2023 The fhub-runtime-go Authors
-// This file is part of fhub-runtime-go.
-//
-// This file is part of fhub-runtime-go.
-// fhub-runtime-go is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// fhub-runtime-go is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with fhub-runtime-go. If not, see .
-
-package model
-
-type Package struct {
- Import string
- Launch string
- Serving Serving
- Build Build
-}
-
-func (p *Package) HasLaunch() bool {
- return p.Launch != ""
-}
-
-type Serving struct {
- Http Http
-}
-
-type Http struct {
- Url string
-}
-
-type Build struct {
- Local Local
- Container Container
-}
-
-type Local struct {
- Source string
-}
-
-type Container struct {
- Image string
- ContainerFile string
- Source string
-}
diff --git a/model/unmarshal.go b/model/unmarshal.go
index ff47740..5b1a04a 100644
--- a/model/unmarshal.go
+++ b/model/unmarshal.go
@@ -30,48 +30,48 @@ import (
"cuelang.org/go/cue/cuecontext"
)
-func UnmarshalFile(path string) (Fhub, error) {
+func UnmarshalFile(path string) (FHub, error) {
data, err := os.ReadFile(path)
if err != nil {
- return Fhub{}, err
+ return FHub{}, err
}
return UnmarshalBytes(data)
}
-func UnmarshalHttp(url string) (Fhub, error) {
+func UnmarshalHttp(url string) (FHub, error) {
resp, err := http.Get(url)
if err != nil {
- return Fhub{}, err
+ return FHub{}, err
}
body, err := io.ReadAll(resp.Body)
if err != nil {
- return Fhub{}, err
+ return FHub{}, err
}
return UnmarshalBytes(body)
}
-func UnmarshalBytes(data []byte) (Fhub, error) {
+func UnmarshalString(data string) (FHub, error) {
+ return UnmarshalBytes([]byte(data))
+}
+
+func UnmarshalBytes(data []byte) (FHub, error) {
ctx := cuecontext.New()
value := ctx.CompileBytes(data)
fhub, err := unmarshalStart(value)
if err != nil {
- return Fhub{}, err
+ return FHub{}, err
}
- return fhub, nil
-}
-func UnmarshalString(data string) (Fhub, error) {
- ctx := cuecontext.New()
- value := ctx.CompileString(data)
- fhub, err := unmarshalStart(value)
+ err = Validator(fhub)
if err != nil {
- return Fhub{}, err
+ return FHub{}, err
}
+
return fhub, nil
}
-func unmarshalStart(value cue.Value) (Fhub, error) {
- fhub := Fhub{}
+func unmarshalStart(value cue.Value) (FHub, error) {
+ fhub := FHub{}
outValueOf := reflect.ValueOf(&fhub).Elem()
err := unmarshalDiscoverType("fhub", outValueOf, value)
@@ -98,9 +98,13 @@ func unmarshalDiscoverType(namespace string, outValueOf reflect.Value, value cue
err = unmarshalMapIt(namespace, outValueOf, value)
case reflect.Slice:
err = unmarshalListIt(namespace, outValueOf, value)
+ case reflect.Ptr:
+ outValueOf.Set(reflect.New(outValueOf.Type().Elem()))
+ err = unmarshalDiscoverType(namespace, outValueOf.Elem(), value)
default:
panic(fmt.Sprintf("type %q not implemented from key", kind))
}
+
return err
}
diff --git a/model/validator.go b/model/validator.go
new file mode 100644
index 0000000..a8f4370
--- /dev/null
+++ b/model/validator.go
@@ -0,0 +1,65 @@
+// Copyright 2023 The fhub-runtime-go Authors
+// This file is part of fhub-runtime-go.
+//
+// This file is part of fhub-runtime-go.
+// fhub-runtime-go is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// fhub-runtime-go is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with fhub-runtime-go. If not, see .
+
+package model
+
+import (
+ "context"
+
+ "github.com/go-playground/validator/v10"
+)
+
+type ValidatorCtxValueKey string
+
+const ValidatorCtxValue ValidatorCtxValueKey = "value"
+
+func Validator(model FHub) error {
+ validate := validator.New()
+
+ validate.RegisterStructValidationCtx(fhubStructLevel, FHub{})
+ validate.RegisterStructValidationCtx(functionStructLevel, Function{})
+
+ ctx := context.Background()
+ ctx = context.WithValue(ctx, ValidatorCtxValue, model)
+ err := validate.StructCtx(ctx, model)
+
+ return err
+}
+
+func fhubStructLevel(ctx context.Context, sl validator.StructLevel) {
+ // fhub, ok := sl.Current().Interface().(FHub)
+ // if !ok {
+ // return
+ // }
+
+}
+
+func functionStructLevel(ctx context.Context, sl validator.StructLevel) {
+ function, ok := sl.Current().Interface().(Function)
+ if !ok {
+ return
+ }
+
+ fhub, ok := ctx.Value(ValidatorCtxValue).(FHub)
+ if !ok {
+ return
+ }
+
+ if _, ok := fhub.Packages[function.Package]; !ok {
+ sl.ReportError(sl.Current(), "package", "Package", "exists", "")
+ }
+}
diff --git a/model/validator_test.go b/model/validator_test.go
new file mode 100644
index 0000000..6db9c27
--- /dev/null
+++ b/model/validator_test.go
@@ -0,0 +1,112 @@
+// Copyright 2023 The fhub-runtime-go Authors
+// This file is part of fhub-runtime-go.
+//
+// This file is part of fhub-runtime-go.
+// fhub-runtime-go is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// fhub-runtime-go is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with fhub-runtime-go. If not, see .
+
+package model
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+var structFhubDefault = FHub{
+ Name: "namespace",
+ Version: "0.1",
+ SpecVersion: "0.1",
+ Packages: map[string]Package{
+ "test": {
+ Import: "fhub.dev/test",
+ Build: Build{
+ Local: &Local{
+ Source: "./",
+ },
+ },
+ },
+ },
+ Functions: map[string]Function{
+ "fuctionTest": {
+ Package: "test",
+ Launch: "Test",
+ InputsLabel: []string{"arg1"},
+ InputsType: []string{"string"},
+ OutputsLabel: []string{"out1"},
+ OutputsType: []string{"string"},
+ },
+ },
+}
+
+type validatorTestCase struct {
+ Name string
+ Model FHub
+ Err string
+}
+
+func TestValidator(t *testing.T) {
+ testCases := []validatorTestCase{{
+ Name: "success",
+ Model: structFhubDefault,
+ Err: ``,
+ }, {
+ Name: "",
+ Model: func() FHub {
+ f := structFhubDefault
+ f.Name = ""
+ f.Version = ""
+ f.SpecVersion = ""
+ return f
+ }(),
+ Err: `Key: 'FHub.Name' Error:Field validation for 'Name' failed on the 'required' tag
+Key: 'FHub.Version' Error:Field validation for 'Version' failed on the 'required' tag
+Key: 'FHub.SpecVersion' Error:Field validation for 'SpecVersion' failed on the 'required' tag`,
+ }, {
+ Name: "package not exists",
+ Model: func() FHub {
+ f := structFhubDefault.DeepCopy()
+ ff := f.Functions["fuctionTest"]
+ ff.Package = "invalid"
+ f.Functions["fuctionTest"] = ff
+
+ return *f
+ }(),
+ Err: `Key: 'FHub.Functions[fuctionTest].package' Error:Field validation for 'package' failed on the 'exists' tag`,
+ }, {
+ Name: "build required",
+ Model: func() FHub {
+ f := structFhubDefault.DeepCopy()
+ pkg := f.Packages["test"]
+ pkg.Build.Local = nil
+ f.Packages["test"] = pkg
+ return *f
+ }(),
+ Err: `Key: 'FHub.Packages[test].Build.Local' Error:Field validation for 'Local' failed on the 'required_without' tag
+Key: 'FHub.Packages[test].Build.Container' Error:Field validation for 'Container' failed on the 'required_without' tag`,
+ }}
+
+ for _, tc := range testCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ err := Validator(tc.Model)
+ if tc.Err != "" {
+ if assert.Error(t, err) {
+ assert.Equal(t, tc.Err, err.Error())
+ }
+ } else {
+ assert.NoError(t, err)
+ }
+ })
+ }
+
+}