feat(writer): 🎉 Update relationship field naming convention
All checks were successful
CI / Test (1.24) (push) Successful in -27m26s
CI / Test (1.25) (push) Successful in -27m14s
CI / Lint (push) Successful in -27m27s
CI / Build (push) Successful in -27m36s
Release / Build and Release (push) Successful in -27m22s
Integration Tests / Integration Tests (push) Successful in -27m17s
All checks were successful
CI / Test (1.24) (push) Successful in -27m26s
CI / Test (1.25) (push) Successful in -27m14s
CI / Lint (push) Successful in -27m27s
CI / Build (push) Successful in -27m36s
Release / Build and Release (push) Successful in -27m22s
Integration Tests / Integration Tests (push) Successful in -27m17s
* Refactor generateRelationshipFieldName to use foreign key columns for unique naming. * Add test for multiple references to the same table to ensure unique relationship field names. * Update existing tests to reflect new naming convention.
This commit is contained in:
@@ -239,7 +239,7 @@ func (w *Writer) addRelationshipFields(modelData *ModelData, table *models.Table
|
||||
|
||||
// Create relationship field (has-one in Bun, similar to belongs-to in GORM)
|
||||
refModelName := w.getModelName(constraint.ReferencedTable)
|
||||
fieldName := w.generateRelationshipFieldName(constraint.ReferencedTable)
|
||||
fieldName := w.generateRelationshipFieldName(constraint)
|
||||
relationTag := w.typeMapper.BuildRelationshipTag(constraint, "has-one")
|
||||
|
||||
modelData.AddRelationshipField(&FieldData{
|
||||
@@ -267,7 +267,7 @@ func (w *Writer) addRelationshipFields(modelData *ModelData, table *models.Table
|
||||
if constraint.ReferencedTable == table.Name && constraint.ReferencedSchema == schema.Name {
|
||||
// Add has-many relationship
|
||||
otherModelName := w.getModelName(otherTable.Name)
|
||||
fieldName := w.generateRelationshipFieldName(otherTable.Name) + "s" // Pluralize
|
||||
fieldName := w.generateRelationshipFieldName(constraint) + "s" // Pluralize
|
||||
relationTag := w.typeMapper.BuildRelationshipTag(constraint, "has-many")
|
||||
|
||||
modelData.AddRelationshipField(&FieldData{
|
||||
@@ -310,10 +310,19 @@ func (w *Writer) getModelName(tableName string) string {
|
||||
return modelName
|
||||
}
|
||||
|
||||
// generateRelationshipFieldName generates a field name for a relationship
|
||||
func (w *Writer) generateRelationshipFieldName(tableName string) string {
|
||||
// Use just the prefix (3 letters) for relationship fields
|
||||
return GeneratePrefix(tableName)
|
||||
// generateRelationshipFieldName generates a field name for a relationship based on the foreign key column
|
||||
func (w *Writer) generateRelationshipFieldName(constraint *models.Constraint) string {
|
||||
// Use the foreign key column name to ensure uniqueness
|
||||
// If there are multiple columns, use the first one
|
||||
if len(constraint.Columns) > 0 {
|
||||
columnName := constraint.Columns[0]
|
||||
// Convert to PascalCase for proper Go field naming
|
||||
// e.g., "rid_filepointer_request" -> "RidFilepointerRequest"
|
||||
return "Rel" + SnakeCaseToPascalCase(columnName)
|
||||
}
|
||||
|
||||
// Fallback to table-based prefix if no columns defined
|
||||
return "Rel" + GeneratePrefix(constraint.ReferencedTable)
|
||||
}
|
||||
|
||||
// getPackageName returns the package name from options or defaults to "models"
|
||||
|
||||
@@ -175,14 +175,118 @@ func TestWriter_WriteDatabase_MultiFile(t *testing.T) {
|
||||
postsStr := string(postsContent)
|
||||
|
||||
// Verify relationship is present with Bun format
|
||||
if !strings.Contains(postsStr, "USE") {
|
||||
t.Errorf("Missing relationship field USE")
|
||||
// Should now be RelUserID instead of USE
|
||||
if !strings.Contains(postsStr, "RelUserID") {
|
||||
t.Errorf("Missing relationship field RelUserID (new naming convention)")
|
||||
}
|
||||
if !strings.Contains(postsStr, "rel:has-one") {
|
||||
t.Errorf("Missing Bun relationship tag: %s", postsStr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriter_MultipleReferencesToSameTable(t *testing.T) {
|
||||
// Test scenario: api_event table with multiple foreign keys to filepointer table
|
||||
db := models.InitDatabase("testdb")
|
||||
schema := models.InitSchema("org")
|
||||
|
||||
// Filepointer table
|
||||
filepointer := models.InitTable("filepointer", "org")
|
||||
filepointer.Columns["id_filepointer"] = &models.Column{
|
||||
Name: "id_filepointer",
|
||||
Type: "bigserial",
|
||||
NotNull: true,
|
||||
IsPrimaryKey: true,
|
||||
}
|
||||
schema.Tables = append(schema.Tables, filepointer)
|
||||
|
||||
// API event table with two foreign keys to filepointer
|
||||
apiEvent := models.InitTable("api_event", "org")
|
||||
apiEvent.Columns["id_api_event"] = &models.Column{
|
||||
Name: "id_api_event",
|
||||
Type: "bigserial",
|
||||
NotNull: true,
|
||||
IsPrimaryKey: true,
|
||||
}
|
||||
apiEvent.Columns["rid_filepointer_request"] = &models.Column{
|
||||
Name: "rid_filepointer_request",
|
||||
Type: "bigint",
|
||||
NotNull: false,
|
||||
}
|
||||
apiEvent.Columns["rid_filepointer_response"] = &models.Column{
|
||||
Name: "rid_filepointer_response",
|
||||
Type: "bigint",
|
||||
NotNull: false,
|
||||
}
|
||||
|
||||
// Add constraints
|
||||
apiEvent.Constraints["fk_request"] = &models.Constraint{
|
||||
Name: "fk_request",
|
||||
Type: models.ForeignKeyConstraint,
|
||||
Columns: []string{"rid_filepointer_request"},
|
||||
ReferencedTable: "filepointer",
|
||||
ReferencedSchema: "org",
|
||||
ReferencedColumns: []string{"id_filepointer"},
|
||||
}
|
||||
apiEvent.Constraints["fk_response"] = &models.Constraint{
|
||||
Name: "fk_response",
|
||||
Type: models.ForeignKeyConstraint,
|
||||
Columns: []string{"rid_filepointer_response"},
|
||||
ReferencedTable: "filepointer",
|
||||
ReferencedSchema: "org",
|
||||
ReferencedColumns: []string{"id_filepointer"},
|
||||
}
|
||||
|
||||
schema.Tables = append(schema.Tables, apiEvent)
|
||||
db.Schemas = append(db.Schemas, schema)
|
||||
|
||||
// Create writer
|
||||
tmpDir := t.TempDir()
|
||||
opts := &writers.WriterOptions{
|
||||
PackageName: "models",
|
||||
OutputPath: tmpDir,
|
||||
Metadata: map[string]interface{}{
|
||||
"multi_file": true,
|
||||
},
|
||||
}
|
||||
|
||||
writer := NewWriter(opts)
|
||||
err := writer.WriteDatabase(db)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteDatabase failed: %v", err)
|
||||
}
|
||||
|
||||
// Read the api_event file
|
||||
apiEventContent, err := os.ReadFile(filepath.Join(tmpDir, "sql_org_api_event.go"))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read api_event file: %v", err)
|
||||
}
|
||||
|
||||
contentStr := string(apiEventContent)
|
||||
|
||||
// Verify both relationships have unique names based on column names
|
||||
expectations := []struct {
|
||||
fieldName string
|
||||
tag string
|
||||
}{
|
||||
{"RelRIDFilepointerRequest", "join:rid_filepointer_request=id_filepointer"},
|
||||
{"RelRIDFilepointerResponse", "join:rid_filepointer_response=id_filepointer"},
|
||||
}
|
||||
|
||||
for _, exp := range expectations {
|
||||
if !strings.Contains(contentStr, exp.fieldName) {
|
||||
t.Errorf("Missing relationship field: %s\nGenerated:\n%s", exp.fieldName, contentStr)
|
||||
}
|
||||
if !strings.Contains(contentStr, exp.tag) {
|
||||
t.Errorf("Missing relationship tag: %s\nGenerated:\n%s", exp.tag, contentStr)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify NO duplicate field names (old behavior would create duplicate "FIL" fields)
|
||||
if strings.Contains(contentStr, "FIL *ModelFilepointer") {
|
||||
t.Errorf("Found old prefix-based naming (FIL), should use column-based naming")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypeMapper_SQLTypeToGoType_Bun(t *testing.T) {
|
||||
mapper := NewTypeMapper()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user