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 {

Reply via email to