Added Graphql
This commit is contained in:
178
pkg/writers/graphql/relationships.go
Normal file
178
pkg/writers/graphql/relationships.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||
)
|
||||
|
||||
func (w *Writer) generateRelationFields(table *models.Table, db *models.Database, schema *models.Schema) []string {
|
||||
var fields []string
|
||||
|
||||
// 1. Forward relationships (this table has FK)
|
||||
for _, constraint := range table.Constraints {
|
||||
if constraint.Type != models.ForeignKeyConstraint {
|
||||
continue
|
||||
}
|
||||
|
||||
// Find the related table
|
||||
relatedTable := w.findTable(db, constraint.ReferencedSchema, constraint.ReferencedTable)
|
||||
if relatedTable == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Generate field name (remove "Id" suffix from FK column if present)
|
||||
fieldName := w.relationFieldName(constraint.Columns[0])
|
||||
|
||||
// Determine nullability from FK column
|
||||
nullable := true
|
||||
for _, colName := range constraint.Columns {
|
||||
if col, exists := table.Columns[colName]; exists {
|
||||
if col.NotNull {
|
||||
nullable = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format: fieldName: RelatedType! or fieldName: RelatedType
|
||||
gqlType := relatedTable.Name
|
||||
if !nullable {
|
||||
gqlType += "!"
|
||||
}
|
||||
|
||||
fields = append(fields, fmt.Sprintf(" %s: %s", fieldName, gqlType))
|
||||
}
|
||||
|
||||
// 2. Reverse relationships (other tables reference this table)
|
||||
for _, otherSchema := range db.Schemas {
|
||||
for _, otherTable := range otherSchema.Tables {
|
||||
if otherTable.Name == table.Name && otherSchema.Name == schema.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip join tables for many-to-many
|
||||
if w.isJoinTable(otherTable) {
|
||||
// Check if this is a many-to-many through this join table
|
||||
if m2mField := w.getManyToManyField(table, otherTable, db); m2mField != "" {
|
||||
fields = append(fields, m2mField)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
for _, constraint := range otherTable.Constraints {
|
||||
if constraint.Type == models.ForeignKeyConstraint &&
|
||||
constraint.ReferencedTable == table.Name &&
|
||||
constraint.ReferencedSchema == schema.Name {
|
||||
// Add reverse relationship field (array)
|
||||
fieldName := w.pluralize(w.camelCase(otherTable.Name))
|
||||
fields = append(fields, fmt.Sprintf(" %s: [%s!]!", fieldName, otherTable.Name))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
func (w *Writer) getManyToManyField(table *models.Table, joinTable *models.Table, db *models.Database) string {
|
||||
// Find the two FK constraints in the join table
|
||||
var fk1, fk2 *models.Constraint
|
||||
for _, constraint := range joinTable.Constraints {
|
||||
if constraint.Type == models.ForeignKeyConstraint {
|
||||
if fk1 == nil {
|
||||
fk1 = constraint
|
||||
} else {
|
||||
fk2 = constraint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if fk1 == nil || fk2 == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Determine which FK points to our table and which to the other table
|
||||
var targetConstraint *models.Constraint
|
||||
if fk1.ReferencedTable == table.Name {
|
||||
targetConstraint = fk2
|
||||
} else if fk2.ReferencedTable == table.Name {
|
||||
targetConstraint = fk1
|
||||
} else {
|
||||
return "" // This join table doesn't involve our table
|
||||
}
|
||||
|
||||
// Find the target table
|
||||
targetTable := w.findTable(db, targetConstraint.ReferencedSchema, targetConstraint.ReferencedTable)
|
||||
if targetTable == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Generate many-to-many field
|
||||
fieldName := w.pluralize(w.camelCase(targetTable.Name))
|
||||
return fmt.Sprintf(" %s: [%s!]!", fieldName, targetTable.Name)
|
||||
}
|
||||
|
||||
func (w *Writer) findTable(db *models.Database, schemaName, tableName string) *models.Table {
|
||||
for _, schema := range db.Schemas {
|
||||
if schema.Name != schemaName {
|
||||
continue
|
||||
}
|
||||
for _, table := range schema.Tables {
|
||||
if table.Name == tableName {
|
||||
return table
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Writer) relationFieldName(fkColumnName string) string {
|
||||
// Remove "Id" or "_id" suffix
|
||||
name := fkColumnName
|
||||
if strings.HasSuffix(name, "Id") {
|
||||
name = name[:len(name)-2]
|
||||
} else if strings.HasSuffix(name, "_id") {
|
||||
name = name[:len(name)-3]
|
||||
}
|
||||
|
||||
return w.camelCase(name)
|
||||
}
|
||||
|
||||
func (w *Writer) camelCase(s string) string {
|
||||
// If already camelCase or PascalCase, convert to camelCase
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
|
||||
// Convert first character to lowercase
|
||||
return strings.ToLower(string(s[0])) + s[1:]
|
||||
}
|
||||
|
||||
func (w *Writer) pluralize(s string) string {
|
||||
// Simple pluralization rules
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
|
||||
// Already plural
|
||||
if strings.HasSuffix(s, "s") {
|
||||
return s
|
||||
}
|
||||
|
||||
// Words ending in 'y' → 'ies'
|
||||
if strings.HasSuffix(s, "y") {
|
||||
return s[:len(s)-1] + "ies"
|
||||
}
|
||||
|
||||
// Words ending in 's', 'x', 'z', 'ch', 'sh' → add 'es'
|
||||
if strings.HasSuffix(s, "s") || strings.HasSuffix(s, "x") ||
|
||||
strings.HasSuffix(s, "z") || strings.HasSuffix(s, "ch") ||
|
||||
strings.HasSuffix(s, "sh") {
|
||||
return s + "es"
|
||||
}
|
||||
|
||||
// Default: add 's'
|
||||
return s + "s"
|
||||
}
|
||||
Reference in New Issue
Block a user