Enhance OpenAPI and JSON Parsing Functionality
- Added app handlers to the OpenAPI attachment process, improving routing for application-related endpoints. - Refactored the parseJSONField function to handle both JSON and boolean column types, enhancing value parsing flexibility and robustness. - Improved error handling and clarity in the JSON parsing logic, ensuring original values are returned when parsing fails.
This commit is contained in:
parent
4d1b427634
commit
d3ed830ec1
4 changed files with 128 additions and 16 deletions
91
openapi/app/app.go
Normal file
91
openapi/app/app.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yaoapp/gou/process"
|
||||
"github.com/yaoapp/yao/openapi/oauth/authorized"
|
||||
"github.com/yaoapp/yao/openapi/oauth/types"
|
||||
"github.com/yaoapp/yao/openapi/response"
|
||||
)
|
||||
|
||||
// Attach attaches the app handlers to the router
|
||||
func Attach(group *gin.RouterGroup, oauth types.OAuth) {
|
||||
// Menu endpoint - requires authentication
|
||||
group.GET("/menu", oauth.Guard, getMenu)
|
||||
}
|
||||
|
||||
// MenuRequest represents the menu request parameters
|
||||
type MenuRequest struct {
|
||||
Locale string `form:"locale" json:"locale"`
|
||||
}
|
||||
|
||||
// getMenu handles GET /app/menu
|
||||
// Returns the application menu based on user permissions and locale
|
||||
func getMenu(c *gin.Context) {
|
||||
var req MenuRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
response.RespondWithError(c, http.StatusBadRequest, &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get authorized info from context (set by oauth.Guard)
|
||||
authInfo := authorized.GetInfo(c)
|
||||
if authInfo == nil {
|
||||
response.RespondWithError(c, http.StatusUnauthorized, &response.ErrorResponse{
|
||||
Code: response.ErrInvalidToken.Code,
|
||||
ErrorDescription: "Authorization required",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Call yao.app.Menu process with locale parameter
|
||||
handle, err := process.Of("yao.app.Menu", req.Locale)
|
||||
if err != nil {
|
||||
response.RespondWithError(c, http.StatusBadRequest, &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Set process context
|
||||
handle.WithSID(authInfo.SessionID)
|
||||
|
||||
// Set authorized info for process
|
||||
handle.WithAuthorized(map[string]interface{}{
|
||||
"subject": authInfo.Subject,
|
||||
"client_id": authInfo.ClientID,
|
||||
"user_id": authInfo.UserID,
|
||||
"scope": authInfo.Scope,
|
||||
"team_id": authInfo.TeamID,
|
||||
"tenant_id": authInfo.TenantID,
|
||||
"session_id": authInfo.SessionID,
|
||||
"remember_me": authInfo.RememberMe,
|
||||
"constraints": map[string]interface{}{
|
||||
"owner_only": authInfo.Constraints.OwnerOnly,
|
||||
"creator_only": authInfo.Constraints.CreatorOnly,
|
||||
"editor_only": authInfo.Constraints.EditorOnly,
|
||||
"team_only": authInfo.Constraints.TeamOnly,
|
||||
"extra": authInfo.Constraints.Extra,
|
||||
},
|
||||
})
|
||||
|
||||
// Execute the process
|
||||
err = handle.Execute()
|
||||
if err != nil {
|
||||
response.RespondWithError(c, http.StatusInternalServerError, &response.ErrorResponse{
|
||||
Code: response.ErrServerError.Code,
|
||||
ErrorDescription: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
defer handle.Dispose()
|
||||
|
||||
// Return the menu data
|
||||
response.RespondWithSuccess(c, http.StatusOK, handle.Value())
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/yaoapp/gou/application"
|
||||
"github.com/yaoapp/yao/config"
|
||||
"github.com/yaoapp/yao/openapi/agent"
|
||||
"github.com/yaoapp/yao/openapi/app"
|
||||
"github.com/yaoapp/yao/openapi/captcha"
|
||||
"github.com/yaoapp/yao/openapi/chat"
|
||||
"github.com/yaoapp/yao/openapi/dsl"
|
||||
|
|
@ -150,6 +151,9 @@ func (openapi *OpenAPI) Attach(router *gin.Engine) {
|
|||
// Trace handlers
|
||||
openapiTrace.Attach(group.Group("/trace"), openapi.OAuth)
|
||||
|
||||
// App handlers (menu, etc.)
|
||||
app.Attach(group.Group("/app"), openapi.OAuth)
|
||||
|
||||
// Custom handlers (Defined by developer)
|
||||
|
||||
}
|
||||
|
|
|
|||
32
seed/seed.go
32
seed/seed.go
|
|
@ -572,17 +572,14 @@ func buildColumnTypeMap(mod *model.Model, header []string) []string {
|
|||
return columnTypes
|
||||
}
|
||||
|
||||
// parseJSONField attempts to parse a value as JSON if the column type is json
|
||||
// Returns the parsed JSON object if successful, otherwise returns the original value
|
||||
// parseJSONField attempts to parse a value based on column type
|
||||
// For JSON columns: parses JSON string to object
|
||||
// For boolean columns: converts "true"/"false"/"1"/"0" to bool
|
||||
// Returns the parsed value if successful, otherwise returns the original value
|
||||
func parseJSONField(value interface{}, columnType string) interface{} {
|
||||
// Check if column type is JSON
|
||||
if columnType != "json" && columnType != "jsonb" {
|
||||
return value
|
||||
}
|
||||
|
||||
// Try to parse string value as JSON
|
||||
// Try to parse string value
|
||||
strValue, ok := value.(string)
|
||||
if !ok || strValue == "" {
|
||||
if !ok {
|
||||
return value
|
||||
}
|
||||
|
||||
|
|
@ -592,6 +589,19 @@ func parseJSONField(value interface{}, columnType string) interface{} {
|
|||
return value
|
||||
}
|
||||
|
||||
// Handle boolean type
|
||||
if columnType == "boolean" || columnType == "bool" {
|
||||
switch strings.ToLower(strValue) {
|
||||
case "true", "1", "yes":
|
||||
return true
|
||||
case "false", "0", "no":
|
||||
return false
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// Handle JSON type
|
||||
if columnType == "json" || columnType == "jsonb" {
|
||||
// Try to parse as JSON
|
||||
var jsonValue interface{}
|
||||
if err := json.Unmarshal([]byte(strValue), &jsonValue); err != nil {
|
||||
|
|
@ -599,8 +609,10 @@ func parseJSONField(value interface{}, columnType string) interface{} {
|
|||
// Don't log error as this is expected for non-JSON strings
|
||||
return value
|
||||
}
|
||||
|
||||
return jsonValue
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// sortColumns sorts column names alphabetically for consistent ordering
|
||||
|
|
|
|||
|
|
@ -396,7 +396,12 @@ func processMenu(p *process.Process) interface{} {
|
|||
exception.New(err.Error(), 400).Throw()
|
||||
}
|
||||
|
||||
err = handle.WithGlobal(p.Global).WithSID(p.Sid).Execute()
|
||||
handle.WithGlobal(p.Global).WithSID(p.Sid)
|
||||
if p.Authorized != nil {
|
||||
handle = handle.WithAuthorized(p.Authorized)
|
||||
}
|
||||
|
||||
err = handle.Execute()
|
||||
if err != nil {
|
||||
exception.New(err.Error(), 500).Throw()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue