This is an automated email from the ASF dual-hosted git repository.
klesh pushed a commit to branch kw-5519-remoteapi-dshelper
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/kw-5519-remoteapi-dshelper by
this push:
new df4a07510 refactor: wip Dynamic Model
df4a07510 is described below
commit df4a0751051fc7d84f0de9aebd93bb75137e67c4
Author: Klesh Wong <[email protected]>
AuthorDate: Mon May 6 09:29:29 2024 +0800
refactor: wip Dynamic Model
---
.../server/services/remote/models/conversion.go | 59 ++++++++++++----------
.../services/remote/models/conversion_test.go | 25 +++++++++
backend/server/services/remote/models/migration.go | 10 ++--
backend/server/services/remote/models/models.go | 21 ++++++--
4 files changed, 80 insertions(+), 35 deletions(-)
diff --git a/backend/server/services/remote/models/conversion.go
b/backend/server/services/remote/models/conversion.go
index c3a3e0c6d..1307e37a1 100644
--- a/backend/server/services/remote/models/conversion.go
+++ b/backend/server/services/remote/models/conversion.go
@@ -27,24 +27,11 @@ import (
"github.com/apache/incubator-devlake/impls/dalgorm"
"github.com/apache/incubator-devlake/core/errors"
- "github.com/apache/incubator-devlake/core/models"
"github.com/apache/incubator-devlake/core/utils"
"gorm.io/datatypes"
)
-func LoadTableModel(tableName string, schema utils.JsonObject, parentModel
any) (models.DynamicTabler, errors.Error) {
- var baseType reflect.Type = nil
- if parentModel != nil {
- baseType = reflect.TypeOf(parentModel)
- }
- structType, err := GenerateStructType(schema, baseType)
- if err != nil {
- return nil, err
- }
- return models.NewDynamicTabler(tableName, structType), nil
-}
-
-func GenerateStructType(schema utils.JsonObject, baseType reflect.Type)
(reflect.Type, errors.Error) {
+func GenerateStructType(schema utils.JsonObject, anonymousFields ...any)
(reflect.Type, errors.Error) {
var structFields []reflect.StructField
props, err := utils.GetProperty[utils.JsonObject](schema, "properties")
if err != nil {
@@ -54,17 +41,32 @@ func GenerateStructType(schema utils.JsonObject, baseType
reflect.Type) (reflect
if err != nil {
required = []string{}
}
- if baseType != nil {
+ var anonymousFieldTypes []reflect.Type
+ for _, field := range anonymousFields {
+ if field == nil {
+ panic(errors.Default.New("anonymousField cannot be
nil"))
+ }
+ var fieldType reflect.Type
+ if t, ok := field.(reflect.Type); ok {
+ fieldType = t
+ } else {
+ fieldType = reflect.TypeOf(field)
+ }
+ anonymousFieldTypes = append(anonymousFieldTypes, fieldType)
+ name := fieldType.Name()
+ if fieldType.Kind() == reflect.Pointer {
+ name = fieldType.Elem().Name()
+ }
anonymousField := reflect.StructField{
- Name: baseType.Name(),
- Type: baseType,
+ Name: name,
+ Type: fieldType,
Tag:
reflect.StructTag("mapstructure:\",squash\""),
Anonymous: true,
}
structFields = append(structFields, anonymousField)
}
for k, v := range props {
- if baseType != nil && isBaseTypeField(k, baseType) {
+ if anonymousFieldTypes != nil && isBaseTypeField(k,
anonymousFieldTypes...) {
continue
}
spec := v.(utils.JsonObject)
@@ -113,18 +115,23 @@ func isRequired(fieldName string, required []string) bool
{
return false
}
-func isBaseTypeField(fieldName string, baseType reflect.Type) bool {
+func isBaseTypeField(fieldName string, baseTypes ...reflect.Type) bool {
fieldName = canonicalFieldName(fieldName)
- for i := 0; i < baseType.NumField(); i++ {
- baseField := baseType.Field(i)
- if baseField.Anonymous {
- if isBaseTypeField(fieldName, baseField.Type) {
+ for _, baseType := range baseTypes {
+ if baseType.Kind() == reflect.Pointer {
+ baseType = baseType.Elem()
+ }
+ for i := 0; i < baseType.NumField(); i++ {
+ baseField := baseType.Field(i)
+ if baseField.Anonymous {
+ if isBaseTypeField(fieldName, baseField.Type) {
+ return true
+ }
+ }
+ if fieldName == canonicalFieldName(baseField.Name) {
return true
}
}
- if fieldName == canonicalFieldName(baseField.Name) {
- return true
- }
}
return false
}
diff --git a/backend/server/services/remote/models/conversion_test.go
b/backend/server/services/remote/models/conversion_test.go
index 1b0bc9c2c..7ed91777b 100644
--- a/backend/server/services/remote/models/conversion_test.go
+++ b/backend/server/services/remote/models/conversion_test.go
@@ -21,6 +21,7 @@ import (
"reflect"
"testing"
+ "github.com/apache/incubator-devlake/core/dal"
"github.com/stretchr/testify/assert"
)
@@ -151,3 +152,27 @@ func TestGetGormTagEncDec(t *testing.T) {
tag := getGormTag(schema, stringType)
assert.Equal(t, "gorm:\"type:text;serializer:encdec\"", tag)
}
+
+func TestGenerateStructType(t *testing.T) {
+ schema := map[string]interface{}{
+ "title": "Test",
+ "type": "object",
+ "properties": map[string]interface{}{
+ "i": map[string]interface{}{
+ "type": "integer",
+ },
+ "s": map[string]interface{}{
+ "type": "string",
+ },
+ },
+ }
+ typ, err := GenerateStructType(schema, reflect.TypeOf(DynamicModel{}))
+ assert.NoError(t, err)
+ assert.Equal(t, reflect.Struct, typ.Kind())
+ val := reflect.New(typ).Interface()
+ di := NewDynamicModel("TestModel", "testTable")
+
reflect.ValueOf(val).Elem().FieldByName("DynamicModel").Set(reflect.ValueOf(di))
+ //
reflect.ValueOf(val).Elem().FieldByName("I").Set(reflect.ValueOf(int64(1)))
+ assert.Equal(t, "testTable", any(di).(dal.Tabler).TableName())
+ assert.Equal(t, "testTable",
reflect.ValueOf(val).Elem().FieldByName("DynamicModel").Interface().(dal.Tabler).TableName())
+}
diff --git a/backend/server/services/remote/models/migration.go
b/backend/server/services/remote/models/migration.go
index 2dccb7b23..67f921295 100644
--- a/backend/server/services/remote/models/migration.go
+++ b/backend/server/services/remote/models/migration.go
@@ -23,8 +23,6 @@ import (
"github.com/apache/incubator-devlake/core/context"
"github.com/apache/incubator-devlake/core/dal"
"github.com/apache/incubator-devlake/core/errors"
- "github.com/apache/incubator-devlake/core/models"
- "github.com/apache/incubator-devlake/core/models/common"
"github.com/apache/incubator-devlake/core/plugin"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
)
@@ -146,14 +144,14 @@ func (o CreateTableOperation) Execute(basicRes
context.BasicRes) errors.Error {
basicRes.GetLogger().Warn(nil, "table %s already exists. It
won't be created.", o.ModelInfo.TableName)
return nil
}
- model, err := o.ModelInfo.LoadDynamicTabler(common.NoPKModel{})
+ mi, err := GenerateRemoteModelInfo[any](o.ModelInfo)
if err != nil {
return err
}
// uncomment to debug "modelDump" as needed
- modelDump := models.DumpInfo(model.New())
- _ = modelDump
- err = api.CallDB(db.AutoMigrate, model.New())
+ // modelDump := models.DumpInfo(mi.New())
+ // _ = modelDump
+ err = api.CallDB(db.AutoMigrate, mi.New())
if err != nil {
return err
}
diff --git a/backend/server/services/remote/models/models.go
b/backend/server/services/remote/models/models.go
index 314ddee27..f6824f124 100644
--- a/backend/server/services/remote/models/models.go
+++ b/backend/server/services/remote/models/models.go
@@ -20,7 +20,6 @@ package models
import (
"reflect"
- "github.com/apache/incubator-devlake/core/errors"
"github.com/apache/incubator-devlake/core/models"
"github.com/apache/incubator-devlake/core/models/common"
"github.com/apache/incubator-devlake/core/plugin"
@@ -52,8 +51,24 @@ type DynamicModelInfo struct {
TableName string `json:"table_name" validate:"required"`
}
-func (d DynamicModelInfo) LoadDynamicTabler(parentModel any)
(models.DynamicTabler, errors.Error) {
- return LoadTableModel(d.TableName, d.JsonSchema, parentModel)
+type DynamicModel struct {
+ modelName string
+ tableName string
+}
+
+func NewDynamicModel(modelName, tableName string) DynamicModel {
+ return DynamicModel{
+ modelName: modelName,
+ tableName: tableName,
+ }
+}
+
+func (di DynamicModel) ModelName() string {
+ return di.modelName
+}
+
+func (di DynamicModel) TableName() string {
+ return di.tableName
}
type ScopeModel struct {