coze-studio/backend/domain/memory/variables/entity/variable_meta_schema.go

176 lines
5.4 KiB
Go

/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package entity
import (
"context"
"encoding/json"
"regexp"
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
"github.com/coze-dev/coze-studio/backend/types/errno"
)
const (
variableMetaSchemaTypeObject = "object"
variableMetaSchemaTypeArray = "list"
variableMetaSchemaTypeInteger = "integer"
variableMetaSchemaTypeString = "string"
variableMetaSchemaTypeBoolean = "boolean"
variableMetaSchemaTypeNumber = "float"
)
type VariableMetaSchema struct {
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Readonly bool `json:"readonly,omitempty"`
Enable bool `json:"enable,omitempty"`
Schema json.RawMessage `json:"schema,omitempty"`
}
func NewVariableMetaSchema(schema []byte) (*VariableMetaSchema, error) {
schemaObj := &VariableMetaSchema{}
err := json.Unmarshal(schema, schemaObj)
if err != nil {
return nil, errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "schema json invalid: %s \n json = %s", err.Error(), string(schema)))
}
return schemaObj, nil
}
func (v *VariableMetaSchema) IsArrayType() bool {
return v.Type == variableMetaSchemaTypeArray
}
// GetArrayType e.g. schema = {"type":"int"}
func (v *VariableMetaSchema) GetArrayType(schema []byte) (string, error) {
schemaObj, err := NewVariableMetaSchema(schema)
if err != nil {
return "", errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "NewVariableMetaSchema failed, %v", err.Error()))
}
if schemaObj.Type == "" {
return "", errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "array type not found in %s", schema))
}
return schemaObj.Type, nil
}
func (v *VariableMetaSchema) IsStringType() bool {
return v.Type == variableMetaSchemaTypeString
}
func (v *VariableMetaSchema) IsIntegerType() bool {
return v.Type == variableMetaSchemaTypeInteger
}
func (v *VariableMetaSchema) IsBooleanType() bool {
return v.Type == variableMetaSchemaTypeBoolean
}
func (v *VariableMetaSchema) IsNumberType() bool {
return v.Type == variableMetaSchemaTypeNumber
}
func (v *VariableMetaSchema) IsObjectType() bool {
return v.Type == variableMetaSchemaTypeObject
}
// GetObjectProperties e.g. schema = [{"name":"app_var_12_sdd","enable":true,"description":"s22","type":"string","readonly":false,"schema":""}]
func (v *VariableMetaSchema) GetObjectProperties(schema []byte) (map[string]*VariableMetaSchema, error) {
schemas := make([]*VariableMetaSchema, 0)
err := json.Unmarshal(schema, &schemas)
if err != nil {
return nil, errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KV("msg", "schema array content json invalid"))
}
properties := make(map[string]*VariableMetaSchema)
for _, schemaObj := range schemas {
properties[schemaObj.Name] = schemaObj
}
return properties, nil
}
func (v *VariableMetaSchema) check(ctx context.Context) error {
return v.checkAppVariableSchema(ctx, v, "")
}
func (v *VariableMetaSchema) checkAppVariableSchema(ctx context.Context, schemaObj *VariableMetaSchema, schema string) (err error) {
if len(schema) == 0 && schemaObj == nil {
return errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KV("msg", "schema is nil"))
}
if schemaObj == nil {
schemaObj, err = NewVariableMetaSchema([]byte(schema))
if err != nil {
return errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "checkAppVariableSchema failed , %v", err.Error()))
}
}
if !schemaObj.nameValidate() {
return errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "name(%s) is invalid", schemaObj.Name))
}
if schemaObj.Type == variableMetaSchemaTypeObject {
return v.checkSchemaObj(ctx, schemaObj.Schema)
} else if schemaObj.Type == variableMetaSchemaTypeArray {
_, err := v.GetArrayType(schemaObj.Schema)
if err != nil {
return errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "GetArrayType failed : %v", err.Error()))
}
}
return nil
}
func (v *VariableMetaSchema) checkSchemaObj(ctx context.Context, schema []byte) error {
properties, err := v.GetObjectProperties(schema)
if err != nil {
return errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "GetObjectProperties failed : %v", err.Error()))
}
for _, schemaObj := range properties {
if err := v.checkAppVariableSchema(ctx, schemaObj, ""); err != nil {
return err
}
}
return nil
}
func (v *VariableMetaSchema) nameValidate() bool {
identifier := v.Name
reservedWords := map[string]bool{
"true": true, "false": true, "and": true, "AND": true,
"or": true, "OR": true, "not": true, "NOT": true,
"null": true, "nil": true, "If": true, "Switch": true,
}
if reservedWords[identifier] {
return false
}
// Check if some of the following regular rules are met
pattern := `^[a-zA-Z_][a-zA-Z_$0-9]*$`
match, _ := regexp.MatchString(pattern, identifier)
return match
}