forked from rachmadiyanti.annisa.3004/service_antrean
Perbaikan generate
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -21,6 +22,7 @@ type HandlerData struct {
|
||||
DirPath string // Path direktori lengkap
|
||||
ModuleName string
|
||||
TableName string
|
||||
TableSchema []ColumnConfig // Untuk penyimpanan schema
|
||||
HasGet bool
|
||||
HasPost bool
|
||||
HasPut bool
|
||||
@@ -71,6 +73,20 @@ type ServiceConfig struct {
|
||||
Endpoints map[string]EndpointConfig `yaml:"endpoints"`
|
||||
}
|
||||
|
||||
// SchemaConfig represents a schema configuration
|
||||
type SchemaConfig struct {
|
||||
Columns []ColumnConfig `yaml:"columns"`
|
||||
}
|
||||
|
||||
// ColumnConfig represents a column configuration
|
||||
type ColumnConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
Type string `yaml:"type"`
|
||||
Nullable bool `yaml:"nullable,omitempty"`
|
||||
GoType string `yaml:"go_type,omitempty"` // Untuk override tipe Go secara manual
|
||||
PrimaryKey bool `yaml:"primary_key,omitempty"`
|
||||
}
|
||||
|
||||
// EndpointConfig represents an endpoint configuration
|
||||
type EndpointConfig struct {
|
||||
Description string `yaml:"description"`
|
||||
@@ -78,6 +94,7 @@ type EndpointConfig struct {
|
||||
HandlerFile string `yaml:"handler_file"`
|
||||
HandlerName string `yaml:"handler_name"`
|
||||
TableName string `yaml:"table_name,omitempty"`
|
||||
Schema SchemaConfig `yaml:"schema,omitempty"`
|
||||
Functions map[string]FunctionConfig `yaml:"functions"`
|
||||
}
|
||||
|
||||
@@ -396,14 +413,15 @@ func generateFromServicesConfig(config *ServicesConfig) {
|
||||
entityPlural := entityLower + "s"
|
||||
|
||||
data := HandlerData{
|
||||
Name: entityName,
|
||||
NameLower: entityLower,
|
||||
NamePlural: entityPlural,
|
||||
Category: pathInfo.Category,
|
||||
DirPath: pathInfo.DirPath,
|
||||
ModuleName: config.Global.ModuleName,
|
||||
TableName: tableName,
|
||||
Timestamp: time.Now().Format("2006-01-02 15:04:05"),
|
||||
Name: entityName,
|
||||
NameLower: entityLower,
|
||||
NamePlural: entityPlural,
|
||||
Category: pathInfo.Category,
|
||||
DirPath: pathInfo.DirPath,
|
||||
ModuleName: config.Global.ModuleName,
|
||||
TableName: tableName,
|
||||
TableSchema: endpoint.Schema.Columns,
|
||||
Timestamp: time.Now().Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
|
||||
// Set methods berdasarkan aggregated methods
|
||||
@@ -444,14 +462,19 @@ func generateFromServicesConfig(config *ServicesConfig) {
|
||||
continue
|
||||
}
|
||||
|
||||
if shouldSkipExistingFile(modelPath, "model") {
|
||||
fmt.Printf("⚠️ Skipping model generation: %s\n", modelPath)
|
||||
continue
|
||||
if len(data.TableSchema) > 0 {
|
||||
if shouldSkipExistingFile(modelPath, "model") {
|
||||
fmt.Printf("⚠️ Skipping model generation: %s\n", modelPath)
|
||||
} else {
|
||||
generateModelFile(data, modelDir) // Memanggil fungsi baru
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("⚠️ Skipping model generation for '%s' because no schema is defined in the config.\n", entityName)
|
||||
}
|
||||
|
||||
// Generate files (SEKALI SAJA per endpoint)
|
||||
generateHandlerFile(data, handlerDir)
|
||||
generateModelFile(data, modelDir)
|
||||
// generateModelFile(data, modelDir)
|
||||
|
||||
// HANYA UPDATE ROUTES SEKALI PER ENDPOINT setelah semua fungsi di-aggregate
|
||||
updateRoutesFile(data)
|
||||
@@ -483,7 +506,8 @@ func main() {
|
||||
}
|
||||
|
||||
// Check for services-config.yaml first (new format)
|
||||
servicesConfig, err := loadServicesConfig(configPath)
|
||||
servicesConfig, err :=
|
||||
loadServicesConfig(configPath)
|
||||
if err == nil {
|
||||
// Use services config
|
||||
if *verboseFlag {
|
||||
@@ -2021,20 +2045,10 @@ func (h *` + data.Name + `Handler) delete` + data.Name + `(ctx context.Context,
|
||||
|
||||
// ================= MODEL GENERATION =====================
|
||||
func generateModelFile(data HandlerData, modelDir string) {
|
||||
// Tentukan nama file model
|
||||
modelFileName := data.NameLower + ".go"
|
||||
modelFilePath := filepath.Join(modelDir, modelFileName)
|
||||
|
||||
// Periksa apakah file model sudah ada
|
||||
if _, err := os.Stat(modelFilePath); err == nil {
|
||||
// File sudah ada, skip pembuatan model
|
||||
fmt.Printf("Model %s already exists, skipping generation\n", data.Name)
|
||||
return
|
||||
}
|
||||
|
||||
// Tentukan import block
|
||||
var importBlock, nullablePrefix string
|
||||
|
||||
if data.Category == "models" {
|
||||
importBlock = `import (
|
||||
"database/sql"
|
||||
@@ -2053,144 +2067,304 @@ func generateModelFile(data HandlerData, modelDir string) {
|
||||
`
|
||||
}
|
||||
|
||||
modelContent := `package ` + data.Category + `
|
||||
var modelContent strings.Builder
|
||||
modelContent.WriteString(fmt.Sprintf("package %s\n\n", data.Category))
|
||||
modelContent.WriteString(importBlock)
|
||||
|
||||
` + importBlock + `
|
||||
// Generate main struct
|
||||
modelContent.WriteString(fmt.Sprintf("// %s represents the data structure for the %s table\n", data.Name, data.TableName))
|
||||
modelContent.WriteString("// with proper null handling and optimized JSON marshaling\n")
|
||||
modelContent.WriteString(fmt.Sprintf("type %s struct {\n", data.Name))
|
||||
|
||||
// ` + data.Name + ` represents the data structure for the ` + data.NameLower + ` table
|
||||
// with proper null handling and optimized JSON marshaling
|
||||
type ` + data.Name + ` struct {
|
||||
ID string ` + "`json:\"id\" db:\"id\"`" + `
|
||||
Status string ` + "`json:\"status\" db:\"status\"`" + `
|
||||
Sort ` + nullablePrefix + "NullableInt32 `json:\"sort,omitempty\" db:\"sort\"`" + `
|
||||
UserCreated sql.NullString ` + "`json:\"user_created,omitempty\" db:\"user_created\"`" + `
|
||||
DateCreated sql.NullTime ` + "`json:\"date_created,omitempty\" db:\"date_created\"`" + `
|
||||
UserUpdated sql.NullString ` + "`json:\"user_updated,omitempty\" db:\"user_updated\"`" + `
|
||||
DateUpdated sql.NullTime ` + "`json:\"date_updated,omitempty\" db:\"date_updated\"`" + `
|
||||
Name sql.NullString ` + "`json:\"name,omitempty\" db:\"name\"`" + `
|
||||
}
|
||||
for _, col := range data.TableSchema {
|
||||
fieldName := snakeToPascal(col.Name)
|
||||
goType, _, _ := mapSQLTypeToGo(col.Type, col.Nullable, col.GoType)
|
||||
jsonTag := snakeToCamel(col.Name)
|
||||
dbTag := col.Name // Gunakan nama kolom langsung
|
||||
jsonTagValue := jsonTag
|
||||
if col.Nullable {
|
||||
jsonTagValue += ",omitempty"
|
||||
}
|
||||
modelContent.WriteString(fmt.Sprintf(" %s %s `json:\"%s\" db:\"%s\"`\n", fieldName, goType, jsonTagValue, dbTag))
|
||||
}
|
||||
modelContent.WriteString("}\n\n")
|
||||
|
||||
// Custom JSON marshaling untuk ` + data.Name + ` agar NULL values tidak muncul di response
|
||||
func (r ` + data.Name + `) MarshalJSON() ([]byte, error) {
|
||||
type Alias ` + data.Name + `
|
||||
aux := &struct {
|
||||
Sort *int ` + "`json:\"sort,omitempty\"`" + `
|
||||
UserCreated *string ` + "`json:\"user_created,omitempty\"`" + `
|
||||
DateCreated *time.Time ` + "`json:\"date_created,omitempty\"`" + `
|
||||
UserUpdated *string ` + "`json:\"user_updated,omitempty\"`" + `
|
||||
DateUpdated *time.Time ` + "`json:\"date_updated,omitempty\"`" + `
|
||||
Name *string ` + "`json:\"name,omitempty\"`" + `
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(&r),
|
||||
}
|
||||
// Generate MarshalJSON method
|
||||
modelContent.WriteString(fmt.Sprintf("// Custom JSON marshaling for %s so NULL values don't appear in response\n", data.Name))
|
||||
modelContent.WriteString(fmt.Sprintf("func (r %s) MarshalJSON() ([]byte, error) {\n", data.Name))
|
||||
modelContent.WriteString(fmt.Sprintf(" type Alias %s\n", data.Name))
|
||||
modelContent.WriteString(" aux := &struct {\n *Alias\n")
|
||||
for _, col := range data.TableSchema {
|
||||
if !col.Nullable {
|
||||
continue
|
||||
}
|
||||
fieldName := snakeToPascal(col.Name)
|
||||
_, baseType, _ := mapSQLTypeToGo(col.Type, col.Nullable, col.GoType)
|
||||
auxType := "*" + baseType
|
||||
jsonTag := snakeToCamel(col.Name) + ",omitempty"
|
||||
modelContent.WriteString(fmt.Sprintf(" %s %s `json:\"%s\"`\n", fieldName, auxType, jsonTag))
|
||||
}
|
||||
modelContent.WriteString(" }{\n Alias: (*Alias)(&r),\n }\n\n")
|
||||
for _, col := range data.TableSchema {
|
||||
if !col.Nullable {
|
||||
continue
|
||||
}
|
||||
fieldName := snakeToPascal(col.Name)
|
||||
_, _, valueType := mapSQLTypeToGo(col.Type, col.Nullable, col.GoType)
|
||||
modelContent.WriteString(fmt.Sprintf(" if r.%s.Valid {\n aux.%s = &r.%s.%s\n }\n", fieldName, fieldName, fieldName, valueType))
|
||||
}
|
||||
modelContent.WriteString(" return json.Marshal(aux)\n}\n\n")
|
||||
|
||||
if r.Sort.Valid {
|
||||
sort := int(r.Sort.Int32)
|
||||
aux.Sort = &sort
|
||||
}
|
||||
if r.UserCreated.Valid {
|
||||
aux.UserCreated = &r.UserCreated.String
|
||||
}
|
||||
if r.DateCreated.Valid {
|
||||
aux.DateCreated = &r.DateCreated.Time
|
||||
}
|
||||
if r.UserUpdated.Valid {
|
||||
aux.UserUpdated = &r.UserUpdated.String
|
||||
}
|
||||
if r.DateUpdated.Valid {
|
||||
aux.DateUpdated = &r.DateUpdated.Time
|
||||
}
|
||||
if r.Name.Valid {
|
||||
aux.Name = &r.Name.String
|
||||
}
|
||||
return json.Marshal(aux)
|
||||
}
|
||||
// Generate helper methods
|
||||
for _, col := range data.TableSchema {
|
||||
if !col.Nullable {
|
||||
continue
|
||||
}
|
||||
fieldName := snakeToPascal(col.Name)
|
||||
_, baseType, valueType := mapSQLTypeToGo(col.Type, col.Nullable, col.GoType)
|
||||
var zeroValue string
|
||||
switch baseType {
|
||||
case "string":
|
||||
zeroValue = `""`
|
||||
case "int32", "int64", "float64":
|
||||
zeroValue = "0"
|
||||
case "bool":
|
||||
zeroValue = "false"
|
||||
case "time.Time":
|
||||
zeroValue = "time.Time{}"
|
||||
default:
|
||||
zeroValue = "nil"
|
||||
}
|
||||
modelContent.WriteString(fmt.Sprintf("// Helper method to safely get %s\n", fieldName))
|
||||
modelContent.WriteString(fmt.Sprintf("func (r *%s) Get%s() %s {\n", data.Name, fieldName, baseType))
|
||||
modelContent.WriteString(fmt.Sprintf(" if r.%s.Valid {\n return r.%s.%s\n }\n return %s\n}\n\n", fieldName, fieldName, valueType, zeroValue))
|
||||
}
|
||||
|
||||
// Helper methods untuk mendapatkan nilai yang aman
|
||||
func (r *` + data.Name + `) GetName() string {
|
||||
if r.Name.Valid {
|
||||
return r.Name.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
`
|
||||
// Generate request/response structs
|
||||
excludedFields := map[string]bool{"id": true, "date_created": true, "date_updated": true, "user_created": true, "user_updated": true}
|
||||
var createFields, updateFields []ColumnConfig
|
||||
for _, col := range data.TableSchema {
|
||||
if excludedFields[strings.ToLower(col.Name)] {
|
||||
continue
|
||||
}
|
||||
createFields = append(createFields, col)
|
||||
updateCol := col
|
||||
updateFields = append(updateFields, updateCol)
|
||||
}
|
||||
|
||||
// Add request/response structs based on enabled methods
|
||||
if data.HasGet {
|
||||
modelContent += `
|
||||
|
||||
// Response struct untuk GET by ID
|
||||
type ` + data.Name + `GetByIDResponse struct {
|
||||
Message string ` + "`json:\"message\"`" + `
|
||||
Data *` + data.Name + ` ` + "`json:\"data\"`" + `
|
||||
modelContent.WriteString(fmt.Sprintf(`// Response struct for GET by ID
|
||||
type %sGetByIDResponse struct {
|
||||
Message string `+"`json:\"message\"`"+`
|
||||
Data *%s `+"`json:\"data\"`"+`
|
||||
}
|
||||
|
||||
// Enhanced GET response dengan pagination dan aggregation
|
||||
type ` + data.Name + `GetResponse struct {
|
||||
Message string ` + "`json:\"message\"`" + `
|
||||
Data []` + data.Name + ` ` + "`json:\"data\"`" + `
|
||||
Meta ` + nullablePrefix + "MetaResponse `json:\"meta\"`" + `
|
||||
Summary *` + nullablePrefix + "AggregateData `json:\"summary,omitempty\"`" + `
|
||||
// Enhanced GET response with pagination and aggregation
|
||||
type %sGetResponse struct {
|
||||
Message string `+"`json:\"message\"`"+`
|
||||
Data []%s `+"`json:\"data\"`"+`
|
||||
Meta %sMetaResponse `+"`json:\"meta\"`"+`
|
||||
Summary *%sAggregateData `+"`json:\"summary,omitempty\"`"+`
|
||||
}
|
||||
`
|
||||
`, data.Name, data.Name, data.Name, data.Name, nullablePrefix, nullablePrefix))
|
||||
}
|
||||
if data.HasPost {
|
||||
modelContent += `
|
||||
|
||||
// Request struct untuk create
|
||||
type ` + data.Name + `CreateRequest struct {
|
||||
Status string ` + "`json:\"status\" validate:\"required,oneof=draft active inactive\"`" + `
|
||||
Name *string ` + "`json:\"name,omitempty\" validate:\"omitempty,min=1,max=255\"`" + `
|
||||
modelContent.WriteString(fmt.Sprintf("\n// Request struct for create\ntype %sCreateRequest struct {\n", data.Name))
|
||||
for _, col := range createFields {
|
||||
fieldName := snakeToPascal(col.Name)
|
||||
_, baseType, _ := mapSQLTypeToGo(col.Type, col.Nullable, col.GoType)
|
||||
jsonTag := snakeToCamel(col.Name)
|
||||
var requestType string
|
||||
if col.Nullable {
|
||||
requestType = "*" + baseType
|
||||
} else {
|
||||
requestType = baseType
|
||||
}
|
||||
modelContent.WriteString(fmt.Sprintf(" %s %s `json:\"%s\"`\n", fieldName, requestType, jsonTag))
|
||||
}
|
||||
modelContent.WriteString("}\n\n")
|
||||
modelContent.WriteString(fmt.Sprintf(`// Response struct for create
|
||||
type %sCreateResponse struct {
|
||||
Message string `+"`json:\"message\"`"+`
|
||||
Data *%s `+"`json:\"data\"`"+`
|
||||
}
|
||||
|
||||
// Response struct untuk create
|
||||
type ` + data.Name + `CreateResponse struct {
|
||||
Message string ` + "`json:\"message\"`" + `
|
||||
Data *` + data.Name + ` ` + "`json:\"data\"`" + `
|
||||
}
|
||||
`
|
||||
`, data.Name, data.Name))
|
||||
}
|
||||
if data.HasPut {
|
||||
modelContent += `
|
||||
// 1. Bangun string tag terlebih dahulu
|
||||
idTagContent := `json:"-" validate:"required,uuid4"`
|
||||
|
||||
// Update request
|
||||
type ` + data.Name + `UpdateRequest struct {
|
||||
ID string ` + "`json:\"-\" validate:\"required,uuid4\"`" + `
|
||||
Status string ` + "`json:\"status\" validate:\"required,oneof=draft active inactive\"`" + `
|
||||
Name *string ` + "`json:\"name,omitempty\" validate:\"omitempty,min=1,max=255\"`" + `
|
||||
}
|
||||
// 2. Gunakan strconv.Quote untuk membuatnya menjadi literal yang valid
|
||||
// Hasilnya akan menjadi: "`json:\"-\" validate:\"required,uuid4\"`"
|
||||
quotedIdTag := strconv.Quote(idTagContent)
|
||||
|
||||
// Response struct untuk update
|
||||
type ` + data.Name + `UpdateResponse struct {
|
||||
Message string ` + "`json:\"message\"`" + `
|
||||
Data *` + data.Name + ` ` + "`json:\"data\"`" + `
|
||||
}
|
||||
`
|
||||
// 3. Gunakan tag yang sudah di-"quote" dalam fmt.Sprintf
|
||||
modelContent.WriteString(fmt.Sprintf("\n// Update request\ntype %sUpdateRequest struct {\n ID string %s\n", data.Name, quotedIdTag))
|
||||
|
||||
for _, col := range updateFields {
|
||||
fieldName := snakeToPascal(col.Name)
|
||||
_, baseType, _ := mapSQLTypeToGo(col.Type, col.Nullable, col.GoType)
|
||||
jsonTag := snakeToCamel(col.Name)
|
||||
var requestType string
|
||||
if col.Nullable {
|
||||
requestType = "*" + baseType
|
||||
} else {
|
||||
requestType = baseType
|
||||
}
|
||||
modelContent.WriteString(fmt.Sprintf(" %s %s `json:\"%s\"`\n", fieldName, requestType, jsonTag))
|
||||
}
|
||||
modelContent.WriteString("}\n\n")
|
||||
modelContent.WriteString(fmt.Sprintf(`// Response struct for update
|
||||
type %sUpdateResponse struct {
|
||||
Message string `+"`json:\"message\"`"+`
|
||||
Data *%s `+"`json:\"data\"`"+`
|
||||
}
|
||||
`, data.Name, data.Name))
|
||||
}
|
||||
if data.HasDelete {
|
||||
modelContent += `
|
||||
|
||||
// Response struct untuk delete
|
||||
type ` + data.Name + `DeleteResponse struct {
|
||||
Message string ` + "`json:\"message\"`" + `
|
||||
ID string ` + "`json:\"id\"`" + `
|
||||
modelContent.WriteString(fmt.Sprintf(`// Response struct for delete
|
||||
type %sDeleteResponse struct {
|
||||
Message string `+"`json:\"message\"`"+`
|
||||
ID string `+"`json:\"id\"`"+`
|
||||
}
|
||||
`
|
||||
`, data.Name))
|
||||
}
|
||||
if data.HasFilter {
|
||||
modelContent.WriteString(fmt.Sprintf("\n// Filter struct for query parameters\ntype %sFilter struct {\n", data.Name))
|
||||
modelContent.WriteString(" Search *string `json:\"search,omitempty\" form:\"search\"`\n")
|
||||
modelContent.WriteString(" DateFrom *time.Time `json:\"date_from,omitempty\" form:\"date_from\"`\n")
|
||||
modelContent.WriteString(" DateTo *time.Time `json:\"date_to,omitempty\" form:\"date_to\"`\n")
|
||||
for _, col := range data.TableSchema {
|
||||
lowerName := strings.ToLower(col.Name)
|
||||
if strings.Contains(lowerName, "status") {
|
||||
modelContent.WriteString(fmt.Sprintf(" Status *string `json:\"status,omitempty\" form:\"%s\"`\n", col.Name))
|
||||
}
|
||||
}
|
||||
modelContent.WriteString("}\n")
|
||||
}
|
||||
// Add filter struct
|
||||
modelContent += `
|
||||
|
||||
// Filter struct untuk query parameters
|
||||
type ` + data.Name + `Filter struct {
|
||||
Status *string ` + "`json:\"status,omitempty\" form:\"status\"`" + `
|
||||
Search *string ` + "`json:\"search,omitempty\" form:\"search\"`" + `
|
||||
DateFrom *time.Time ` + "`json:\"date_from,omitempty\" form:\"date_from\"`" + `
|
||||
DateTo *time.Time ` + "`json:\"date_to,omitempty\" form:\"date_to\"`" + `
|
||||
writeFile(modelFilePath, modelContent.String())
|
||||
fmt.Printf("Successfully generated DYNAMIC model: %s\n", modelFileName)
|
||||
}
|
||||
`
|
||||
writeFile(modelFilePath, modelContent)
|
||||
fmt.Printf("Successfully generated model: %s\n", modelFileName)
|
||||
|
||||
// MODIFIKASI: Fungsi pembantu untuk memetakan tipe SQL ke Go
|
||||
func mapSQLTypeToGo(sqlType string, nullable bool, explicitGoType string) (goType string, baseType string, valueType string) {
|
||||
sqlType = strings.ToLower(sqlType)
|
||||
if explicitGoType != "" {
|
||||
baseType = explicitGoType
|
||||
if nullable {
|
||||
switch baseType {
|
||||
case "string":
|
||||
goType, valueType = "sql.NullString", "String"
|
||||
case "int32":
|
||||
goType, valueType = "sql.NullInt32", "Int32"
|
||||
case "int64":
|
||||
goType, valueType = "sql.NullInt64", "Int64"
|
||||
case "bool":
|
||||
goType, valueType = "sql.NullBool", "Bool"
|
||||
case "float64":
|
||||
goType, valueType = "sql.NullFloat64", "Float64"
|
||||
case "time.Time":
|
||||
goType, valueType = "sql.NullTime", "Time"
|
||||
default:
|
||||
goType, valueType = "sql.NullString", "String" // fallback
|
||||
}
|
||||
} else {
|
||||
goType = baseType
|
||||
}
|
||||
return goType, baseType, valueType
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.Contains(sqlType, "serial") || sqlType == "int4":
|
||||
if nullable {
|
||||
return "sql.NullInt32", "int32", "Int32"
|
||||
}
|
||||
return "int32", "int32", ""
|
||||
case sqlType == "int8" || sqlType == "bigint":
|
||||
if nullable {
|
||||
return "sql.NullInt64", "int64", "Int64"
|
||||
}
|
||||
return "int64", "int64", ""
|
||||
case sqlType == "uuid":
|
||||
if nullable {
|
||||
return "sql.NullString", "string", "String"
|
||||
}
|
||||
return "string", "string", ""
|
||||
case strings.Contains(sqlType, "varchar") || strings.Contains(sqlType, "text") || sqlType == "char":
|
||||
if nullable {
|
||||
return "sql.NullString", "string", "String"
|
||||
}
|
||||
return "string", "string", ""
|
||||
case sqlType == "bool":
|
||||
if nullable {
|
||||
return "sql.NullBool", "bool", "Bool"
|
||||
}
|
||||
return "bool", "bool", ""
|
||||
case strings.Contains(sqlType, "timestamp") || strings.Contains(sqlType, "date"):
|
||||
if nullable {
|
||||
return "sql.NullTime", "time.Time", "Time"
|
||||
}
|
||||
return "time.Time", "time.Time", ""
|
||||
case strings.Contains(sqlType, "decimal") || strings.Contains(sqlType, "numeric") || sqlType == "float8" || sqlType == "real":
|
||||
if nullable {
|
||||
return "sql.NullFloat64", "float64", "Float64"
|
||||
}
|
||||
return "float64", "float64", ""
|
||||
default:
|
||||
if nullable {
|
||||
return "sql.NullString", "string", "String"
|
||||
}
|
||||
return "string", "string", ""
|
||||
}
|
||||
}
|
||||
func snakeToPascal(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
// Pisahkan string berdasarkan underscore
|
||||
parts := strings.Split(s, "_")
|
||||
var pascal strings.Builder
|
||||
|
||||
for _, part := range parts {
|
||||
if part == "" {
|
||||
continue // Lewati bagian kosong jika ada double underscore
|
||||
}
|
||||
lowerPart := strings.ToLower(part)
|
||||
// Handle common acronyms
|
||||
if lowerPart == "id" {
|
||||
pascal.WriteString("ID")
|
||||
} else {
|
||||
// Ubah huruf pertama menjadi kapital dan sisanya kecil
|
||||
pascal.WriteString(strings.Title(lowerPart))
|
||||
}
|
||||
}
|
||||
return pascal.String()
|
||||
}
|
||||
|
||||
// snakeToCamel mengubah string dari snake_case menjadi camelCase.
|
||||
// Contoh: "nama_hari" -> "namaHari"
|
||||
func snakeToCamel(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
parts := strings.Split(s, "_")
|
||||
if len(parts) == 1 {
|
||||
return strings.ToLower(parts[0])
|
||||
}
|
||||
|
||||
// Bagian pertama: huruf pertama kecil
|
||||
var camel strings.Builder
|
||||
camel.WriteString(strings.ToLower(parts[0]))
|
||||
|
||||
// Bagian selanjutnya: huruf pertama besar (PascalCase)
|
||||
for i := 1; i < len(parts); i++ {
|
||||
if parts[i] == "" {
|
||||
continue
|
||||
}
|
||||
camel.WriteString(strings.Title(strings.ToLower(parts[i])))
|
||||
}
|
||||
return camel.String()
|
||||
}
|
||||
|
||||
// ================= ROUTES GENERATION =====================
|
||||
|
||||
@@ -15,141 +15,141 @@ services:
|
||||
retry_count: 3
|
||||
|
||||
endpoints:
|
||||
# retribusi:
|
||||
# description: "Retribusi tariff management"
|
||||
# handler_folder: "retribusi"
|
||||
# handler_file: "retribusi.go"
|
||||
# handler_name: "Retribusi"
|
||||
# table_name: "data_retribusi"
|
||||
# functions:
|
||||
# list:
|
||||
# methods: ["GET"]
|
||||
# path: "/"
|
||||
# get_routes: "/"
|
||||
# get_path: "/"
|
||||
# model: "Retribusi"
|
||||
# response_model: "RetribusiGetResponse"
|
||||
# description: "Get retribusi list with pagination and filters"
|
||||
# summary: "Get Retribusi List"
|
||||
# tags: ["Retribusi"]
|
||||
# require_auth: true
|
||||
# cache_enabled: true
|
||||
# enable_database: true
|
||||
# cache_ttl: 300
|
||||
# has_pagination: true
|
||||
# has_filter: true
|
||||
# has_search: true
|
||||
# has_stats: true
|
||||
retribusi:
|
||||
description: "Retribusi tariff management"
|
||||
handler_folder: "retribusi"
|
||||
handler_file: "retribusi.go"
|
||||
handler_name: "Retribusi"
|
||||
table_name: "data_retribusi"
|
||||
functions:
|
||||
list:
|
||||
methods: ["GET"]
|
||||
path: "/"
|
||||
get_routes: "/"
|
||||
get_path: "/"
|
||||
model: "Retribusi"
|
||||
response_model: "RetribusiGetResponse"
|
||||
description: "Get retribusi list with pagination and filters"
|
||||
summary: "Get Retribusi List"
|
||||
tags: ["Retribusi"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
enable_database: true
|
||||
cache_ttl: 300
|
||||
has_pagination: true
|
||||
has_filter: true
|
||||
has_search: true
|
||||
has_stats: true
|
||||
|
||||
# get:
|
||||
# methods: ["GET"]
|
||||
# path: "/:id"
|
||||
# get_routes: "/:id"
|
||||
# get_path: "/:id"
|
||||
# model: "Retribusi"
|
||||
# response_model: "RetribusiGetByIDResponse"
|
||||
# description: "Get retribusi by ID"
|
||||
# summary: "Get Retribusi by ID"
|
||||
# tags: ["Retribusi"]
|
||||
# require_auth: true
|
||||
# cache_enabled: true
|
||||
# enable_database: true
|
||||
# cache_ttl: 300
|
||||
get:
|
||||
methods: ["GET"]
|
||||
path: "/:id"
|
||||
get_routes: "/:id"
|
||||
get_path: "/:id"
|
||||
model: "Retribusi"
|
||||
response_model: "RetribusiGetByIDResponse"
|
||||
description: "Get retribusi by ID"
|
||||
summary: "Get Retribusi by ID"
|
||||
tags: ["Retribusi"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
enable_database: true
|
||||
cache_ttl: 300
|
||||
|
||||
# dynamic:
|
||||
# methods: ["GET"]
|
||||
# path: "/dynamic"
|
||||
# get_routes: "/dynamic"
|
||||
# get_path: "/dynamic"
|
||||
# model: "Retribusi"
|
||||
# response_model: "RetribusiGetResponse"
|
||||
# description: "Get retribusi with dynamic filtering"
|
||||
# summary: "Get Retribusi Dynamic"
|
||||
# tags: ["Retribusi"]
|
||||
# require_auth: true
|
||||
# cache_enabled: true
|
||||
# enable_database: true
|
||||
# cache_ttl: 300
|
||||
# has_dynamic: true
|
||||
dynamic:
|
||||
methods: ["GET"]
|
||||
path: "/dynamic"
|
||||
get_routes: "/dynamic"
|
||||
get_path: "/dynamic"
|
||||
model: "Retribusi"
|
||||
response_model: "RetribusiGetResponse"
|
||||
description: "Get retribusi with dynamic filtering"
|
||||
summary: "Get Retribusi Dynamic"
|
||||
tags: ["Retribusi"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
enable_database: true
|
||||
cache_ttl: 300
|
||||
has_dynamic: true
|
||||
|
||||
# search:
|
||||
# methods: ["GET"]
|
||||
# path: "/search"
|
||||
# get_routes: "/search"
|
||||
# get_path: "/search"
|
||||
# model: "Retribusi"
|
||||
# response_model: "RetribusiGetResponse"
|
||||
# description: "Search retribusi"
|
||||
# summary: "Search Retribusi"
|
||||
# tags: ["Retribusi"]
|
||||
# require_auth: true
|
||||
# cache_enabled: true
|
||||
# enable_database: true
|
||||
# cache_ttl: 300
|
||||
# has_search: true
|
||||
search:
|
||||
methods: ["GET"]
|
||||
path: "/search"
|
||||
get_routes: "/search"
|
||||
get_path: "/search"
|
||||
model: "Retribusi"
|
||||
response_model: "RetribusiGetResponse"
|
||||
description: "Search retribusi"
|
||||
summary: "Search Retribusi"
|
||||
tags: ["Retribusi"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
enable_database: true
|
||||
cache_ttl: 300
|
||||
has_search: true
|
||||
|
||||
# create:
|
||||
# methods: ["POST"]
|
||||
# path: "/"
|
||||
# post_routes: "/"
|
||||
# post_path: "/"
|
||||
# model: "RetribusiCreateRequest"
|
||||
# response_model: "RetribusiCreateResponse"
|
||||
# request_model: "RetribusiCreateRequest"
|
||||
# description: "Create new retribusi"
|
||||
# summary: "Create Retribusi"
|
||||
# tags: ["Retribusi"]
|
||||
# require_auth: true
|
||||
# cache_enabled: false
|
||||
# enable_database: true
|
||||
# cache_ttl: 0
|
||||
create:
|
||||
methods: ["POST"]
|
||||
path: "/"
|
||||
post_routes: "/"
|
||||
post_path: "/"
|
||||
model: "RetribusiCreateRequest"
|
||||
response_model: "RetribusiCreateResponse"
|
||||
request_model: "RetribusiCreateRequest"
|
||||
description: "Create new retribusi"
|
||||
summary: "Create Retribusi"
|
||||
tags: ["Retribusi"]
|
||||
require_auth: true
|
||||
cache_enabled: false
|
||||
enable_database: true
|
||||
cache_ttl: 0
|
||||
|
||||
# update:
|
||||
# methods: ["PUT"]
|
||||
# path: "/:id"
|
||||
# put_routes: "/:id"
|
||||
# put_path: "/:id"
|
||||
# model: "RetribusiUpdateRequest"
|
||||
# response_model: "RetribusiUpdateResponse"
|
||||
# request_model: "RetribusiUpdateRequest"
|
||||
# description: "Update retribusi"
|
||||
# summary: "Update Retribusi"
|
||||
# tags: ["Retribusi"]
|
||||
# require_auth: true
|
||||
# cache_enabled: false
|
||||
# enable_database: true
|
||||
# cache_ttl: 0
|
||||
update:
|
||||
methods: ["PUT"]
|
||||
path: "/:id"
|
||||
put_routes: "/:id"
|
||||
put_path: "/:id"
|
||||
model: "RetribusiUpdateRequest"
|
||||
response_model: "RetribusiUpdateResponse"
|
||||
request_model: "RetribusiUpdateRequest"
|
||||
description: "Update retribusi"
|
||||
summary: "Update Retribusi"
|
||||
tags: ["Retribusi"]
|
||||
require_auth: true
|
||||
cache_enabled: false
|
||||
enable_database: true
|
||||
cache_ttl: 0
|
||||
|
||||
# delete:
|
||||
# methods: ["DELETE"]
|
||||
# path: "/:id"
|
||||
# delete_routes: "/:id"
|
||||
# delete_path: "/:id"
|
||||
# model: "Retribusi"
|
||||
# response_model: "RetribusiDeleteResponse"
|
||||
# description: "Delete retribusi"
|
||||
# summary: "Delete Retribusi"
|
||||
# tags: ["Retribusi"]
|
||||
# require_auth: true
|
||||
# cache_enabled: false
|
||||
# enable_database: true
|
||||
# cache_ttl: 0
|
||||
delete:
|
||||
methods: ["DELETE"]
|
||||
path: "/:id"
|
||||
delete_routes: "/:id"
|
||||
delete_path: "/:id"
|
||||
model: "Retribusi"
|
||||
response_model: "RetribusiDeleteResponse"
|
||||
description: "Delete retribusi"
|
||||
summary: "Delete Retribusi"
|
||||
tags: ["Retribusi"]
|
||||
require_auth: true
|
||||
cache_enabled: false
|
||||
enable_database: true
|
||||
cache_ttl: 0
|
||||
|
||||
# stats:
|
||||
# methods: ["GET"]
|
||||
# path: "/stats"
|
||||
# get_routes: "/stats"
|
||||
# get_path: "/stats"
|
||||
# model: "AggregateData"
|
||||
# response_model: "AggregateData"
|
||||
# description: "Get retribusi statistics"
|
||||
# summary: "Get Retribusi Stats"
|
||||
# tags: ["Retribusi"]
|
||||
# require_auth: true
|
||||
# cache_enabled: true
|
||||
# enable_database: true
|
||||
# cache_ttl: 180
|
||||
# has_stats: true
|
||||
stats:
|
||||
methods: ["GET"]
|
||||
path: "/stats"
|
||||
get_routes: "/stats"
|
||||
get_path: "/stats"
|
||||
model: "AggregateData"
|
||||
response_model: "AggregateData"
|
||||
description: "Get retribusi statistics"
|
||||
summary: "Get Retribusi Stats"
|
||||
tags: ["Retribusi"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
enable_database: true
|
||||
cache_ttl: 180
|
||||
has_stats: true
|
||||
|
||||
# Example of another service
|
||||
user:
|
||||
@@ -248,3 +248,180 @@ services:
|
||||
cache_enabled: false
|
||||
enable_database: true
|
||||
cache_ttl: 0
|
||||
|
||||
search:
|
||||
methods: ["GET"]
|
||||
path: "/search"
|
||||
get_routes: "/search"
|
||||
get_path: "/search"
|
||||
model: "User"
|
||||
response_model: "UserGetResponse"
|
||||
description: "Search user"
|
||||
summary: "Search User"
|
||||
tags: ["User"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
enable_database: true
|
||||
cache_ttl: 300
|
||||
has_search: true
|
||||
|
||||
schedule:
|
||||
name: "Jadwal Dokter"
|
||||
category: "schedule"
|
||||
package: "schedule"
|
||||
description: "Jadwal Dokter management"
|
||||
base_url: ""
|
||||
timeout: 30
|
||||
retry_count: 3
|
||||
|
||||
endpoints:
|
||||
schedule:
|
||||
description: "Jadwal dokter management"
|
||||
handler_folder: "master"
|
||||
handler_file: "schedule.go"
|
||||
handler_name: "schedule"
|
||||
table_name: "daftar_jadwal_dokter"
|
||||
# Definisikan skema tabel di sini
|
||||
# =
|
||||
schema:
|
||||
columns:
|
||||
- name: "id"
|
||||
type: "serial4"
|
||||
primary_key: true
|
||||
go_type: "string" # Override tipe Go, UUID biasanya string
|
||||
- name: "Hari"
|
||||
type: "int4"
|
||||
nullable: true
|
||||
- name: "Nama_hari"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
- name: "Waktu"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
- name: "Dokter"
|
||||
type: "uuid"
|
||||
nullable: true
|
||||
go_type: "string" # Override tipe Go
|
||||
- name: "Spesialis"
|
||||
type: "int4"
|
||||
nullable: true
|
||||
- name: "Sub_spesialis"
|
||||
type: "int4"
|
||||
nullable: true
|
||||
- name: "Status"
|
||||
type: "int4"
|
||||
nullable: true
|
||||
# ======================================================================
|
||||
functions:
|
||||
list:
|
||||
methods: ["GET"]
|
||||
path: "/"
|
||||
get_routes: "/"
|
||||
get_path: "/"
|
||||
model: "Schedule"
|
||||
response_model: "ScheduleGetResponse"
|
||||
description: "Get schedule list with pagination and filters"
|
||||
summary: "Get Schedule List"
|
||||
tags: ["Schedule"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
enable_database: true
|
||||
cache_ttl: 300
|
||||
has_pagination: true
|
||||
has_filter: true
|
||||
has_search: true
|
||||
has_stats: true
|
||||
|
||||
get:
|
||||
methods: ["GET"]
|
||||
path: "/:id"
|
||||
get_routes: "/:id"
|
||||
get_path: "/:id"
|
||||
model: "Schedule"
|
||||
response_model: "ScheduleGetByIDResponse"
|
||||
description: "Get schedule by ID"
|
||||
summary: "Get schedule by ID"
|
||||
tags: ["Schedule"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
enable_database: true
|
||||
cache_ttl: 300
|
||||
|
||||
search:
|
||||
methods: ["GET"]
|
||||
path: "/search"
|
||||
get_routes: "/search"
|
||||
get_path: "/search"
|
||||
model: "Schedule"
|
||||
response_model: "ScheduleGetResponse"
|
||||
description: "Search schedule"
|
||||
summary: "Search Schedule"
|
||||
tags: ["Schedule"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
enable_database: true
|
||||
cache_ttl: 300
|
||||
has_search: true
|
||||
|
||||
create:
|
||||
methods: ["POST"]
|
||||
path: "/"
|
||||
post_routes: "/"
|
||||
post_path: "/"
|
||||
model: "Schedule"
|
||||
response_model: "ScheduleCreateResponse"
|
||||
request_model: "ScheduleCreateRequest"
|
||||
description: "Create new schedule"
|
||||
summary: "Create Schedule"
|
||||
tags: ["Schedule"]
|
||||
require_auth: true
|
||||
cache_enabled: false
|
||||
enable_database: true
|
||||
cache_ttl: 0
|
||||
|
||||
update:
|
||||
methods: ["PUT"]
|
||||
path: "/:id"
|
||||
put_routes: "/:id"
|
||||
put_path: "/:id"
|
||||
model: "Schedule"
|
||||
response_model: "ScheduleUpdateResponse"
|
||||
request_model: "ScheduleUpdateRequest"
|
||||
description: "Update schedule"
|
||||
summary: "Update Schedule"
|
||||
tags: ["Schedule"]
|
||||
require_auth: true
|
||||
cache_enabled: false
|
||||
enable_database: true
|
||||
cache_ttl: 0
|
||||
|
||||
delete:
|
||||
methods: ["DELETE"]
|
||||
path: "/:id"
|
||||
delete_routes: "/:id"
|
||||
delete_path: "/:id"
|
||||
model: "Schedule"
|
||||
response_model: "ScheduleDeleteResponse"
|
||||
description: "Delete schedule"
|
||||
summary: "Delete Schedule"
|
||||
tags: ["Schedule"]
|
||||
require_auth: true
|
||||
cache_enabled: false
|
||||
enable_database: true
|
||||
cache_ttl: 0
|
||||
|
||||
stats:
|
||||
methods: ["GET"]
|
||||
path: "/stats"
|
||||
get_routes: "/stats"
|
||||
get_path: "/stats"
|
||||
model: "AggregateData"
|
||||
response_model: "AggregateData"
|
||||
description: "Get retribusi statistics"
|
||||
summary: "Get Retribusi Stats"
|
||||
tags: ["Retribusi"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
enable_database: true
|
||||
cache_ttl: 180
|
||||
has_stats: true
|
||||
Reference in New Issue
Block a user