Skip to content

Commit 5d2ba3f

Browse files
authored
Merge pull request #48 from sev-2/import/rpc
fix(apply) fix import rpc
2 parents 954c9f4 + 3cda974 commit 5d2ba3f

File tree

11 files changed

+509
-87
lines changed

11 files changed

+509
-87
lines changed

pkg/generator/model.go

Lines changed: 59 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -118,49 +118,8 @@ func GenerateModel(folderPath string, input *GenerateModelInput, generateFn Gene
118118
// define file path
119119
filePath := filepath.Join(folderPath, fmt.Sprintf("%s.%s", input.Table.Name, "go"))
120120

121-
// build relation tag
122-
mapRelationName := make(map[string]bool)
123-
relation := make([]state.Relation, 0)
124-
125-
for i := range input.Relations {
126-
r := input.Relations[i]
127-
128-
if r.RelationType == raiden.RelationTypeManyToMany {
129-
key := fmt.Sprintf("%s_%s", input.Table.Name, r.Table)
130-
_, exist := mapRelationName[key]
131-
if exist {
132-
r.Table = inflection.Plural(r.Through)
133-
key = fmt.Sprintf("%s_%s", input.Table.Name, r.Table)
134-
mapRelationName[key] = true
135-
} else {
136-
mapRelationName[key] = true
137-
}
138-
}
139-
140-
if r.RelationType == raiden.RelationTypeHasOne {
141-
snakeFk := utils.ToSnakeCase(r.ForeignKey)
142-
fkTableSplit := strings.Split(snakeFk, "_")
143-
fkName := inflection.Singular(utils.SnakeCaseToPascalCase(fkTableSplit[0]))
144-
145-
r.Table = inflection.Singular(utils.SnakeCaseToPascalCase(r.Table))
146-
if fkName != r.Table {
147-
r.Table = fmt.Sprintf("%s%s", r.Table, fkName)
148-
}
149-
}
150-
151-
if r.RelationType == raiden.RelationTypeHasMany {
152-
snakeFk := utils.ToSnakeCase(r.ForeignKey)
153-
fkTableSplit := strings.Split(snakeFk, "_")
154-
fkName := inflection.Plural(utils.SnakeCaseToPascalCase(fkTableSplit[0]))
155-
r.Table = inflection.Plural(utils.SnakeCaseToPascalCase(r.Table))
156-
if fkName != r.Table {
157-
r.Table = fmt.Sprintf("%s%s", inflection.Singular(r.Table), fkName)
158-
}
159-
}
160-
161-
r.Tag = BuildJoinTag(&r)
162-
relation = append(relation, r)
163-
}
121+
// build relation field
122+
relations := BuildRelationFields(input.Table, input.Relations)
164123

165124
// set data
166125
data := GenerateModelData{
@@ -173,7 +132,7 @@ func GenerateModel(folderPath string, input *GenerateModelInput, generateFn Gene
173132
RlsTag: rlsTag,
174133
RlsEnable: input.Table.RLSEnabled,
175134
RlsForced: input.Table.RLSForced,
176-
Relations: relation,
135+
Relations: relations,
177136
}
178137

179138
// setup generate input param
@@ -338,3 +297,59 @@ func BuildJoinTag(r *state.Relation) string {
338297

339298
return strings.Join(tags, " ")
340299
}
300+
301+
func BuildRelationFields(table objects.Table, relations []state.Relation) (mappedRelations []state.Relation) {
302+
mapRelationName := make(map[string]bool)
303+
304+
for i := range relations {
305+
r := relations[i]
306+
307+
if r.RelationType == raiden.RelationTypeHasOne {
308+
snakeFk := utils.ToSnakeCase(r.ForeignKey)
309+
fkTableSplit := strings.Split(snakeFk, "_")
310+
fkName := inflection.Singular(utils.SnakeCaseToPascalCase(fkTableSplit[0]))
311+
312+
r.Table = inflection.Singular(utils.SnakeCaseToPascalCase(r.Table))
313+
if fkName != r.Table {
314+
r.Table = fmt.Sprintf("%s%s", r.Table, fkName)
315+
}
316+
mapRelationName[r.Table] = true
317+
}
318+
319+
if r.RelationType == raiden.RelationTypeHasMany {
320+
snakeFk := utils.ToSnakeCase(r.ForeignKey)
321+
fkTableSplit := strings.Split(snakeFk, "_")
322+
fkName := inflection.Plural(utils.SnakeCaseToPascalCase(fkTableSplit[0]))
323+
r.Table = inflection.Plural(utils.SnakeCaseToPascalCase(r.Table))
324+
if fkName != r.Table {
325+
r.Table = fmt.Sprintf("%s%s", inflection.Singular(r.Table), fkName)
326+
}
327+
mapRelationName[r.Table] = true
328+
}
329+
330+
if r.RelationType == raiden.RelationTypeManyToMany {
331+
r.Table = inflection.Plural(r.Table)
332+
_, exist := mapRelationName[r.Table]
333+
if exist {
334+
r.Table = inflection.Plural(r.Through)
335+
}
336+
337+
_, exist = mapRelationName[r.Table]
338+
if exist {
339+
snakeFk := utils.ToSnakeCase(r.ForeignKey)
340+
fkTableSplit := strings.Split(snakeFk, "_")
341+
fkName := inflection.Plural(utils.SnakeCaseToPascalCase(fkTableSplit[0]))
342+
r.Table = inflection.Plural(utils.SnakeCaseToPascalCase(r.Table))
343+
if fkName != r.Table {
344+
r.Table = fmt.Sprintf("%s%s", inflection.Singular(r.Table), fkName)
345+
}
346+
}
347+
mapRelationName[r.Table] = true
348+
}
349+
350+
r.Tag = BuildJoinTag(&r)
351+
mappedRelations = append(mappedRelations, r)
352+
}
353+
354+
return
355+
}

pkg/generator/model_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package generator_test
22

33
import (
4+
"encoding/json"
45
"os"
56
"path/filepath"
67
"testing"
@@ -55,3 +56,22 @@ func TestGenerateModels(t *testing.T) {
5556
assert.NoError(t, err2)
5657
assert.FileExists(t, dir+"/internal/models/test_table.go")
5758
}
59+
60+
func TestBuildRelationFields(t *testing.T) {
61+
table := objects.Table{
62+
Name: "profiles",
63+
}
64+
relationStr := `[{"Table":"places","Type":"[]*Places","RelationType":"manyToMany","PrimaryKey":"","ForeignKey":"","Tag":"","SourcePrimaryKey":"id","JoinsSourceForeignKey":"profile_id","TargetPrimaryKey":"id","JoinTargetForeignKey":"place_id","Through":"place_likes"},{"Table":"followers","Type":"[]*Followers","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"food_review_likes","Type":"[]*FoodReviewLikes","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"food_review_buddies","Type":"[]*FoodReviewBuddies","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"place_reviews","Type":"[]*PlaceReviews","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"place_likes","Type":"[]*PlaceLikes","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"collections","Type":"[]*Collections","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"chat_messages","Type":"[]*ChatMessages","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"place_review_likes","Type":"[]*PlaceReviewLikes","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"contact_numbers","Type":"[]*ContactNumbers","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"food_likes","Type":"[]*FoodLikes","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"place_review_buddies","Type":"[]*PlaceReviewBuddies","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"profile_badges","Type":"[]*ProfileBadges","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"place_check_ins","Type":"[]*PlaceCheckIns","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"food_reviews","Type":"[]*FoodReviews","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"posts","Type":"[]*Posts","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"activity_logs","Type":"[]*ActivityLogs","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"social_media_accounts","Type":"[]*SocialMediaAccounts","RelationType":"hasMany","PrimaryKey":"id","ForeignKey":"profile_id","Tag":""},{"Table":"places","Type":"[]*Places","RelationType":"manyToMany","PrimaryKey":"","ForeignKey":"","Tag":"","SourcePrimaryKey":"id","JoinsSourceForeignKey":"profile_id","TargetPrimaryKey":"id","JoinTargetForeignKey":"place_id","Through":"place_reviews"},{"Table":"place_reviews","Type":"[]*PlaceReviews","RelationType":"manyToMany","PrimaryKey":"","ForeignKey":"","Tag":"","SourcePrimaryKey":"id","JoinsSourceForeignKey":"profile_id","TargetPrimaryKey":"id","JoinTargetForeignKey":"place_review_id","Through":"place_review_likes"},{"Table":"collection_types","Type":"[]*CollectionTypes","RelationType":"manyToMany","PrimaryKey":"","ForeignKey":"","Tag":"","SourcePrimaryKey":"id","JoinsSourceForeignKey":"profile_id","TargetPrimaryKey":"id","JoinTargetForeignKey":"collection_type_id","Through":"collections"},{"Table":"foods","Type":"[]*Foods","RelationType":"manyToMany","PrimaryKey":"","ForeignKey":"","Tag":"","SourcePrimaryKey":"id","JoinsSourceForeignKey":"profile_id","TargetPrimaryKey":"id","JoinTargetForeignKey":"food_id","Through":"food_likes"},{"Table":"food_reviews","Type":"[]*FoodReviews","RelationType":"manyToMany","PrimaryKey":"","ForeignKey":"","Tag":"","SourcePrimaryKey":"id","JoinsSourceForeignKey":"profile_id","TargetPrimaryKey":"id","JoinTargetForeignKey":"food_review_id","Through":"food_review_likes"},{"Table":"places","Type":"[]*Places","RelationType":"manyToMany","PrimaryKey":"","ForeignKey":"","Tag":"","SourcePrimaryKey":"id","JoinsSourceForeignKey":"profile_id","TargetPrimaryKey":"id","JoinTargetForeignKey":"place_id","Through":"place_check_ins"},{"Table":"place_reviews","Type":"[]*PlaceReviews","RelationType":"manyToMany","PrimaryKey":"","ForeignKey":"","Tag":"","SourcePrimaryKey":"id","JoinsSourceForeignKey":"profile_id","TargetPrimaryKey":"id","JoinTargetForeignKey":"place_review_id","Through":"place_review_buddies"},{"Table":"food_reviews","Type":"[]*FoodReviews","RelationType":"manyToMany","PrimaryKey":"","ForeignKey":"","Tag":"","SourcePrimaryKey":"id","JoinsSourceForeignKey":"profile_id","TargetPrimaryKey":"id","JoinTargetForeignKey":"food_review_id","Through":"food_review_buddies"},{"Table":"badges","Type":"[]*Badges","RelationType":"manyToMany","PrimaryKey":"","ForeignKey":"","Tag":"","SourcePrimaryKey":"id","JoinsSourceForeignKey":"profile_id","TargetPrimaryKey":"id","JoinTargetForeignKey":"badge_id","Through":"profile_badges"},{"Table":"foods","Type":"[]*Foods","RelationType":"manyToMany","PrimaryKey":"","ForeignKey":"","Tag":"","SourcePrimaryKey":"id","JoinsSourceForeignKey":"profile_id","TargetPrimaryKey":"id","JoinTargetForeignKey":"food_id","Through":"food_reviews"}]`
65+
relations := make([]state.Relation, 0)
66+
err := json.Unmarshal([]byte(relationStr), &relations)
67+
assert.NoError(t, err)
68+
69+
result := generator.BuildRelationFields(table, relations)
70+
71+
mapTable := make(map[string]bool)
72+
for _, v := range result {
73+
_, exist := mapTable[v.Table]
74+
assert.False(t, exist)
75+
mapTable[v.Table] = true
76+
}
77+
}

pkg/generator/rpc.go

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ func (r *{{ .Name }}) GetRawDefinition() string {
142142
}`
143143
)
144144

145-
func GenerateRpc(basePath string, projectName string, functions []objects.Function, generateFn GenerateFn) (err error) {
145+
func GenerateRpc(basePath string, projectName string, functions []objects.Function, tables []objects.Table, generateFn GenerateFn) (err error) {
146146
folderPath := filepath.Join(basePath, RpcDir)
147147
RpcLogger.Trace("create rpc folder if not exist", "path", folderPath)
148148
if exist := utils.IsFolderExists(folderPath); !exist {
@@ -153,15 +153,15 @@ func GenerateRpc(basePath string, projectName string, functions []objects.Functi
153153

154154
for i := range functions {
155155
f := functions[i]
156-
if err := generateRpcItem(folderPath, projectName, &f, generateFn); err != nil {
156+
if err := generateRpcItem(folderPath, projectName, &f, tables, generateFn); err != nil {
157157
return err
158158
}
159159
}
160160

161161
return nil
162162
}
163163

164-
func generateRpcItem(folderPath string, projectName string, function *objects.Function, generateFn GenerateFn) error {
164+
func generateRpcItem(folderPath string, projectName string, function *objects.Function, tables []objects.Table, generateFn GenerateFn) error {
165165
// define binding func
166166
funcMaps := []template.FuncMap{
167167
{"ToSnakeCase": utils.ToSnakeCase},
@@ -178,7 +178,7 @@ func generateRpcItem(folderPath string, projectName string, function *objects.Fu
178178
filePath := filepath.Join(folderPath, fmt.Sprintf("%s.%s", utils.ToSnakeCase(function.Name), "go"))
179179

180180
// // extract rpc function
181-
result, err := ExtractRpcFunction(function)
181+
result, err := ExtractRpcFunction(function, tables)
182182
if err != nil {
183183
return err
184184
}
@@ -240,7 +240,7 @@ func generateRpcItem(folderPath string, projectName string, function *objects.Fu
240240
return generateFn(generateInput, nil)
241241
}
242242

243-
func ExtractRpcFunction(fn *objects.Function) (result ExtractRpcDataResult, err error) {
243+
func ExtractRpcFunction(fn *objects.Function, tables []objects.Table) (result ExtractRpcDataResult, err error) {
244244
// extract param
245245
params, usePrefix, e := ExtractRpcParam(fn)
246246
if e != nil {
@@ -256,6 +256,21 @@ func ExtractRpcFunction(fn *objects.Function) (result ExtractRpcDataResult, err
256256
return
257257
}
258258

259+
// validate to actual exist table
260+
if len(tables) > 0 {
261+
mapTable := make(map[string]any)
262+
for _, v := range tables {
263+
mapTable[v.Name] = true
264+
}
265+
266+
for k := range mapScannedTable {
267+
_, exist := mapTable[k]
268+
if !exist {
269+
delete(mapScannedTable, k)
270+
}
271+
}
272+
}
273+
259274
// normalize aliases
260275
if e := RpcNormalizeTableAliases(mapScannedTable); e != nil {
261276
err = e
@@ -371,7 +386,8 @@ func ExtractRpcParam(fn *objects.Function) (params []raiden.RpcParam, usePrefix
371386
pt = strings.TrimLeft(strings.TrimRight(pt, " "), " ")
372387
paramType, err := raiden.GetValidRpcParamType(pt, true)
373388
if err != nil {
374-
return params, usePrefix, err
389+
err = fmt.Errorf("got error in rpc '%s' return param type > %s", fn.Name, err.Error())
390+
return params, usePrefix, fmt.Errorf("%s %s", fa.Name, err.Error())
375391
}
376392
p.Type = paramType
377393
}
@@ -390,6 +406,7 @@ func ExtractRpcParam(fn *objects.Function) (params []raiden.RpcParam, usePrefix
390406

391407
// code for detect table in query and return array of object contain table name, table aliases and relation
392408
func ExtractRpcTable(def string) (string, map[string]*RpcScannedTable, error) {
409+
def = removeSQLComments(def)
393410
dFields := strings.Fields(utils.CleanUpString(def))
394411
mapResult := make(map[string]*RpcScannedTable)
395412
mapTableOrAlias := make(map[string]string)
@@ -407,14 +424,15 @@ func ExtractRpcTable(def string) (string, map[string]*RpcScannedTable, error) {
407424
k := strings.ToUpper(f)
408425

409426
switch k {
410-
case postgres.Create, postgres.Update, postgres.Delete, postgres.Alter:
427+
case postgres.Create, postgres.Update, postgres.Delete, postgres.Alter, postgres.With:
411428
readMode = false
412-
case postgres.Select, postgres.With:
429+
case postgres.Select:
413430
readMode = true
414431
}
415432

416433
switch lastField {
417434
case postgres.From:
435+
// stop condition
418436
if postgres.IsReservedKeyword(k) {
419437
mapResult[foundTable.Name] = foundTable
420438
mapTableOrAlias[foundTable.Name] = foundTable.Name
@@ -426,6 +444,10 @@ func ExtractRpcTable(def string) (string, map[string]*RpcScannedTable, error) {
426444
continue
427445
}
428446

447+
if postgres.IsReservedSymbol(f) || k[0] == '(' || k[0] == ')' {
448+
continue
449+
}
450+
429451
if len(foundTable.Name) == 0 {
430452
split := strings.Split(f, ".")
431453
if len(split) == 2 {
@@ -434,9 +456,6 @@ func ExtractRpcTable(def string) (string, map[string]*RpcScannedTable, error) {
434456
}
435457
foundTable.Name = f
436458
} else {
437-
if postgres.IsReservedSymbol(f) {
438-
continue
439-
}
440459
foundTable.Alias = f
441460
}
442461
case postgres.Inner, postgres.Outer, postgres.Left, postgres.Right:
@@ -445,7 +464,7 @@ func ExtractRpcTable(def string) (string, map[string]*RpcScannedTable, error) {
445464
continue
446465
}
447466
case postgres.Join, postgres.InnerJoin, postgres.OuterJoin, postgres.LeftJoin, postgres.RightJoin:
448-
if k == postgres.On {
467+
if k == postgres.On || postgres.IsReservedSymbol(f) || k[0] == '(' || k[0] == ')' {
449468
lastField = k
450469
continue
451470
}
@@ -458,13 +477,10 @@ func ExtractRpcTable(def string) (string, map[string]*RpcScannedTable, error) {
458477
}
459478
foundTable.Name = f
460479
} else {
461-
if postgres.IsReservedSymbol(f) {
462-
continue
463-
}
464480
foundTable.Alias = f
465481
}
466482
case postgres.On:
467-
if !readMode {
483+
if !readMode || k[0] == '(' || k[0] == ')' {
468484
lastField = k
469485
continue
470486
}
@@ -525,8 +541,6 @@ func ExtractRpcTable(def string) (string, map[string]*RpcScannedTable, error) {
525541
}
526542
}
527543

528-
fmt.Printf("mapResult : %+v\n", mapResult)
529-
530544
return strings.Join(dFields, " "), mapResult, nil
531545
}
532546

@@ -539,7 +553,7 @@ func RpcNormalizeTableAliases(mapTables map[string]*RpcScannedTable) error {
539553
}
540554

541555
for _, v := range mapTables {
542-
if v.Alias != "" && v.Name != "" {
556+
if (v.Alias != "" && v.Name != "") || v.Name == "" {
543557
continue
544558
}
545559
newAlias := findAvailableAlias(v.Name, mapAlias, 1)
@@ -572,7 +586,7 @@ func bindModelToDefinition(def string, mapTable map[string]*RpcScannedTable, par
572586
}
573587

574588
func findAvailableAlias(tableName string, mapAlias map[string]bool, sub int) (alias string) {
575-
if len(tableName) == sub {
589+
if sub >= len(tableName) {
576590
return ""
577591
}
578592

@@ -677,7 +691,7 @@ func (r *ExtractRpcDataResult) GetReturn(mapImports map[string]bool) (returnDecl
677691
cName := splitC[0]
678692
cType, e := raiden.GetValidRpcParamType(splitC[1], true)
679693
if e != nil {
680-
err = e
694+
err = fmt.Errorf("got error in rpc '%s' return table in column %s > %s", r.Rpc.Name, splitC[0], e.Error())
681695
return
682696
}
683697

@@ -746,3 +760,14 @@ func (r *ExtractRpcDataResult) GetBehavior() (behavior string) {
746760
return "RpcBehaviorVolatile"
747761
}
748762
}
763+
764+
func removeSQLComments(query string) string {
765+
lines := strings.Split(query, "\n")
766+
var result []string
767+
for _, line := range lines {
768+
if !strings.HasPrefix(strings.TrimSpace(line), "--") {
769+
result = append(result, line)
770+
}
771+
}
772+
return strings.Join(result, "\n")
773+
}

0 commit comments

Comments
 (0)