From f253e65736e6f3c2bd05f351dcc1ba616d04efbf Mon Sep 17 00:00:00 2001 From: Meninjar Mulyono Date: Mon, 20 Oct 2025 09:46:08 +0700 Subject: [PATCH] Perbaikan generate --- tools/general/generate-handler.go | 450 ++++++++++++++++++++--------- tools/general/services-config.yaml | 433 +++++++++++++++++++-------- 2 files changed, 617 insertions(+), 266 deletions(-) diff --git a/tools/general/generate-handler.go b/tools/general/generate-handler.go index bd0408c..da14b9d 100644 --- a/tools/general/generate-handler.go +++ b/tools/general/generate-handler.go @@ -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 ===================== diff --git a/tools/general/services-config.yaml b/tools/general/services-config.yaml index 40944b3..3811f61 100644 --- a/tools/general/services-config.yaml +++ b/tools/general/services-config.yaml @@ -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 \ No newline at end of file